diff --git a/.github/ISSUE_TEMPLATE/tracking_issue.md b/.github/ISSUE_TEMPLATE/tracking_issue.md index 5f17f30b3b0b1..598628936112e 100644 --- a/.github/ISSUE_TEMPLATE/tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/tracking_issue.md @@ -28,6 +28,8 @@ Tracking issues are used to record the overall progress of implementation. They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions. A tracking issue is however *not* meant for large scale discussion, questions, or bug reports about a feature. Instead, open a dedicated issue for the specific matter and add the relevant feature gate label. +Discussion comments will get marked as off-topic or deleted. +Repeated discussions on the tracking issue may lead to the tracking issue getting locked. ### Steps $DIR/nested-loop-moved-value-wrong-continue.rs:41:17 + // | + // 28 | for foo in foos { + // | --------------- + // ... + // 33 | for bar in &bars { + // | ---------------- + // ... + // 41 | continue; + // | ^^^^^^^^ this `continue` advances the loop at line 33 + err.span_note(spans, "verify that your loop breaking logic is correct"); + } + if let Some(parent) = parent + && let hir::ExprKind::MethodCall(..) | hir::ExprKind::Call(..) = parent.kind + { + // FIXME: We could check that the call's *parent* takes `&mut val` to make the + // suggestion more targeted to the `mk_iter(val).next()` case. Maybe do that only to + // check for whether to suggest `let value` or `let mut value`. + + let span = in_loop.span; + if !finder.found_breaks.is_empty() + && let Ok(value) = sm.span_to_snippet(parent.span) + { + // We know with high certainty that this move would affect the early return of a + // loop, so we suggest moving the expression with the move out of the loop. + let indent = if let Some(indent) = sm.indentation_before(span) { + format!("\n{indent}") + } else { + " ".to_string() + }; + err.multipart_suggestion( + "consider moving the expression out of the loop so it is only moved once", + vec![ + (parent.span, "value".to_string()), + (span.shrink_to_lo(), format!("let mut value = {value};{indent}")), + ], + Applicability::MaybeIncorrect, + ); + } + } + } + can_suggest_clone + } + + /// 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>>, + use_spans: Option>, + ) { + 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 + // `::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))) + }) + { + let ty_span = self.infcx.tcx.def_span(def.did()); + let mut span: MultiSpan = ty_span.into(); + span.push_span_label(ty_span, "consider implementing `Clone` for this type"); + span.push_span_label(expr.span, "you could clone this value"); + err.span_note( + span, + format!("if `{ty}` implemented `Clone`, you could clone the value"), + ); + } else if let ty::Param(param) = ty.kind() + && let Some(_clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() + && let generics = self.infcx.tcx.generics_of(self.mir_def_id()) + && let generic_param = generics.type_param(*param, self.infcx.tcx) + && let param_span = self.infcx.tcx.def_span(generic_param.def_id) + && if let Some(UseSpans::FnSelfUse { kind, .. }) = use_spans + && let CallKind::FnCall { fn_trait_id, self_ty } = kind + && let ty::Param(_) = self_ty.kind() + && ty == self_ty + && [ + self.infcx.tcx.lang_items().fn_once_trait(), + self.infcx.tcx.lang_items().fn_mut_trait(), + self.infcx.tcx.lang_items().fn_trait(), + ] + .contains(&Some(fn_trait_id)) + { + // Do not suggest `F: FnOnce() + Clone`. + false + } else { + true + } + { + let mut span: MultiSpan = param_span.into(); + span.push_span_label( + param_span, + "consider constraining this type parameter with `Clone`", + ); + span.push_span_label(expr.span, "you could clone this value"); + err.span_help( + span, + format!("if `{ty}` implemented `Clone`, you could clone the value"), + ); + } + } + + pub(crate) 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 { + 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) { @@ -756,27 +1300,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) { @@ -802,7 +1358,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .into_iter() .map(|err| match err.obligation.predicate.kind().skip_binder() { PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { - match predicate.self_ty().kind() { + match *predicate.self_ty().kind() { ty::Param(param_ty) => Ok(( generics.type_param(param_ty, tcx), predicate.trait_ref.print_only_trait_path().to_string(), @@ -881,11 +1437,17 @@ 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), Some(move_spans)); + } self.buffer_error(err); } pub(crate) fn report_use_while_mutably_borrowed( - &mut self, + &self, location: Location, (place, _span): (Place<'tcx>, Span), borrow: &BorrowData<'tcx>, @@ -934,7 +1496,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } pub(crate) fn report_conflicting_borrow( - &mut self, + &self, location: Location, (place, span): (Place<'tcx>, Span), gen_borrow_kind: BorrowKind, @@ -962,11 +1524,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let first_borrow_desc; let mut err = match (gen_borrow_kind, issued_borrow.kind) { ( - BorrowKind::Shared, + BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep), BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, ) => { first_borrow_desc = "mutable "; - self.cannot_reborrow_already_borrowed( + let mut err = self.cannot_reborrow_already_borrowed( span, &desc_place, &msg_place, @@ -976,11 +1538,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "mutable", &msg_borrow, None, - ) + ); + self.suggest_slice_method_if_applicable( + &mut err, + place, + issued_borrow.borrowed_place, + span, + issued_span, + ); + err } ( BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow }, - BorrowKind::Shared, + BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep), ) => { first_borrow_desc = "immutable "; let mut err = self.cannot_reborrow_already_borrowed( @@ -994,6 +1564,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &msg_borrow, None, ); + self.suggest_slice_method_if_applicable( + &mut err, + place, + issued_borrow.borrowed_place, + span, + issued_span, + ); self.suggest_binding_for_closure_capture_self(&mut err, &issued_spans); self.suggest_using_closure_argument_instead_of_capture( &mut err, @@ -1020,6 +1597,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &mut err, place, issued_borrow.borrowed_place, + span, + issued_span, ); self.suggest_using_closure_argument_instead_of_capture( &mut err, @@ -1042,7 +1621,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None) } - (BorrowKind::Mut { .. }, BorrowKind::Fake) => { + (BorrowKind::Mut { .. }, BorrowKind::Fake(FakeBorrowKind::Shallow)) => { if let Some(immutable_section_description) = self.classify_immutable_section(issued_borrow.assigned_place) { @@ -1105,7 +1684,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) } - (BorrowKind::Shared, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => { + ( + BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep), + BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, + ) => { first_borrow_desc = "first "; self.cannot_reborrow_already_uniquely_borrowed( span, @@ -1135,8 +1717,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) } - (BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Fake) - | (BorrowKind::Fake, BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake) => { + ( + BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep), + BorrowKind::Shared | BorrowKind::Fake(_), + ) + | ( + BorrowKind::Fake(FakeBorrowKind::Shallow), + BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_), + ) => { unreachable!() } }; @@ -1228,28 +1816,31 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let tcx = self.infcx.tcx; let hir = tcx.hir(); let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; - struct FindUselessClone<'hir> { - pub clones: Vec<&'hir hir::Expr<'hir>>, + + struct FindUselessClone<'tcx> { + tcx: TyCtxt<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, + pub clones: Vec<&'tcx hir::Expr<'tcx>>, } - impl<'hir> FindUselessClone<'hir> { - pub fn new() -> Self { - Self { clones: vec![] } + impl<'tcx> FindUselessClone<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + Self { tcx, typeck_results: tcx.typeck(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 + impl<'tcx> Visitor<'tcx> for FindUselessClone<'tcx> { + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::MethodCall(..) = ex.kind + && let Some(method_def_id) = + self.typeck_results.type_dependent_def_id(ex.hir_id) + && self.tcx.lang_items().clone_trait() == Some(self.tcx.parent(method_def_id)) { 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()); let body = hir.body(body_id).value; expr_finder.visit_expr(body); @@ -1298,22 +1889,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), + ); } } @@ -1414,45 +2016,60 @@ 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, tcx); + expr_finder.visit_expr(tcx.hir().body(body_id).value); + expr_finder.result + } + fn suggest_slice_method_if_applicable( &self, err: &mut Diag<'_>, place: Place<'tcx>, borrowed_place: Place<'tcx>, + span: Span, + issued_span: Span, ) { let tcx = self.infcx.tcx; let hir = tcx.hir(); + let has_split_at_mut = |ty: Ty<'tcx>| { + let ty = ty.peel_refs(); + match ty.kind() { + ty::Array(..) | ty::Slice(..) => true, + ty::Adt(def, _) if tcx.get_diagnostic_item(sym::Vec) == Some(def.did()) => true, + _ if ty == tcx.types.str_ => true, + _ => false, + } + }; if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)]) | ( [ProjectionElem::Deref, ProjectionElem::Index(index1)], [ProjectionElem::Deref, ProjectionElem::Index(index2)], ) = (&place.projection[..], &borrowed_place.projection[..]) { + let decl1 = &self.body.local_decls[*index1]; + let decl2 = &self.body.local_decls[*index2]; + let mut note_default_suggestion = || { err.help( - "consider using `.split_at_mut(position)` or similar method to obtain \ - two mutable non-overlapping sub-slices", + "consider using `.split_at_mut(position)` or similar method to obtain two \ + mutable non-overlapping sub-slices", ) - .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices"); - }; - - let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { - note_default_suggestion(); - return; + .help( + "consider using `.swap(index_1, index_2)` to swap elements at the specified \ + indices", + ); }; - let mut expr_finder = - FindExprBySpan::new(self.body.local_decls[*index1].source_info.span); - expr_finder.visit_expr(hir.body(body_id).value); - let Some(index1) = expr_finder.result else { + let Some(index1) = self.find_expr(decl1.source_info.span) else { note_default_suggestion(); return; }; - expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span); - expr_finder.visit_expr(hir.body(body_id).value); - let Some(index2) = expr_finder.result else { + let Some(index2) = self.find_expr(decl2.source_info.span) else { note_default_suggestion(); return; }; @@ -1500,7 +2117,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None } }) else { - note_default_suggestion(); + let hir::Node::Expr(parent) = tcx.parent_hir_node(index1.hir_id) else { return }; + let hir::ExprKind::Index(_, idx1, _) = parent.kind else { return }; + let hir::Node::Expr(parent) = tcx.parent_hir_node(index2.hir_id) else { return }; + let hir::ExprKind::Index(_, idx2, _) = parent.kind else { return }; + if !idx1.equivalent_for_indexing(idx2) { + err.help("use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices"); + } return; }; @@ -1510,7 +2133,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("{obj_str}.swap({index1_str}, {index2_str})"), Applicability::MachineApplicable, ); + return; + } + let place_ty = PlaceRef::ty(&place.as_ref(), self.body, tcx).ty; + let borrowed_place_ty = PlaceRef::ty(&borrowed_place.as_ref(), self.body, tcx).ty; + if !has_split_at_mut(place_ty) && !has_split_at_mut(borrowed_place_ty) { + // Only mention `split_at_mut` on `Vec`, array and slices. + return; + } + let Some(index1) = self.find_expr(span) else { return }; + let hir::Node::Expr(parent) = tcx.parent_hir_node(index1.hir_id) else { return }; + let hir::ExprKind::Index(_, idx1, _) = parent.kind else { return }; + let Some(index2) = self.find_expr(issued_span) else { return }; + let hir::Node::Expr(parent) = tcx.parent_hir_node(index2.hir_id) else { return }; + let hir::ExprKind::Index(_, idx2, _) = parent.kind else { return }; + if idx1.equivalent_for_indexing(idx2) { + // `let a = &mut foo[0]` and `let b = &mut foo[0]`? Don't mention `split_at_mut` + return; } + err.help("use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices"); } /// Suggest using `while let` for call `next` on an iterator in a for loop. @@ -1894,7 +2535,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { hir::intravisit::walk_expr(self, e); } - fn visit_local(&mut self, local: &'hir hir::Local<'hir>) { + fn visit_local(&mut self, local: &'hir hir::LetStmt<'hir>) { if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && let Some(init) = local.init @@ -2223,7 +2864,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn report_local_value_does_not_live_long_enough( - &mut self, + &self, location: Location, name: &str, borrow: &BorrowData<'tcx>, @@ -2402,7 +3043,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn report_thread_local_value_does_not_live_long_enough( - &mut self, + &self, drop_span: Span, borrow_span: Span, ) -> Diag<'tcx> { @@ -2423,7 +3064,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] fn report_temporary_value_does_not_live_long_enough( - &mut self, + &self, location: Location, borrow: &BorrowData<'tcx>, drop_span: Span, @@ -2681,7 +3322,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] fn report_escaping_closure_capture( - &mut self, + &self, use_span: UseSpans<'tcx>, var_span: Span, fr_name: &RegionName, @@ -2791,7 +3432,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn report_escaping_data( - &mut self, + &self, borrow_span: Span, name: &Option, upvar_span: Span, @@ -2825,7 +3466,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } fn get_moved_indexes( - &mut self, + &self, location: Location, mpi: MovePathIndex, ) -> (Vec, Vec) { @@ -3026,7 +3667,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let loan_span = loan_spans.args_or_use(); let descr_place = self.describe_any_place(place.as_ref()); - if loan.kind == BorrowKind::Fake { + if let BorrowKind::Fake(_) = loan.kind { if let Some(section) = self.classify_immutable_section(loan.assigned_place) { let mut err = self.cannot_mutate_in_immutable_section( span, @@ -3151,7 +3792,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | None => (self.describe_any_place(place.as_ref()), assigned_span), Some(decl) => (self.describe_any_place(err_place.as_ref()), decl.source_info.span), }; - let mut err = self.cannot_reassign_immutable(span, &place_description, from_arg); let msg = if from_arg { "cannot assign to immutable argument" @@ -3171,6 +3811,22 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("mut {name}"), Applicability::MachineApplicable, ); + if !from_arg + && matches!( + decl.local_info(), + LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((Some(_), _)), + .. + })) + ) + { + err.span_suggestion( + decl.source_info.span, + "to modify the original value, take a borrow instead", + format!("ref mut {name}"), + Applicability::MaybeIncorrect, + ); + } } err.span_label(span, msg); self.buffer_error(err); @@ -3614,7 +4270,7 @@ enum AnnotatedBorrowFnSignature<'tcx> { impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { /// Annotate the provided diagnostic with information about borrow from the fn signature that /// helps explain. - pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diag<'_>) -> String { + pub(crate) fn emit(&self, cx: &MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diag<'_>) -> String { match self { &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => { diag.span_label( @@ -3688,6 +4344,28 @@ impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> { } } +/// Look for `break` expressions within any arbitrary expressions. We'll do this to infer +/// whether this is a case where the moved value would affect the exit of a loop, making it +/// unsuitable for a `.clone()` suggestion. +struct BreakFinder { + found_breaks: Vec<(hir::Destination, Span)>, + found_continues: Vec<(hir::Destination, Span)>, +} +impl<'hir> Visitor<'hir> for BreakFinder { + fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { + match ex.kind { + hir::ExprKind::Break(destination, _) => { + self.found_breaks.push((destination, ex.span)); + } + hir::ExprKind::Continue(destination) => { + self.found_continues.push((destination, ex.span)); + } + _ => {} + } + hir::intravisit::walk_expr(self, ex); + } +} + /// Given a set of spans representing statements initializing the relevant binding, visit all the /// function expressions looking for branching code paths that *do not* initialize the binding. struct ConditionVisitor<'b> { diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 418eabe3ae242..5ebdb69050b24 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -76,7 +76,7 @@ impl<'tcx> BorrowExplanation<'tcx> { && let Some(body_id) = node.body_id() { let body = tcx.hir().body(body_id); - let mut expr_finder = FindExprBySpan::new(span); + let mut expr_finder = FindExprBySpan::new(span, tcx); expr_finder.visit_expr(body.value); if let Some(mut expr) = expr_finder.result { while let hir::ExprKind::AddrOf(_, _, inner) diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 914f68a38b4f5..0b4018a23ce7b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -4,14 +4,15 @@ use crate::session_diagnostics::{ CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause, CaptureVarKind, CaptureVarPathUseCause, OnClosureNote, }; -use itertools::Itertools; 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; use rustc_index::IndexSlice; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{FulfillmentErrorCode, SelectionError}; +use rustc_middle::bug; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location, @@ -30,6 +31,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; @@ -226,16 +229,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } else { if autoderef_index.is_none() { - autoderef_index = - match place.projection.into_iter().rev().find_position(|elem| { - !matches!( - elem, - ProjectionElem::Deref | ProjectionElem::Downcast(..) - ) - }) { - Some((index, _)) => Some(place.projection.len() - index), - None => Some(0), - }; + autoderef_index = match place.projection.iter().rposition(|elem| { + !matches!( + elem, + ProjectionElem::Deref | ProjectionElem::Downcast(..) + ) + }) { + Some(index) => Some(index + 1), + None => Some(0), + }; } if index >= autoderef_index.unwrap() { buf.insert(0, '*'); @@ -366,7 +368,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(variant.fields[field].name.to_string()) } ty::Tuple(_) => Some(field.index().to_string()), - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => { self.describe_field_from_ty(ty, field, variant_index, including_tuple_field) } ty::Array(ty, _) | ty::Slice(ty) => { @@ -589,7 +591,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, ) { @@ -603,7 +605,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, ) { @@ -641,7 +643,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, f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause, @@ -653,7 +655,7 @@ impl UseSpans<'_> { match kind { Some(kd) => match kd { rustc_middle::mir::BorrowKind::Shared - | rustc_middle::mir::BorrowKind::Fake => { + | rustc_middle::mir::BorrowKind::Fake(_) => { CaptureVarKind::Immut { kind_span: capture_kind_span } } @@ -1036,7 +1038,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( @@ -1048,7 +1050,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(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(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::>() + && !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(); @@ -1214,13 +1288,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 `` // 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 0d1b875cbed5b..1d844c3d6a295 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -2,10 +2,14 @@ #![allow(rustc::untranslatable_diagnostic)] use rustc_errors::{Applicability, Diag}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{CaptureBy, ExprKind, HirId, Node}; +use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; +use rustc_trait_selection::traits::error_reporting::FindExprBySpan; use crate::diagnostics::CapturedMessageOpt; use crate::diagnostics::{DescribePlaceOpt, UseSpans}; @@ -303,6 +307,121 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.cannot_move_out_of(span, &description) } + fn suggest_clone_of_captured_var_in_move_closure( + &self, + err: &mut Diag<'_>, + upvar_hir_id: HirId, + upvar_name: &str, + use_spans: Option>, + ) { + let tcx = self.infcx.tcx; + let typeck_results = tcx.typeck(self.mir_def_id()); + let Some(use_spans) = use_spans else { return }; + // We only care about the case where a closure captured a binding. + let UseSpans::ClosureUse { args_span, .. } = use_spans else { return }; + let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; + // Fetch the type of the expression corresponding to the closure-captured binding. + let Some(captured_ty) = typeck_results.node_type_opt(upvar_hir_id) else { return }; + if !self.implements_clone(captured_ty) { + // We only suggest cloning the captured binding if the type can actually be cloned. + return; + }; + // Find the closure that captured the binding. + let mut expr_finder = FindExprBySpan::new(args_span, tcx); + expr_finder.include_closures = true; + expr_finder.visit_expr(tcx.hir().body(body_id).value); + let Some(closure_expr) = expr_finder.result else { return }; + let ExprKind::Closure(closure) = closure_expr.kind else { return }; + // We'll only suggest cloning the binding if it's a `move` closure. + let CaptureBy::Value { .. } = closure.capture_clause else { return }; + // Find the expression within the closure where the binding is consumed. + let mut suggested = false; + let use_span = use_spans.var_or_use(); + let mut expr_finder = FindExprBySpan::new(use_span, tcx); + expr_finder.include_closures = true; + expr_finder.visit_expr(tcx.hir().body(body_id).value); + let Some(use_expr) = expr_finder.result else { return }; + let parent = tcx.parent_hir_node(use_expr.hir_id); + if let Node::Expr(expr) = parent + && let ExprKind::Assign(lhs, ..) = expr.kind + && lhs.hir_id == use_expr.hir_id + { + // Cloning the value being assigned makes no sense: + // + // error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closure + // --> $DIR/option-content-move2.rs:11:9 + // | + // LL | let mut var = None; + // | ------- captured outer variable + // LL | func(|| { + // | -- captured by this `FnMut` closure + // LL | // Shouldn't suggest `move ||.as_ref()` here + // LL | move || { + // | ^^^^^^^ `var` is moved here + // LL | + // LL | var = Some(NotCopyable); + // | --- + // | | + // | variable moved due to use in closure + // | move occurs because `var` has type `Option`, which does not implement the `Copy` trait + // | + return; + } + + // Search for an appropriate place for the structured `.clone()` suggestion to be applied. + // If we encounter a statement before the borrow error, we insert a statement there. + for (_, node) in tcx.hir().parent_iter(closure_expr.hir_id) { + if let Node::Stmt(stmt) = node { + let padding = tcx + .sess + .source_map() + .indentation_before(stmt.span) + .unwrap_or_else(|| " ".to_string()); + err.multipart_suggestion_verbose( + "clone the value before moving it into the closure", + vec![ + ( + stmt.span.shrink_to_lo(), + format!("let value = {upvar_name}.clone();\n{padding}"), + ), + (use_span, "value".to_string()), + ], + Applicability::MachineApplicable, + ); + suggested = true; + break; + } else if let Node::Expr(expr) = node + && let ExprKind::Closure(_) = expr.kind + { + // We want to suggest cloning only on the first closure, not + // subsequent ones (like `ui/suggestions/option-content-move2.rs`). + break; + } + } + if !suggested { + // If we couldn't find a statement for us to insert a new `.clone()` statement before, + // we have a bare expression, so we suggest the creation of a new block inline to go + // from `move || val` to `{ let value = val.clone(); move || value }`. + let padding = tcx + .sess + .source_map() + .indentation_before(closure_expr.span) + .unwrap_or_else(|| " ".to_string()); + err.multipart_suggestion_verbose( + "clone the value before moving it into the closure", + vec![ + ( + closure_expr.span.shrink_to_lo(), + format!("{{\n{padding}let value = {upvar_name}.clone();\n{padding}"), + ), + (use_spans.var_or_use(), "value".to_string()), + (closure_expr.span.shrink_to_hi(), format!("\n{padding}}}")), + ], + Applicability::MachineApplicable, + ); + } + } + fn report_cannot_move_from_borrowed_content( &mut self, move_place: Place<'tcx>, @@ -310,10 +429,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { span: Span, use_spans: Option>, ) -> Diag<'tcx> { + let tcx = self.infcx.tcx; // Inspect the type of the content behind the // borrow to provide feedback about why this // was a move rather than a copy. - let ty = deref_target_place.ty(self.body, self.infcx.tcx).ty; + let ty = deref_target_place.ty(self.body, tcx).ty; let upvar_field = self .prefixes(move_place.as_ref(), PrefixSet::All) .find_map(|p| self.is_upvar_field_projection(p)); @@ -363,8 +483,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let upvar = &self.upvars[upvar_field.unwrap().index()]; let upvar_hir_id = upvar.get_root_variable(); - let upvar_name = upvar.to_string(self.infcx.tcx); - let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id); + let upvar_name = upvar.to_string(tcx); + let upvar_span = tcx.hir().span(upvar_hir_id); let place_name = self.describe_any_place(move_place.as_ref()); @@ -380,12 +500,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { closure_kind_ty, closure_kind, place_description, ); - self.cannot_move_out_of(span, &place_description) + let closure_span = tcx.def_span(def_id); + let mut err = self + .cannot_move_out_of(span, &place_description) .with_span_label(upvar_span, "captured outer variable") .with_span_label( - self.infcx.tcx.def_span(def_id), + closure_span, format!("captured by this `{closure_kind}` closure"), - ) + ); + self.suggest_clone_of_captured_var_in_move_closure( + &mut err, + upvar_hir_id, + &upvar_name, + use_spans, + ); + err } _ => { let source = self.borrowed_content_source(deref_base); @@ -415,7 +544,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ), (_, _, _) => self.cannot_move_out_of( span, - &source.describe_for_unnamed_place(self.infcx.tcx), + &source.describe_for_unnamed_place(tcx), ), } } @@ -435,7 +564,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 +575,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), None); + } + err.subdiagnostic( self.dcx(), crate::session_diagnostics::TypeNoCopy::Label { @@ -468,19 +603,30 @@ 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), + Some(use_spans), + ); + } + 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 +728,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, 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 a27d016e0ba51..5d74a01aa2076 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -2,12 +2,12 @@ #![allow(rustc::untranslatable_diagnostic)] use core::ops::ControlFlow; -use hir::ExprKind; +use hir::{ExprKind, Param}; use rustc_errors::{Applicability, Diag}; -use rustc_hir as hir; use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; +use rustc_hir::{self as hir, BindingMode, ByRef, Node}; use rustc_infer::traits; +use rustc_middle::bug; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, InstanceDef, ToPredicate, Ty, TyCtxt}; use rustc_middle::{ @@ -22,7 +22,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 { @@ -235,7 +235,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, } @@ -304,7 +304,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { { match *decl.local_info() { LocalInfo::User(BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(Mutability::Not), + binding_mode: BindingMode(ByRef::No, Mutability::Not), opt_ty_info: Some(sp), opt_match_place: _, pat_span: _, @@ -342,7 +342,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } else if decl.mutability.is_not() { if matches!( decl.local_info(), - LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::MutRef)) + LocalInfo::User(BindingForm::ImplicitSelf(hir::ImplicitSelfKind::RefMut)) ) { err.note( "as `Self` may be unsized, this call attempts to take `&mut &mut self`", @@ -399,7 +399,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let upvar_hir_id = captured_place.get_root_variable(); if let Node::Pat(pat) = self.infcx.tcx.hir_node(upvar_hir_id) - && let hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, upvar_ident, _) = + && let hir::PatKind::Binding(hir::BindingMode::NONE, _, upvar_ident, _) = pat.kind { if upvar_ident.name == kw::SelfLower { @@ -407,7 +407,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(fn_decl) = node.fn_decl() { if !matches!( fn_decl.implicit_self, - hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef + hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut ) { err.span_suggestion( upvar_ident.span, @@ -541,24 +541,28 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } + /// Suggest `map[k] = v` => `map.insert(k, v)` and the like. fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'tcx>, span: Span) { let Some(adt) = ty.ty_adt_def() else { return }; let did = adt.did(); if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did) || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did) { - struct V<'a, 'tcx> { + /// Walks through the HIR, looking for the corresponding span for this error. + /// When it finds it, see if it corresponds to assignment operator whose LHS + /// is an index expr. + struct SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> { assign_span: Span, err: &'a mut Diag<'tcx>, ty: Ty<'tcx>, suggested: bool, } - impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> { + impl<'a, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { hir::intravisit::walk_stmt(self, stmt); let expr = match stmt.kind { hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, - hir::StmtKind::Let(hir::Local { init: Some(expr), .. }) => expr, + hir::StmtKind::Let(hir::LetStmt { init: Some(expr), .. }) => expr, _ => { return; } @@ -646,7 +650,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let Some(body_id) = hir_map.maybe_body_owned_by(local_def_id) else { return }; let body = self.infcx.tcx.hir().body(body_id); - let mut v = V { assign_span: span, err, ty, suggested: false }; + let mut v = SuggestIndexOperatorAlternativeVisitor { + assign_span: span, + err, + ty, + suggested: false, + }; v.visit_body(body); if !v.suggested { err.help(format!( @@ -659,19 +668,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) { + /// + /// 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) { 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; @@ -687,19 +703,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 } }) } @@ -717,7 +730,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { debug!("local_decl: {:?}", local_decl); let pat_span = match *local_decl.local_info() { LocalInfo::User(BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(Mutability::Not), + binding_mode: BindingMode(ByRef::No, Mutability::Not), opt_ty_info: _, opt_match_place: _, pat_span, @@ -725,25 +738,26 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { _ => local_decl.source_info.span, }; - 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) - { - let body = self.infcx.tcx.hir().body(body_id); - BindingFinder { span: pat_span }.visit_body(body).break_value() - } else { - None - }; - // With ref-binding patterns, the mutability suggestion has to apply to // the binding, not the reference (which would be a type error): // // `let &b = a;` -> `let &(mut b) = a;` - if let Some(hir_id) = hir_id - && let hir::Node::Local(hir::Local { + // or + // `fn foo(&x: &i32)` -> `fn foo(&(mut x): &i32)` + let def_id = self.body.source.def_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) + && let body = self.infcx.tcx.hir().body(body_id) + && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(body).break_value() + && let node = self.infcx.tcx.hir_node(hir_id) + && let hir::Node::LetStmt(hir::LetStmt { + pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, + .. + }) + | hir::Node::Param(Param { pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, .. - }) = self.infcx.tcx.hir_node(hir_id) + }) = node && let Ok(name) = self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span) { @@ -1052,24 +1066,28 @@ 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 { - binding_mode: ty::BindingMode::BindByValue(_), + binding_mode: BindingMode(ByRef::No, _), opt_ty_info, .. })) => { @@ -1104,7 +1122,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, @@ -1131,33 +1149,44 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ), } }; - Some(label) + Some((has_sugg, decl_span, sugg, None)) } } } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByReference(_), + binding_mode: BindingMode(ByRef::Yes(_), _), .. })) => { 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) @@ -1169,7 +1198,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(hir_id) = hir_id - && let hir::Node::Local(local) = self.infcx.tcx.hir_node(hir_id) + && let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id) { let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() @@ -1310,6 +1339,16 @@ impl<'tcx> Visitor<'tcx> for BindingFinder { hir::intravisit::walk_stmt(self, s) } } + + fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result { + if let hir::Pat { kind: hir::PatKind::Ref(_, _), span, .. } = param.pat + && *span == self.span + { + ControlFlow::Break(param.hir_id) + } else { + ControlFlow::Continue(()) + } + } } pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option) -> bool { @@ -1318,7 +1357,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option< match *local_decl.local_info() { // Check if mutably borrowing a mutable reference. LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(Mutability::Not), + binding_mode: BindingMode(ByRef::No, Mutability::Not), .. })) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)), LocalInfo::User(mir::BindingForm::ImplicitSelf(kind)) => { @@ -1327,7 +1366,7 @@ pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option< // // Deliberately fall into this case for all implicit self types, // so that we don't fall into the next case with them. - kind == hir::ImplicitSelfKind::MutRef + kind == hir::ImplicitSelfKind::RefMut } _ if Some(kw::SelfLower) == local_name => { // Otherwise, check if the name is the `self` keyword - in which case diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index c06bf94a6fd5f..d8d582b0ad1c3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -18,6 +18,7 @@ use rustc_infer::infer::{ error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin, RelateParamBound, }; +use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::GenericArgs; @@ -26,6 +27,8 @@ 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::InferCtxtExt; +use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use crate::borrowck_errors; use crate::session_diagnostics::{ @@ -503,10 +506,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ty::VarianceDiagInfo::None => {} ty::VarianceDiagInfo::Invariant { ty, param_index } => { let (desc, note) = match ty.kind() { - ty::RawPtr(ty_mut) => { - assert_eq!(ty_mut.mutbl, rustc_hir::Mutability::Mut); + ty::RawPtr(ty, mutbl) => { + assert_eq!(*mutbl, rustc_hir::Mutability::Mut); ( - format!("a mutable pointer to `{}`", ty_mut.ty), + format!("a mutable pointer to `{}`", ty), "mutable pointers are invariant over their type parameter".to_string(), ) } @@ -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_return(&mut diag); diag } @@ -1039,6 +1043,133 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag); } + #[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_return(&self, diag: &mut Diag<'_>) { + let tcx = self.infcx.tcx; + + // Get the closure return value and type. + let closure_def_id = self.mir_def_id(); + let hir::Node::Expr( + closure_expr @ hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { body, .. }), .. + }, + ) = tcx.hir_node_by_def_id(closure_def_id) + else { + return; + }; + let ty::Closure(_, args) = *tcx.type_of(closure_def_id).instantiate_identity().kind() + else { + return; + }; + let args = args.as_closure(); + + // Make sure that the parent expression is a method call. + let parent_expr_id = tcx.parent_hir_id(self.mir_hir_id()); + let hir::Node::Expr( + parent_expr @ hir::Expr { + kind: hir::ExprKind::MethodCall(_, rcvr, call_args, _), .. + }, + ) = tcx.hir_node(parent_expr_id) + else { + return; + }; + let typeck_results = tcx.typeck(self.mir_def_id()); + + // We don't use `ty.peel_refs()` to get the number of `*`s needed to get the root type. + let liberated_sig = tcx.liberate_late_bound_regions(closure_def_id.to_def_id(), args.sig()); + let mut peeled_ty = liberated_sig.output(); + let mut count = 0; + while let ty::Ref(_, ref_ty, _) = *peeled_ty.kind() { + peeled_ty = ref_ty; + count += 1; + } + if !self.infcx.type_is_copy_modulo_regions(self.param_env, peeled_ty) { + return; + } + + // Build a new closure where the return type is an owned value, instead of a ref. + let closure_sig_as_fn_ptr_ty = Ty::new_fn_ptr( + tcx, + ty::Binder::dummy(tcx.mk_fn_sig( + liberated_sig.inputs().iter().copied(), + peeled_ty, + liberated_sig.c_variadic, + hir::Unsafety::Normal, + rustc_target::spec::abi::Abi::Rust, + )), + ); + let closure_ty = Ty::new_closure( + tcx, + closure_def_id.to_def_id(), + ty::ClosureArgs::new( + tcx, + ty::ClosureArgsParts { + parent_args: args.parent_args(), + closure_kind_ty: args.kind_ty(), + tupled_upvars_ty: args.tupled_upvars_ty(), + closure_sig_as_fn_ptr_ty, + }, + ) + .args, + ); + + let Some((closure_arg_pos, _)) = + call_args.iter().enumerate().find(|(_, arg)| arg.hir_id == closure_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(method_def_id) = typeck_results.type_dependent_def_id(parent_expr.hir_id) else { + return; + }; + let Some(input_arg) = tcx + .fn_sig(method_def_id) + .skip_binder() + .inputs() + .skip_binder() + // Methods have a `self` arg, so `pos` is actually `+ 1` to match the method call arg. + .get(closure_arg_pos + 1) + else { + return; + }; + // If this isn't a param, then we can't substitute a new closure. + let ty::Param(closure_param) = input_arg.kind() else { return }; + + // Get the arguments for the found method, only specifying that `Self` is the receiver type. + let Some(possible_rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) else { return }; + let args = GenericArgs::for_item(tcx, method_def_id, |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(parent_expr.span, param) + } + }); + + let preds = tcx.predicates_of(method_def_id).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( + tcx.hir().body(*body).value.peel_blocks().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<'_>) { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 08199068020d8..28c5c505f7916 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -11,6 +11,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; +use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -555,8 +556,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { search_stack.push((*elem_ty, elem_hir_ty)); } - (ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => { - search_stack.push((mut_ty.ty, &mut_hir_ty.ty)); + (ty::RawPtr(mut_ty, _), hir::TyKind::Ptr(mut_hir_ty)) => { + search_stack.push((*mut_ty, &mut_hir_ty.ty)); } _ => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 415ba095a1b24..47c83e0bb2bb6 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -4,7 +4,6 @@ #![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(assert_matches)] -#![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(let_chains)] @@ -14,8 +13,6 @@ #![feature(stmt_expr_attributes)] #![feature(try_blocks)] -#[macro_use] -extern crate rustc_middle; #[macro_use] extern crate tracing; @@ -32,8 +29,8 @@ use rustc_infer::infer::{ use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::*; use rustc_middle::query::Providers; -use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::UNUSED_MUT; use rustc_span::{Span, Symbol}; use rustc_target::abi::FieldIdx; @@ -126,7 +123,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> { return tcx.arena.alloc(result); } - let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::bind(tcx, def)).build(); + let infcx = tcx.infer_ctxt().with_opaque_type_inference(def).build(); let promoted: &IndexSlice<_, _> = &promoted.borrow(); let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, None).0; debug!("mir_borrowck done"); @@ -1057,18 +1054,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Control::Continue } - (Read(_), BorrowKind::Shared | BorrowKind::Fake) - | (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => { - Control::Continue - } + (Read(_), BorrowKind::Shared | BorrowKind::Fake(_)) + | ( + Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))), + BorrowKind::Mut { .. }, + ) => Control::Continue, - (Reservation(_), BorrowKind::Fake | BorrowKind::Shared) => { + (Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => { // This used to be a future compatibility warning (to be // disallowed on NLL). See rust-lang/rust#56254 Control::Continue } - (Write(WriteKind::Move), BorrowKind::Fake) => { + (Write(WriteKind::Move), BorrowKind::Fake(FakeBorrowKind::Shallow)) => { // Handled by initialization checks. Control::Continue } @@ -1176,10 +1174,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { match rvalue { &Rvalue::Ref(_ /*rgn*/, bk, place) => { let access_kind = match bk { - BorrowKind::Fake => { + BorrowKind::Fake(FakeBorrowKind::Shallow) => { (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) } - BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), + BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => { + (Deep, Read(ReadKind::Borrow(bk))) + } BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); if allow_two_phase_borrow(bk) { @@ -1198,7 +1198,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { flow_state, ); - let action = if bk == BorrowKind::Fake { + let action = if bk == BorrowKind::Fake(FakeBorrowKind::Shallow) { InitializationRequiringAction::MatchOn } else { InitializationRequiringAction::Borrow @@ -1316,7 +1316,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } AggregateKind::Adt(..) | AggregateKind::Array(..) - | AggregateKind::Tuple { .. } => (), + | AggregateKind::Tuple { .. } + | AggregateKind::RawPtr(..) => (), } for operand in operands { @@ -1557,7 +1558,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // only mutable borrows should be 2-phase assert!(match borrow.kind { - BorrowKind::Shared | BorrowKind::Fake => false, + BorrowKind::Shared | BorrowKind::Fake(_) => false, BorrowKind::Mut { .. } => true, }); @@ -1606,6 +1607,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -1648,8 +1650,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -2120,14 +2123,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | WriteKind::Replace | WriteKind::StorageDeadOrDrop | WriteKind::MutableBorrow(BorrowKind::Shared) - | WriteKind::MutableBorrow(BorrowKind::Fake), + | WriteKind::MutableBorrow(BorrowKind::Fake(_)), ) | Write( WriteKind::Move | WriteKind::Replace | WriteKind::StorageDeadOrDrop | WriteKind::MutableBorrow(BorrowKind::Shared) - | WriteKind::MutableBorrow(BorrowKind::Fake), + | WriteKind::MutableBorrow(BorrowKind::Fake(_)), ) => { if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err() && !self.has_buffered_diags() @@ -2151,7 +2154,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return false; } Read( - ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake) + ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_)) | ReadKind::Copy, ) => { // Access authorized @@ -2284,8 +2287,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } } - ty::RawPtr(tnm) => { - match tnm.mutbl { + ty::RawPtr(_, mutbl) => { + match mutbl { // `*const` raw pointers are not mutable hir::Mutability::Not => Err(place), // `*mut` raw pointers are always mutable, regardless of diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 7e8dba43b715e..ad3c3e6d07932 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -54,8 +54,9 @@ use crate::ArtificialField; use crate::Overlap; use crate::{AccessDepth, Deep, Shallow}; use rustc_hir as hir; +use rustc_middle::bug; use rustc_middle::mir::{ - Body, BorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem, + Body, BorrowKind, FakeBorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem, }; use rustc_middle::ty::{self, TyCtxt}; use std::cmp::max; @@ -271,10 +272,10 @@ fn place_components_conflict<'tcx>( // If the second example, where we did, then we still know // that the borrow can access a *part* of our place that // our access cares about, so we still have a conflict. - if borrow_kind == BorrowKind::Fake + if borrow_kind == BorrowKind::Fake(FakeBorrowKind::Shallow) && borrow_place.projection.len() < access_place.projection.len() { - debug!("borrow_conflicts_with_place: fake borrow"); + debug!("borrow_conflicts_with_place: shallow borrow"); false } else { debug!("borrow_conflicts_with_place: full borrow, CONFLICT"); diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index 956de1dec9b2b..a1e59977ede5a 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -1,6 +1,9 @@ use rustc_data_structures::graph::dominators::Dominators; +use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, BasicBlock, Body, Location, NonDivergingIntrinsic, Place, Rvalue}; +use rustc_middle::mir::{ + self, BasicBlock, Body, FakeBorrowKind, Location, NonDivergingIntrinsic, Place, Rvalue, +}; use rustc_middle::mir::{BorrowKind, Mutability, Operand}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::mir::{Statement, StatementKind}; @@ -239,10 +242,12 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> { match rvalue { &Rvalue::Ref(_ /*rgn*/, bk, place) => { let access_kind = match bk { - BorrowKind::Fake => { + BorrowKind::Fake(FakeBorrowKind::Shallow) => { (Shallow(Some(ArtificialField::FakeBorrow)), Read(ReadKind::Borrow(bk))) } - BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), + BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep) => { + (Deep, Read(ReadKind::Borrow(bk))) + } BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); if allow_two_phase_borrow(bk) { @@ -357,8 +362,11 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> { // have already taken the reservation } - (Read(_), BorrowKind::Fake | BorrowKind::Shared) - | (Read(ReadKind::Borrow(BorrowKind::Fake)), BorrowKind::Mut { .. }) => { + (Read(_), BorrowKind::Fake(_) | BorrowKind::Shared) + | ( + Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))), + BorrowKind::Mut { .. }, + ) => { // Reads don't invalidate shared or shallow borrows } @@ -403,7 +411,7 @@ impl<'cx, 'tcx> LoanInvalidationsGenerator<'cx, 'tcx> { // only mutable borrows should be 2-phase assert!(match borrow.kind { - BorrowKind::Shared | BorrowKind::Fake => false, + BorrowKind::Shared | BorrowKind::Fake(_) => false, BorrowKind::Mut { .. } => true, }); diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index c103ba3c40709..f145d30fe38a9 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -6,7 +6,38 @@ use std::borrow::Cow; use std::io::{self, Write}; use super::*; +use itertools::Itertools; use rustc_graphviz as dot; +use rustc_middle::ty::UniverseIndex; + +fn render_outlives_constraint(constraint: &OutlivesConstraint<'_>) -> String { + match constraint.locations { + Locations::All(_) => "All(...)".to_string(), + Locations::Single(loc) => format!("{loc:?}"), + } +} + +fn render_universe(u: UniverseIndex) -> String { + if u.is_root() { + return "".to_string(); + } + + format!("/{:?}", u) +} + +fn render_region_vid(rvid: RegionVid, regioncx: &RegionInferenceContext<'_>) -> String { + let universe_str = render_universe(regioncx.region_definition(rvid).universe); + + let external_name_str = if let Some(external_name) = + regioncx.region_definition(rvid).external_name.and_then(|e| e.get_name()) + { + format!(" ({external_name})") + } else { + "".to_string() + }; + + format!("{:?}{universe_str}{external_name_str}", rvid) +} impl<'tcx> RegionInferenceContext<'tcx> { /// Write out the region constraint graph. @@ -46,10 +77,10 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> { Some(dot::LabelText::LabelStr(Cow::Borrowed("box"))) } fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> { - dot::LabelText::LabelStr(format!("{n:?}").into()) + dot::LabelText::LabelStr(render_region_vid(*n, self.regioncx).into()) } fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> { - dot::LabelText::LabelStr(format!("{:?}", e.locations).into()) + dot::LabelText::LabelStr(render_outlives_constraint(e).into()) } } @@ -96,8 +127,9 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> { Some(dot::LabelText::LabelStr(Cow::Borrowed("box"))) } fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> { - let nodes = &self.nodes_per_scc[*n]; - dot::LabelText::LabelStr(format!("{n:?} = {nodes:?}").into()) + let nodes_str = + self.nodes_per_scc[*n].iter().map(|n| render_region_vid(*n, self.regioncx)).join(", "); + dot::LabelText::LabelStr(format!("SCC({n}) = {{{nodes_str}}}", n = n.as_usize()).into()) } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index c3800a1f1f21b..78465ad7975d9 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -11,6 +11,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::outlives::test_type_match; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; +use rustc_middle::bug; use rustc_middle::mir::{ BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy, ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint, @@ -95,9 +96,11 @@ pub struct RegionInferenceContext<'tcx> { /// visible from this index. scc_universes: IndexVec, - /// Contains a "representative" from each SCC. This will be the - /// minimal RegionVid belonging to that universe. It is used as a - /// kind of hacky way to manage checking outlives relationships, + /// Contains the "representative" region of each SCC. + /// It is defined as the one with the minimal RegionVid, favoring + /// free regions, then placeholders, then existential regions. + /// + /// It is a hacky way to manage checking regions for equality, /// since we can 'canonicalize' each region to the representative /// of its SCC and be sure that -- if they have the same repr -- /// they *must* be equal (though not having the same repr does not @@ -481,8 +484,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_universes } - /// For each SCC, we compute a unique `RegionVid` (in fact, the - /// minimal one that belongs to the SCC). See + /// For each SCC, we compute a unique `RegionVid`. See the /// `scc_representatives` field of `RegionInferenceContext` for /// more details. fn compute_scc_representatives( @@ -490,13 +492,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { definitions: &IndexSlice>, ) -> IndexVec { let num_sccs = constraints_scc.num_sccs(); - let next_region_vid = definitions.next_index(); - let mut scc_representatives = IndexVec::from_elem_n(next_region_vid, num_sccs); - - for region_vid in definitions.indices() { - let scc = constraints_scc.scc(region_vid); - let prev_min = scc_representatives[scc]; - scc_representatives[scc] = region_vid.min(prev_min); + let mut scc_representatives = IndexVec::from_elem_n(RegionVid::MAX, num_sccs); + + // Iterate over all RegionVids *in-order* and pick the least RegionVid as the + // representative of its SCC. This naturally prefers free regions over others. + for (vid, def) in definitions.iter_enumerated() { + let repr = &mut scc_representatives[constraints_scc.scc(vid)]; + if *repr == ty::RegionVid::MAX { + *repr = vid; + } else if matches!(def.origin, NllRegionVariableOrigin::Placeholder(_)) + && matches!(definitions[*repr].origin, NllRegionVariableOrigin::Existential { .. }) + { + // Pick placeholders over existentials even if they have a greater RegionVid. + *repr = vid; + } } scc_representatives @@ -1554,7 +1563,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Because this free region must be in the ROOT universe, we // know it cannot contain any bound universes. - assert!(self.scc_universes[longer_fr_scc] == ty::UniverseIndex::ROOT); + assert!(self.scc_universes[longer_fr_scc].is_root()); debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none()); // Only check all of the relations for the main representative of each @@ -2065,7 +2074,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { from_closure: constraint.from_closure, cause: ObligationCause::new(constraint.span, CRATE_DEF_ID, cause_code.clone()), variance_info: constraint.variance_info, - outlives_constraint: *constraint, }) .collect(); debug!("categorized_path={:#?}", categorized_path); @@ -2294,5 +2302,4 @@ pub struct BlameConstraint<'tcx> { pub from_closure: bool, pub cause: ObligationCause<'tcx>, pub variance_info: ty::VarianceDiagInfo<'tcx>, - pub outlives_constraint: OutlivesConstraint<'tcx>, } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 8a17223303718..73ba5bee13b7f 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,13 +1,12 @@ -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::OpaqueTyOrigin; -use rustc_infer::infer::InferCtxt; use rustc_infer::infer::TyCtxtInferExt as _; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_macros::extension; -use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{GenericArgKind, GenericArgs}; @@ -15,7 +14,9 @@ use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::ObligationCtxt; +use crate::session_diagnostics::LifetimeMismatchOpaqueParam; use crate::session_diagnostics::NonGenericOpaqueTypeParam; +use crate::universal_regions::RegionClassification; use super::RegionInferenceContext; @@ -26,8 +27,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// For example consider `fn f<'a>(x: &'a i32) -> impl Sized + 'a { x }`. /// This is lowered to give HIR something like /// - /// type f<'a>::_Return<'_a> = impl Sized + '_a; - /// fn f<'a>(x: &'a i32) -> f<'static>::_Return<'a> { x } + /// type f<'a>::_Return<'_x> = impl Sized + '_x; + /// fn f<'a>(x: &'a i32) -> f<'a>::_Return<'a> { x } /// /// When checking the return type record the type from the return and the /// type used in the return value. In this case they might be `_Return<'1>` @@ -35,30 +36,34 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// /// Once we to this method, we have completed region inference and want to /// call `infer_opaque_definition_from_instantiation` to get the inferred - /// type of `_Return<'_a>`. `infer_opaque_definition_from_instantiation` + /// type of `_Return<'_x>`. `infer_opaque_definition_from_instantiation` /// compares lifetimes directly, so we need to map the inference variables /// back to concrete lifetimes: `'static`, `ReEarlyParam` or `ReLateParam`. /// - /// First we map all the lifetimes in the concrete type to an equal - /// universal region that occurs in the concrete type's args, in this case - /// this would result in `&'1 i32`. We only consider regions in the args + /// First we map the regions in the the generic parameters `_Return<'1>` to + /// their `external_name` giving `_Return<'a>`. This step is a bit involved. + /// See the [rustc-dev-guide chapter] for more info. + /// + /// Then we map all the lifetimes in the concrete type to an equal + /// universal region that occurs in the opaque type's args, in this case + /// this would result in `&'a i32`. We only consider regions in the args /// in case there is an equal region that does not. For example, this should /// be allowed: /// `fn f<'a: 'b, 'b: 'a>(x: *mut &'b i32) -> impl Sized + 'a { x }` /// - /// Then we map the regions in both the type and the generic parameters to their - /// `external_name` giving `concrete_type = &'a i32`, - /// `args = ['static, 'a]`. This will then allow - /// `infer_opaque_definition_from_instantiation` to determine that - /// `_Return<'_a> = &'_a i32`. + /// This will then allow `infer_opaque_definition_from_instantiation` to + /// determine that `_Return<'_x> = &'_x i32`. /// /// There's a slight complication around closures. Given /// `fn f<'a: 'a>() { || {} }` the closure's type is something like /// `f::<'a>::{{closure}}`. The region parameter from f is essentially /// ignored by type checking so ends up being inferred to an empty region. /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`, - /// which has no `external_name` in which case we use `'empty` as the + /// which has no `external_name` in which case we use `'{erased}` as the /// region to pass to `infer_opaque_definition_from_instantiation`. + /// + /// [rustc-dev-guide chapter]: + /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html #[instrument(level = "debug", skip(self, infcx), ret)] pub(crate) fn infer_opaque_types( &self, @@ -66,86 +71,79 @@ impl<'tcx> RegionInferenceContext<'tcx> { opaque_ty_decls: FxIndexMap, OpaqueHiddenType<'tcx>>, ) -> FxIndexMap> { let mut result: FxIndexMap> = FxIndexMap::default(); - - let member_constraints: FxIndexMap<_, _> = self - .member_constraints - .all_indices() - .map(|ci| (self.member_constraints[ci].key, ci)) - .collect(); - debug!(?member_constraints); + let mut decls_modulo_regions: FxIndexMap, (OpaqueTypeKey<'tcx>, Span)> = + FxIndexMap::default(); for (opaque_type_key, concrete_type) in opaque_ty_decls { - let args = opaque_type_key.args; - debug!(?concrete_type, ?args); + debug!(?opaque_type_key, ?concrete_type); - let mut arg_regions = vec![self.universal_regions.fr_static]; + let mut arg_regions: Vec<(ty::RegionVid, ty::Region<'_>)> = + vec![(self.universal_regions.fr_static, infcx.tcx.lifetimes.re_static)]; - let to_universal_region = |vid, arg_regions: &mut Vec<_>| { - trace!(?vid); - let scc = self.constraint_sccs.scc(vid); - trace!(?scc); - match self.scc_values.universal_regions_outlived_by(scc).find_map(|lb| { - self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?) - }) { - Some(region) => { - let vid = self.universal_regions.to_region_vid(region); - arg_regions.push(vid); - region + let opaque_type_key = + opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |region| { + // Use the SCC representative instead of directly using `region`. + // See [rustc-dev-guide chapter] § "Strict lifetime equality". + let scc = self.constraint_sccs.scc(region.as_var()); + let vid = self.scc_representatives[scc]; + let named = match self.definitions[vid].origin { + // Iterate over all universal regions in a consistent order and find the + // *first* equal region. This makes sure that equal lifetimes will have + // the same name and simplifies subsequent handling. + // See [rustc-dev-guide chapter] § "Semantic lifetime equality". + NllRegionVariableOrigin::FreeRegion => self + .universal_regions + .universal_regions() + .filter(|&ur| { + // See [rustc-dev-guide chapter] § "Closure restrictions". + !matches!( + self.universal_regions.region_classification(ur), + Some(RegionClassification::External) + ) + }) + .find(|&ur| self.universal_region_relations.equal(vid, ur)) + .map(|ur| self.definitions[ur].external_name.unwrap()), + NllRegionVariableOrigin::Placeholder(placeholder) => { + Some(ty::Region::new_placeholder(infcx.tcx, placeholder)) + } + NllRegionVariableOrigin::Existential { .. } => None, } - None => { - arg_regions.push(vid); + .unwrap_or_else(|| { ty::Region::new_error_with_message( infcx.tcx, concrete_type.span, "opaque type with non-universal region args", ) - } - } - }; + }); - // Start by inserting universal regions from the member_constraint choice regions. - // This will ensure they get precedence when folding the regions in the concrete type. - if let Some(&ci) = member_constraints.get(&opaque_type_key) { - for &vid in self.member_constraints.choice_regions(ci) { - to_universal_region(vid, &mut arg_regions); - } - } - debug!(?arg_regions); - - // Next, insert universal regions from args, so we can translate regions that appear - // in them but are not subject to member constraints, for instance closure args. - let universal_args = infcx.tcx.fold_regions(args, |region, _| { - if let ty::RePlaceholder(..) = region.kind() { - // Higher kinded regions don't need remapping, they don't refer to anything outside of this the args. - return region; - } - let vid = self.to_region_vid(region); - to_universal_region(vid, &mut arg_regions) - }); - debug!(?universal_args); - debug!(?arg_regions); - - // Deduplicate the set of regions while keeping the chosen order. - let arg_regions = arg_regions.into_iter().collect::>(); - debug!(?arg_regions); - - let universal_concrete_type = - infcx.tcx.fold_regions(concrete_type, |region, _| match *region { - ty::ReVar(vid) => arg_regions - .iter() - .find(|ur_vid| self.eval_equal(vid, **ur_vid)) - .and_then(|ur_vid| self.definitions[*ur_vid].external_name) - .unwrap_or(infcx.tcx.lifetimes.re_erased), - _ => region, + arg_regions.push((vid, named)); + named }); - debug!(?universal_concrete_type); - - let opaque_type_key = - OpaqueTypeKey { def_id: opaque_type_key.def_id, args: universal_args }; - let ty = infcx.infer_opaque_definition_from_instantiation( - opaque_type_key, - universal_concrete_type, - ); + debug!(?opaque_type_key, ?arg_regions); + + let concrete_type = infcx.tcx.fold_regions(concrete_type, |region, _| { + arg_regions + .iter() + .find(|&&(arg_vid, _)| self.eval_equal(region.as_var(), arg_vid)) + .map(|&(_, arg_named)| arg_named) + .unwrap_or(infcx.tcx.lifetimes.re_erased) + }); + debug!(?concrete_type); + + let ty = + infcx.infer_opaque_definition_from_instantiation(opaque_type_key, concrete_type); + + // Sometimes, when the hidden type is an inference variable, it can happen that + // the hidden type becomes the opaque type itself. In this case, this was an opaque + // usage of the opaque type and we can ignore it. This check is mirrored in typeck's + // writeback. + // FIXME(-Znext-solver): This should be unnecessary with the new solver. + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() + && alias_ty.def_id == opaque_type_key.def_id.to_def_id() + && alias_ty.args == opaque_type_key.args + { + continue; + } // Sometimes two opaque types are the same only after we remap the generic parameters // back to the opaque type definition. E.g. we may have `OpaqueType` mapped to `(X, Y)` // and `OpaqueType` mapped to `(Y, X)`, and those are the same, but we only know that @@ -173,6 +171,29 @@ impl<'tcx> RegionInferenceContext<'tcx> { OpaqueHiddenType { ty, span: concrete_type.span }, ); } + + // Check that all opaque types have the same region parameters if they have the same + // non-region parameters. This is necessary because within the new solver we perform + // various query operations modulo regions, and thus could unsoundly select some impls + // that don't hold. + if !ty.references_error() + && let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( + infcx.tcx.erase_regions(opaque_type_key), + (opaque_type_key, concrete_type.span), + ) + && let Some((arg1, arg2)) = std::iter::zip( + prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), + opaque_type_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), + ) + .find(|(arg1, arg2)| arg1 != arg2) + { + infcx.dcx().emit_err(LifetimeMismatchOpaqueParam { + arg: arg1, + prev: arg2, + span: prev_span, + prev_span: concrete_type.span, + }); + } } result } @@ -192,7 +213,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc = self.constraint_sccs.scc(vid); // Special handling of higher-ranked regions. - if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + if !self.scc_universes[scc].is_root() { match self.scc_values.placeholders_contained_in(scc).enumerate().last() { // If the region contains a single placeholder then they're equal. Some((0, placeholder)) => { @@ -311,13 +332,13 @@ fn check_opaque_type_well_formed<'tcx>( parent_def_id = tcx.local_parent(parent_def_id); } - // FIXME(-Znext-solver): We probably should use `DefiningAnchor::Bind(&[])` + // FIXME(-Znext-solver): We probably should use `&[]` instead of // and prepopulate this `InferCtxt` with known opaque values, rather than - // using the `Bind` anchor here. For now it's fine. + // allowing opaque types to be defined and checking them after the fact. let infcx = tcx .infer_ctxt() .with_next_trait_solver(next_trait_solver) - .with_opaque_type_inference(DefiningAnchor::bind(tcx, parent_def_id)) + .with_opaque_type_inference(parent_def_id) .build(); let ocx = ObligationCtxt::new(&infcx); let identity_args = GenericArgs::identity_for_item(tcx, def_id); @@ -361,46 +382,46 @@ fn check_opaque_type_well_formed<'tcx>( } } -fn check_opaque_type_parameter_valid( - tcx: TyCtxt<'_>, - opaque_type_key: OpaqueTypeKey<'_>, +/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter]. +/// +/// [rustc-dev-guide chapter]: +/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html +fn check_opaque_type_parameter_valid<'tcx>( + tcx: TyCtxt<'tcx>, + opaque_type_key: OpaqueTypeKey<'tcx>, span: Span, ) -> Result<(), ErrorGuaranteed> { - let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id); - let (parent, is_ty_alias) = match opaque_ty_hir.expect_opaque_ty().origin { - OpaqueTyOrigin::TyAlias { parent, .. } => (parent, true), - OpaqueTyOrigin::AsyncFn(parent) | OpaqueTyOrigin::FnReturn(parent) => (parent, false), - }; - - let parent_generics = tcx.generics_of(parent); + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id); let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default(); - // Only check the parent generics, which will ignore any of the - // duplicated lifetime args that come from reifying late-bounds. - for (i, arg) in opaque_type_key.args.iter().take(parent_generics.count()).enumerate() { - if let Err(guar) = arg.error_reported() { - return Err(guar); - } - + for (i, arg) in opaque_type_key.iter_captured_args(tcx) { let arg_is_param = match arg.unpack() { GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), - GenericArgKind::Lifetime(lt) if is_ty_alias => { + GenericArgKind::Lifetime(lt) => { matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_)) + || (lt.is_static() && opaque_env.param_equal_static(i)) } - // FIXME(#113916): we can't currently check for unique lifetime params, - // see that issue for more. We will also have to ignore unused lifetime - // params for RPIT, but that's comparatively trivial ✨ - GenericArgKind::Lifetime(_) => continue, GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)), }; if arg_is_param { - seen_params.entry(arg).or_default().push(i); + // Register if the same lifetime appears multiple times in the generic args. + // There is an exception when the opaque type *requires* the lifetimes to be equal. + // See [rustc-dev-guide chapter] § "An exception to uniqueness rule". + let seen_where = seen_params.entry(arg).or_default(); + if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) { + seen_where.push(i); + } } else { // Prevent `fn foo() -> Foo` from being defining. - let opaque_param = parent_generics.param_at(i, tcx); + let opaque_param = opaque_generics.param_at(i, tcx); let kind = opaque_param.kind.descr(); + if let Err(guar) = opaque_env.param_is_error(i) { + return Err(guar); + } + return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam { ty: arg, kind, @@ -412,10 +433,10 @@ fn check_opaque_type_parameter_valid( for (_, indices) in seen_params { if indices.len() > 1 { - let descr = parent_generics.param_at(indices[0], tcx).kind.descr(); + let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); let spans: Vec<_> = indices .into_iter() - .map(|i| tcx.def_span(parent_generics.param_at(i, tcx).def_id)) + .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) .collect(); #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] @@ -429,3 +450,91 @@ fn check_opaque_type_parameter_valid( Ok(()) } + +/// Computes if an opaque type requires a lifetime parameter to be equal to +/// another one or to the `'static` lifetime. +/// These requirements are derived from the explicit and implied bounds. +struct LazyOpaqueTyEnv<'tcx> { + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + + /// Equal parameters will have the same name. Computed Lazily. + /// Example: + /// `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;` + /// Identity args: `['a, 'b, 'c]` + /// Canonical args: `['static, 'b, 'b]` + canonical_args: std::cell::OnceCell>, +} + +impl<'tcx> LazyOpaqueTyEnv<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() } + } + + pub fn param_equal_static(&self, param_index: usize) -> bool { + self.get_canonical_args()[param_index].expect_region().is_static() + } + + pub fn params_equal(&self, param1: usize, param2: usize) -> bool { + let canonical_args = self.get_canonical_args(); + canonical_args[param1] == canonical_args[param2] + } + + pub fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> { + self.get_canonical_args()[param_index].error_reported() + } + + fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> { + use rustc_hir as hir; + use rustc_infer::infer::outlives::env::OutlivesEnvironment; + use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; + + if let Some(&canonical_args) = self.canonical_args.get() { + return canonical_args; + } + + let &Self { tcx, def_id, .. } = self; + let origin = tcx.opaque_type_origin(def_id); + let parent = match origin { + hir::OpaqueTyOrigin::FnReturn(parent) + | hir::OpaqueTyOrigin::AsyncFn(parent) + | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, + }; + let param_env = tcx.param_env(parent); + let args = GenericArgs::identity_for_item(tcx, parent).extend_to( + tcx, + def_id.to_def_id(), + |param, _| { + tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into() + }, + ); + + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + + let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| { + tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds"); + Default::default() + }); + let implied_bounds = infcx.implied_bounds_tys(param_env, parent, &wf_tys); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + + let mut seen = vec![tcx.lifetimes.re_static]; + let canonical_args = tcx.fold_regions(args, |r1, _| { + if r1.is_error() { + r1 + } else if let Some(&r2) = seen.iter().find(|&&r2| { + let free_regions = outlives_env.free_region_map(); + free_regions.sub_free_regions(tcx, r1, r2) + && free_regions.sub_free_regions(tcx, r2, r1) + }) { + r2 + } else { + seen.push(r1); + r1 + } + }); + self.canonical_args.set(canonical_args).unwrap(); + canonical_args + } +} diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs index eeb944702a7f9..97ddc45ee476d 100644 --- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs +++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs @@ -1,9 +1,8 @@ use crate::constraints::ConstraintSccIndex; use crate::RegionInferenceContext; -use itertools::Itertools; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::graph; use rustc_data_structures::graph::vec_graph::VecGraph; -use rustc_data_structures::graph::WithSuccessors; use rustc_middle::ty::RegionVid; use std::ops::Range; @@ -24,8 +23,7 @@ impl ReverseSccGraph { scc0: ConstraintSccIndex, ) -> impl Iterator + 'a { let mut duplicates = FxIndexSet::default(); - self.graph - .depth_first_search(scc0) + graph::depth_first_search(&self.graph, scc0) .flat_map(move |scc1| { self.scc_regions .get(&scc1) @@ -48,16 +46,16 @@ impl RegionInferenceContext<'_> { .universal_regions .universal_regions() .map(|region| (self.constraint_sccs.scc(region), region)) - .collect_vec(); + .collect::>(); paired_scc_regions.sort(); let universal_regions = paired_scc_regions.iter().map(|&(_, region)| region).collect(); let mut scc_regions = FxIndexMap::default(); let mut start = 0; - for (scc, group) in &paired_scc_regions.into_iter().group_by(|(scc, _)| *scc) { - let group_size = group.count(); - scc_regions.insert(scc, start..start + group_size); - start += group_size; + for chunk in paired_scc_regions.chunk_by(|&(scc1, _), &(scc2, _)| scc1 == scc2) { + let (scc, _) = chunk[0]; + scc_regions.insert(scc, start..start + chunk.len()); + start += chunk.len(); } self.rev_scc_graph = Some(ReverseSccGraph { graph, scc_regions, universal_regions }); diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 77021ae4321d2..40c2ef1c91e14 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -304,6 +304,19 @@ pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> { pub param_span: Span, } +#[derive(Diagnostic)] +#[diag(borrowck_opaque_type_lifetime_mismatch)] +pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> { + pub arg: GenericArg<'tcx>, + pub prev: GenericArg<'tcx>, + #[primary_span] + #[label] + #[note] + pub span: Span, + #[label(borrowck_prev_lifetime_label)] + pub prev_span: Span, +} + #[derive(Subdiagnostic)] pub(crate) enum CaptureReasonLabel<'a> { #[label(borrowck_moved_due_to_call)] diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index e5ebf97cfc4ee..49d3f7381d910 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -2,6 +2,7 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::canonical::Canonical; +use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_span::def_id::DefId; @@ -39,6 +40,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx, locations.span(self.body))?; + if cfg!(debug_assertions) { + let data = self.infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + panic!("leftover region constraints: {data:#?}"); + } + } debug!(?output, ?constraints); @@ -61,7 +68,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Ok(output) } - pub(super) fn instantiate_canonical_with_fresh_inference_vars( + pub(super) fn instantiate_canonical( &mut self, span: Span, canonical: &Canonical<'tcx, T>, @@ -69,8 +76,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { where T: TypeFoldable>, { - let (instantiated, _) = - self.infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); + let (instantiated, _) = self.infcx.instantiate_canonical(span, canonical); instantiated } @@ -83,7 +89,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) { self.prove_predicate( ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Trait( - ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Positive }, + ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive }, ))), locations, category, diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index de75a9857f8b5..5aa8fe213814d 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -4,6 +4,7 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; +use rustc_middle::bug; use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 86d20599a2a88..7553e3ee04fb4 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -164,6 +164,13 @@ impl UniversalRegionRelations<'_> { self.outlives.contains(fr1, fr2) } + /// Returns `true` if fr1 is known to equal fr2. + /// + /// This will only ever be true for universally quantified regions. + pub(crate) fn equal(&self, fr1: RegionVid, fr2: RegionVid) -> bool { + self.outlives.contains(fr1, fr2) && self.outlives.contains(fr2, fr1) + } + /// Returns a vector of free regions `x` such that `fr1: x` is /// known to hold. pub(crate) fn regions_outlived_by(&self, fr1: RegionVid) -> Vec { diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 8af78b08f69c1..2511a1535af18 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -11,7 +11,7 @@ use std::assert_matches::assert_matches; use itertools::Itertools; use rustc_hir as hir; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin}; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty}; @@ -39,8 +39,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // (e.g., the `_` in the code above) with fresh variables. // Then replace the bound items in the fn sig with fresh variables, // so that they represent the view from "inside" the closure. - let user_provided_sig = self - .instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig); + let user_provided_sig = self.instantiate_canonical(body.span, &user_provided_poly_sig); let mut user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars( body.span, BoundRegionConversionTime::FnCall, @@ -76,10 +75,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); let next_ty_var = || { - self.infcx.next_ty_var(TypeVariableOrigin { - span: body.span, - kind: TypeVariableOriginKind::MiscVariable, - }) + self.infcx.next_ty_var(TypeVariableOrigin { span: body.span, param_def_id: None }) }; let output_ty = Ty::new_coroutine( self.tcx(), @@ -88,7 +84,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.tcx(), ty::CoroutineArgsParts { parent_args: args.parent_args(), - kind_ty: Ty::from_closure_kind(self.tcx(), args.kind()), + kind_ty: Ty::from_coroutine_closure_kind(self.tcx(), args.kind()), return_ty: user_provided_sig.output(), tupled_upvars_ty, // For async closures, none of these can be annotated, so just fill diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 51ae7d14e4386..38ec9f7678ebc 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -2,6 +2,7 @@ use itertools::{Either, Itertools}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::visit::{TyContext, Visitor}; use rustc_middle::mir::{Body, Local, Location, SourceInfo}; +use rustc_middle::span_bug; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs index 45f7b07fd5f93..7f5302270439c 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/polonius.rs @@ -87,6 +87,8 @@ pub(super) fn populate_access_facts<'a, 'tcx>( body: &Body<'tcx>, location_table: &LocationTable, move_data: &MoveData<'tcx>, + //FIXME: this is not mutated, but expected to be modified as + // out param, bug? dropped_at: &mut Vec<(Local, Location)>, ) { debug!("populate_access_facts()"); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 18975a4e3b271..6cc0e67c0f801 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -1,5 +1,4 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::graph::WithSuccessors; use rustc_index::bit_set::BitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; @@ -64,7 +63,10 @@ pub(super) fn trace<'mir, 'tcx>( // Traverse each issuing region's constraints, and record the loan as flowing into the // outlived region. for (loan, issuing_region_data) in borrow_set.iter_enumerated() { - for succ in region_graph.depth_first_search(issuing_region_data.region) { + for succ in rustc_data_structures::graph::depth_first_search( + ®ion_graph, + issuing_region_data.region, + ) { // We don't need to mention that a loan flows into its issuing region. if succ == issuing_region_data.region { continue; @@ -200,7 +202,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { for local in boring_locals { let local_ty = self.cx.body.local_decls[local].ty; let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({ - let typeck = &mut self.cx.typeck; + let typeck = &self.cx.typeck; move || LivenessContext::compute_drop_data(typeck, local_ty) }); @@ -542,7 +544,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { ); let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ - let typeck = &mut self.typeck; + let typeck = &self.typeck; move || Self::compute_drop_data(typeck, dropped_ty) }); @@ -597,10 +599,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { }); } - fn compute_drop_data( - typeck: &mut TypeChecker<'_, 'tcx>, - dropped_ty: Ty<'tcx>, - ) -> DropData<'tcx> { + fn compute_drop_data(typeck: &TypeChecker<'_, 'tcx>, dropped_ty: Ty<'tcx>) -> DropData<'tcx> { debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); match typeck diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 412d50f493eee..b67c5d8581839 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -16,7 +16,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::RegionConstraintData; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{ BoundRegion, BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin, }; @@ -33,6 +33,7 @@ use rustc_middle::ty::{ OpaqueHiddenType, OpaqueTypeKey, RegionVid, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, }; use rustc_middle::ty::{GenericArgsRef, UserArgs}; +use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::Spanned; @@ -229,6 +230,22 @@ pub(crate) fn type_check<'mir, 'tcx>( ); } + // Convert all regions to nll vars. + let (opaque_type_key, hidden_type) = + infcx.tcx.fold_regions((opaque_type_key, hidden_type), |region, _| { + match region.kind() { + ty::ReVar(_) => region, + ty::RePlaceholder(placeholder) => checker + .borrowck_context + .constraints + .placeholder_region(infcx, placeholder), + _ => ty::Region::new_var( + infcx.tcx, + checker.borrowck_context.universal_regions.to_region_vid(region), + ), + } + }); + (opaque_type_key, hidden_type) }) .collect(); @@ -592,7 +609,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } self.cx.borrowck_context.constraints.outlives_constraints.push(constraint) } - // If the region is live at at least one location in the promoted MIR, + // If the region is live at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region // at the location provided as an argument to this method // @@ -1109,7 +1126,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tcx = self.tcx(); for user_annotation in self.user_type_annotations { let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; - let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty); + let annotation = self.instantiate_canonical(span, user_ty); if let ty::UserType::TypeOf(def, args) = annotation && let DefKind::InlineConst = tcx.def_kind(def) { @@ -1905,7 +1922,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } AggregateKind::Array(ty) => Ok(ty), - AggregateKind::Tuple => { + AggregateKind::Tuple | AggregateKind::RawPtr(..) => { unreachable!("This should have been covered in check_rvalues"); } } @@ -2000,7 +2017,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } - &Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {} + &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} Rvalue::ShallowInitBox(operand, ty) => { self.check_operand(operand, location); @@ -2157,15 +2174,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) => { - let ty::RawPtr(ty::TypeAndMut { ty: ty_from, mutbl: hir::Mutability::Mut }) = - op.ty(body, tcx).kind() + let ty::RawPtr(ty_from, hir::Mutability::Mut) = op.ty(body, tcx).kind() else { span_mirbug!(self, rvalue, "unexpected base type for cast {:?}", ty,); return; }; - let ty::RawPtr(ty::TypeAndMut { ty: ty_to, mutbl: hir::Mutability::Not }) = - ty.kind() - else { + let ty::RawPtr(ty_to, hir::Mutability::Not) = ty.kind() else { span_mirbug!(self, rvalue, "unexpected target type for cast {:?}", ty,); return; }; @@ -2190,12 +2204,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_from = op.ty(body, tcx); let opt_ty_elem_mut = match ty_from.kind() { - ty::RawPtr(ty::TypeAndMut { mutbl: array_mut, ty: array_ty }) => { - match array_ty.kind() { - ty::Array(ty_elem, _) => Some((ty_elem, *array_mut)), - _ => None, - } - } + ty::RawPtr(array_ty, array_mut) => match array_ty.kind() { + ty::Array(ty_elem, _) => Some((ty_elem, *array_mut)), + _ => None, + }, _ => None, }; @@ -2210,9 +2222,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { }; let (ty_to, ty_to_mut) = match ty.kind() { - ty::RawPtr(ty::TypeAndMut { mutbl: ty_to_mut, ty: ty_to }) => { - (ty_to, *ty_to_mut) - } + ty::RawPtr(ty_to, ty_to_mut) => (ty_to, *ty_to_mut), _ => { span_mirbug!( self, @@ -2252,7 +2262,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerExposeAddress => { + CastKind::PointerExposeProvenance => { let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); @@ -2262,7 +2272,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!( self, rvalue, - "Invalid PointerExposeAddress cast {:?} -> {:?}", + "Invalid PointerExposeProvenance cast {:?} -> {:?}", ty_from, ty ) @@ -2270,7 +2280,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - CastKind::PointerFromExposedAddress => { + CastKind::PointerWithExposedProvenance => { let ty_from = op.ty(body, tcx); let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); @@ -2280,7 +2290,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!( self, rvalue, - "Invalid PointerFromExposedAddress cast {:?} -> {:?}", + "Invalid PointerWithExposedProvenance cast {:?} -> {:?}", ty_from, ty ) @@ -2413,10 +2423,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_left = left.ty(body, tcx); match ty_left.kind() { // Types with regions are comparable if they have a common super-type. - ty::RawPtr(_) | ty::FnPtr(_) => { + ty::RawPtr(_, _) | ty::FnPtr(_) => { let ty_right = right.ty(body, tcx); let common_ty = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, + param_def_id: None, span: body.source_info(location).span, }); self.sub_types( @@ -2509,6 +2519,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { AggregateKind::Closure(_, _) => None, AggregateKind::Coroutine(_, _) => None, AggregateKind::CoroutineClosure(_, _) => None, + AggregateKind::RawPtr(_, _) => None, }, } } @@ -2530,6 +2541,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return; } + if let AggregateKind::RawPtr(..) = aggregate_kind { + bug!("RawPtr should only be in runtime MIR"); + } + for (i, operand) in operands.iter_enumerated() { let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) { Ok(field_ty) => field_ty, @@ -2748,7 +2763,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ), ), - AggregateKind::Array(_) | AggregateKind::Tuple => { + AggregateKind::Array(_) | AggregateKind::Tuple | AggregateKind::RawPtr(..) => { (CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty()) } }; diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 78609a482ed22..5e4b3e532c413 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,10 +1,11 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_infer::infer::{ObligationEmittingRelation, StructurallyRelateAliases}; use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_middle::mir::ConstraintCategory; +use rustc_middle::span_bug; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::fold::FnMutDelegate; @@ -129,10 +130,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> { // the opaque. let mut enable_subtyping = |ty, opaque_is_expected| { let ty_vid = infcx.next_ty_var_id_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.span(), - }, + TypeVariableOrigin { param_def_id: None, span: self.span() }, ty::UniverseIndex::ROOT, ); @@ -437,7 +435,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> { a: ty::Const<'tcx>, b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { - let a = self.type_checker.infcx.shallow_resolve(a); + let a = self.type_checker.infcx.shallow_resolve_const(a); assert!(!a.has_non_region_infer(), "unexpected inference var {:?}", a); assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b); diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 9c65f64b03fec..070238fc37882 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -27,6 +27,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, InlineConstArgs, InlineConstArgsParts, RegionVid, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgs, GenericArgsRef}; +use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{kw, sym}; use rustc_span::Symbol; use std::iter; diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index ba8401393d7ab..0f15899031938 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -114,8 +114,12 @@ builtin_macros_env_not_defined = environment variable `{$var}` not defined at co .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time +builtin_macros_env_not_unicode = environment variable `{$var}` is not a valid Unicode string + builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments +builtin_macros_expected_comma_in_list = expected token: `,` + builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern builtin_macros_expected_register_class_or_explicit_register = expected register class or explicit register @@ -217,12 +221,16 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants .help = consider a manual implementation of `Default` +builtin_macros_only_one_argument = {$name} takes 1 argument + builtin_macros_proc_macro = `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` builtin_macros_requires_cfg_pattern = macro requires a cfg-pattern as an argument .label = cfg-pattern required +builtin_macros_takes_no_arguments = {$name} takes no arguments + builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests .label = `{$kind}` because of this diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index bc94e0b972b47..064cf7d7f0f8e 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; -pub fn expand( +pub(crate) fn expand( ecx: &mut ExtCtxt<'_>, _span: Span, meta_item: &ast::MetaItem, diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 62c02817011fd..49b1b8cf99268 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,3 +1,5 @@ +use crate::errors; +use crate::util::expr_to_spanned_string; use ast::token::IdentIsRaw; use rustc_ast as ast; use rustc_ast::ptr::P; @@ -16,8 +18,6 @@ use rustc_span::{ErrorGuaranteed, InnerSpan, Span}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; -use crate::errors; - pub struct AsmArgs { pub templates: Vec>, pub operands: Vec<(ast::InlineAsmOperand, Span)>, @@ -29,7 +29,7 @@ pub struct AsmArgs { } fn parse_args<'a>( - ecx: &mut ExtCtxt<'a>, + ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream, is_global_asm: bool, @@ -189,7 +189,7 @@ pub fn parse_asm_args<'a>( args.templates.push(template); continue; } else { - return p.unexpected(); + p.unexpected_any()? }; allow_templates = false; @@ -303,7 +303,7 @@ pub fn parse_asm_args<'a>( /// /// This function must be called immediately after the option token is parsed. /// Otherwise, the suggestion will be incorrect. -fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) { +fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) { // Tool-only output let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span }; p.psess.dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span }); @@ -315,7 +315,7 @@ fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) { /// This function must be called immediately after the option token is parsed. /// Otherwise, the error will not point to the correct spot. fn try_set_option<'a>( - p: &mut Parser<'a>, + p: &Parser<'a>, args: &mut AsmArgs, symbol: Symbol, option: ast::InlineAsmOptions, diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 35a0857fe51ce..c75050f270188 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -15,7 +15,7 @@ use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use thin_vec::thin_vec; -pub fn expand_assert<'cx>( +pub(crate) fn expand_assert<'cx>( cx: &'cx mut ExtCtxt<'_>, span: Span, tts: TokenStream, @@ -111,7 +111,7 @@ fn expr_if_not( cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els) } -fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { +fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { let mut parser = cx.new_parser_from_tts(stream); if parser.token == token::Eof { @@ -151,7 +151,7 @@ fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PRes }; if parser.token != token::Eof { - return parser.unexpected(); + parser.unexpected()?; } Ok(Assert { cond_expr, custom_message }) diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 56c56b2704b00..92efeab08ebea 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -245,7 +245,7 @@ impl<'cx, 'a> Context<'cx, 'a> { ExprKind::Let(_, local_expr, _, _) => { self.manage_cond_expr(local_expr); } - ExprKind::Match(local_expr, _) => { + ExprKind::Match(local_expr, ..) => { self.manage_cond_expr(local_expr); } ExprKind::MethodCall(call) => { diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 9197b9ebdf9fc..827719d794493 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -11,7 +11,7 @@ use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_span::Span; -pub fn expand_cfg( +pub(crate) fn expand_cfg( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -35,7 +35,7 @@ pub fn expand_cfg( }) } -fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> { +fn parse_cfg<'a>(cx: &ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> { let mut p = cx.new_parser_from_tts(tts); if p.token == token::Eof { diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs index 1933b2e1fb7fe..98c0ca3a526b8 100644 --- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs +++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs @@ -10,7 +10,7 @@ use rustc_span::Span; pub(crate) struct Expander; -fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { +fn validate_input<'a>(ecx: &ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { use errors::CfgAccessibleInvalid::*; match mi.meta_item_list() { None => {} diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 93f7d09546b70..5f63a8ae0a8c2 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -18,6 +18,7 @@ use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Span; use smallvec::SmallVec; +use tracing::instrument; pub(crate) fn expand( ecx: &mut ExtCtxt<'_>, @@ -246,18 +247,18 @@ impl MutVisitor for CfgEval<'_, '_> { } fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { - mut_visit::noop_flat_map_assoc_item(configure!(self, item), self) + mut_visit::noop_flat_map_item(configure!(self, item), self) } fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { - mut_visit::noop_flat_map_assoc_item(configure!(self, item), self) + mut_visit::noop_flat_map_item(configure!(self, item), self) } fn flat_map_foreign_item( &mut self, foreign_item: P, ) -> SmallVec<[P; 1]> { - mut_visit::noop_flat_map_foreign_item(configure!(self, foreign_item), self) + mut_visit::noop_flat_map_item(configure!(self, foreign_item), self) } fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> { diff --git a/compiler/rustc_builtin_macros/src/compile_error.rs b/compiler/rustc_builtin_macros/src/compile_error.rs index 2f2a87fc9aa00..a08e8b2819b56 100644 --- a/compiler/rustc_builtin_macros/src/compile_error.rs +++ b/compiler/rustc_builtin_macros/src/compile_error.rs @@ -1,11 +1,11 @@ // The compiler code necessary to support the compile_error! extension. +use crate::util::get_single_str_from_tts; use rustc_ast::tokenstream::TokenStream; -use rustc_expand::base::get_single_str_from_tts; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_span::Span; -pub fn expand_compile_error<'cx>( +pub(crate) fn expand_compile_error<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index 93a7ac05a9bef..15af79ef67d4a 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -1,13 +1,12 @@ +use crate::errors; +use crate::util::get_exprs_from_tts; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ExprKind, LitKind, UnOp}; -use rustc_expand::base::get_exprs_from_tts; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_session::errors::report_lit_error; use rustc_span::symbol::Symbol; -use crate::errors; - -pub fn expand_concat( +pub(crate) fn expand_concat( cx: &mut ExtCtxt<'_>, sp: rustc_span::Span, tts: TokenStream, diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs index a2f827c5567d8..3130870df4105 100644 --- a/compiler/rustc_builtin_macros/src/concat_bytes.rs +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -1,14 +1,13 @@ +use crate::errors; +use crate::util::get_exprs_from_tts; use rustc_ast::{ptr::P, token, tokenstream::TokenStream, ExprKind, LitIntType, LitKind, UintTy}; -use rustc_expand::base::get_exprs_from_tts; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_session::errors::report_lit_error; use rustc_span::{ErrorGuaranteed, Span}; -use crate::errors; - /// Emits errors for literal expressions that are invalid inside and outside of an array. fn invalid_type_err( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, token_lit: token::Lit, span: Span, is_nested: bool, @@ -65,7 +64,7 @@ fn invalid_type_err( /// Otherwise, returns `None`, and either pushes the `expr`'s span to `missing_literals` or /// updates `guar` accordingly. fn handle_array_element( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, guar: &mut Option, missing_literals: &mut Vec, expr: &P, @@ -108,7 +107,7 @@ fn handle_array_element( None } -pub fn expand_concat_bytes( +pub(crate) fn expand_concat_bytes( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs index 3ddb0ae45b51a..13729a9d250fe 100644 --- a/compiler/rustc_builtin_macros/src/concat_idents.rs +++ b/compiler/rustc_builtin_macros/src/concat_idents.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use crate::errors; -pub fn expand_concat_idents<'cx>( +pub(crate) fn expand_concat_idents<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 4f412cf79d91f..d14858e5c1db3 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -3,14 +3,18 @@ use crate::errors; use rustc_ast as ast; use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; -use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; +use rustc_expand::base::{ + Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier, +}; use rustc_feature::AttributeTemplate; use rustc_parse::validate_attr; use rustc_session::Session; use rustc_span::symbol::{sym, Ident}; use rustc_span::{ErrorGuaranteed, Span}; -pub(crate) struct Expander(pub bool); +pub(crate) struct Expander { + pub is_const: bool, +} impl MultiItemModifier for Expander { fn expand( @@ -58,7 +62,12 @@ impl MultiItemModifier for Expander { report_path_args(sess, meta); meta.path.clone() }) - .map(|path| (path, dummy_annotatable(), None, self.0)) + .map(|path| DeriveResolution { + path, + item: dummy_annotatable(), + exts: None, + is_const: self.is_const, + }) .collect() } _ => vec![], @@ -67,15 +76,15 @@ impl MultiItemModifier for Expander { // Do not configure or clone items unless necessary. match &mut resolutions[..] { [] => {} - [(_, first_item, ..), others @ ..] => { - *first_item = cfg_eval( + [first, others @ ..] => { + first.item = cfg_eval( sess, features, item.clone(), ecx.current_expansion.lint_node_id, ); - for (_, item, _, _) in others { - *item = first_item.clone(); + for other in others { + other.item = first.item.clone(); } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 8027ca2e7bb47..97e2344ff30e5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -5,8 +5,8 @@ use rustc_ast::MetaItem; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::Span; -pub fn expand_deriving_copy( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_copy( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -28,8 +28,8 @@ pub fn expand_deriving_copy( trait_def.expand(cx, mitem, item, push); } -pub fn expand_deriving_const_param_ty( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_const_param_ty( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 267405ac32e24..abcb402a46f8e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -8,8 +8,8 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; -pub fn expand_deriving_clone( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_clone( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -94,7 +94,7 @@ pub fn expand_deriving_clone( fn cs_clone_simple( name: &str, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, is_union: bool, @@ -157,14 +157,14 @@ fn cs_clone_simple( fn cs_clone( name: &str, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, ) -> BlockOrExpr { let ctor_path; let all_fields; let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); - let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo| { + let subcall = |cx: &ExtCtxt<'_>, field: &FieldInfo| { let args = thin_vec![field.self_expr.clone()]; cx.expr_call_global(field.span, fn_path.clone(), args) }; @@ -181,8 +181,8 @@ fn cs_clone( all_fields = af; vdata = &variant.data; } - EnumTag(..) | AllFieldlessEnum(..) => { - cx.dcx().span_bug(trait_span, format!("enum tags in `derive({name})`",)) + EnumDiscr(..) | AllFieldlessEnum(..) => { + cx.dcx().span_bug(trait_span, format!("enum discriminants in `derive({name})`",)) } StaticEnum(..) | StaticStruct(..) => { cx.dcx().span_bug(trait_span, format!("associated function in `derive({name})`")) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index ce3fa1ab32c6a..53a1513160542 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -9,8 +9,8 @@ use rustc_span::symbol::sym; use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; -pub fn expand_deriving_eq( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_eq( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -49,7 +49,7 @@ pub fn expand_deriving_eq( } fn cs_total_eq_assert( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, ) -> BlockOrExpr { diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 0923acfeeddb8..8470d466a233c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -7,8 +7,8 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use thin_vec::thin_vec; -pub fn expand_deriving_ord( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_ord( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -39,7 +39,7 @@ pub fn expand_deriving_ord( trait_def.expand(cx, mitem, item, push) } -pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { +pub(crate) fn cs_cmp(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let test_id = Ident::new(sym::cmp, span); let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); let cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 006110cd4b1f6..dc73caa4ad5be 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -8,15 +8,15 @@ use rustc_span::symbol::sym; use rustc_span::Span; use thin_vec::thin_vec; -pub fn expand_deriving_partial_eq( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_partial_eq( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), is_const: bool, ) { - fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { + fn cs_eq(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let base = true; let expr = cs_fold( true, // use foldl diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 6eccd67f8af19..006e5a3d268a6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -7,8 +7,8 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use thin_vec::thin_vec; -pub fn expand_deriving_partial_ord( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_partial_ord( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -20,12 +20,12 @@ pub fn expand_deriving_partial_ord( Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std)); // Order in which to perform matching - let tag_then_data = if let Annotatable::Item(item) = item + let discr_then_data = if let Annotatable::Item(item) = item && let ItemKind::Enum(def, _) = &item.kind { let dataful: Vec = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect(); match dataful.iter().filter(|&&b| b).count() { - // No data, placing the tag check first makes codegen simpler + // No data, placing the discriminant check first makes codegen simpler 0 => true, 1..=2 => false, _ => (0..dataful.len() - 1).any(|i| { @@ -50,7 +50,7 @@ pub fn expand_deriving_partial_ord( attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, combine_substructure: combine_substructure(Box::new(|cx, span, substr| { - cs_partial_cmp(cx, span, substr, tag_then_data) + cs_partial_cmp(cx, span, substr, discr_then_data) })), }; @@ -69,10 +69,10 @@ pub fn expand_deriving_partial_ord( } fn cs_partial_cmp( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>, - tag_then_data: bool, + discr_then_data: bool, ) -> BlockOrExpr { let test_id = Ident::new(sym::cmp, span); let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); @@ -108,12 +108,12 @@ fn cs_partial_cmp( // cmp => cmp // } // ``` - // where `expr2` is `partial_cmp(self_tag, other_tag)`, and `expr1` is a `match` - // against the enum variants. This means that we begin by comparing the enum tags, + // where `expr2` is `partial_cmp(self_discr, other_discr)`, and `expr1` is a `match` + // against the enum variants. This means that we begin by comparing the enum discriminants, // before either inspecting their contents (if they match), or returning - // the `cmp::Ordering` of comparing the enum tags. + // the `cmp::Ordering` of comparing the enum discriminants. // ``` - // match partial_cmp(self_tag, other_tag) { + // match partial_cmp(self_discr, other_discr) { // Some(Ordering::Equal) => match (self, other) { // (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0), // (Self::B(self_0), Self::B(other_0)) => partial_cmp(self_0, other_0), @@ -126,13 +126,13 @@ fn cs_partial_cmp( // ``` // match (self, other) { // (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0), - // _ => partial_cmp(self_tag, other_tag) + // _ => partial_cmp(self_discr, other_discr) // } // ``` // Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354 - if !tag_then_data - && let ExprKind::Match(_, arms) = &mut expr1.kind + if !discr_then_data + && let ExprKind::Match(_, arms, _) = &mut expr1.kind && let Some(last) = arms.last_mut() && let PatKind::Wild = last.pat.kind { diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index b11a1b6cda154..57ec0435e3e05 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -2,15 +2,14 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use ast::EnumDef; -use rustc_ast::{self as ast, MetaItem}; +use rustc_ast::{self as ast, EnumDef, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; -pub fn expand_deriving_debug( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_debug( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -46,15 +45,15 @@ pub fn expand_deriving_debug( trait_def.expand(cx, mitem, item, push) } -fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { +fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { // We want to make sure we have the ctxt set so that we can use unstable methods let span = cx.with_def_site_ctxt(span); let (ident, vdata, fields) = match substr.fields { Struct(vdata, fields) => (substr.type_ident, *vdata, fields), - EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields), + EnumMatching(_, v, fields) => (v.ident, &v.data, fields), AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr), - EnumTag(..) | StaticStruct(..) | StaticEnum(..) => { + EnumDiscr(..) | StaticStruct(..) | StaticEnum(..) => { cx.dcx().span_bug(span, "nonsensical .fields in `#[derive(Debug)]`") } }; @@ -210,7 +209,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> /// } /// ``` fn show_fieldless_enum( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, span: Span, def: &EnumDef, substr: &Substructure<'_>, diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index bf4693cd54198..e9851c87aeaef 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -10,8 +10,8 @@ use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; -pub fn expand_deriving_rustc_decodable( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_rustc_decodable( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -63,7 +63,7 @@ pub fn expand_deriving_rustc_decodable( } fn decodable_substructure( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, krate: Symbol, @@ -186,14 +186,14 @@ fn decodable_substructure( /// - `outer_pat_path` is the path to this enum variant/struct /// - `getarg` should retrieve the `usize`-th field with name `@str`. fn decode_static_fields( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, outer_pat_path: ast::Path, fields: &StaticFields, mut getarg: F, ) -> P where - F: FnMut(&mut ExtCtxt<'_>, Span, Symbol, usize) -> P, + F: FnMut(&ExtCtxt<'_>, Span, Symbol, usize) -> P, { match fields { Unnamed(fields, is_tuple) => { diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 292a916e2a78b..bf92ddb33701c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -12,8 +12,8 @@ use rustc_span::{ErrorGuaranteed, Span}; use smallvec::SmallVec; use thin_vec::{thin_vec, ThinVec}; -pub fn expand_deriving_default( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_default( + cx: &ExtCtxt<'_>, span: Span, mitem: &ast::MetaItem, item: &Annotatable, @@ -54,7 +54,7 @@ pub fn expand_deriving_default( } fn default_struct_substructure( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, summary: &StaticFields, @@ -81,7 +81,7 @@ fn default_struct_substructure( } fn default_enum_substructure( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, enum_def: &EnumDef, ) -> BlockOrExpr { @@ -103,7 +103,7 @@ fn default_enum_substructure( } fn extract_default_variant<'a>( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, enum_def: &'a EnumDef, trait_span: Span, ) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> { @@ -173,7 +173,7 @@ fn extract_default_variant<'a>( } fn validate_default_attribute( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, default_variant: &rustc_ast::Variant, ) -> Result<(), ErrorGuaranteed> { let attrs: SmallVec<[_; 1]> = diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index 14d93a8cc23c2..3bd74d8d0198a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -94,8 +94,8 @@ use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; -pub fn expand_deriving_rustc_encodable( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_rustc_encodable( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -147,7 +147,7 @@ pub fn expand_deriving_rustc_encodable( } fn encodable_substructure( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, krate: Symbol, @@ -226,7 +226,7 @@ fn encodable_substructure( BlockOrExpr::new_expr(expr) } - EnumMatching(idx, _, variant, fields) => { + EnumMatching(idx, variant, fields) => { // We're not generating an AST that the borrow checker is expecting, // so we need to generate a unique local variable to take the // mutable loan out on, otherwise we get conflicts which don't diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3cb3e30daa72e..52c1ba1757b1e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -21,7 +21,7 @@ //! `struct T(i32, char)`). //! - `EnumMatching`, when `Self` is an enum and all the arguments are the //! same variant of the enum (e.g., `Some(1)`, `Some(3)` and `Some(4)`) -//! - `EnumTag` when `Self` is an enum, for comparing the enum tags. +//! - `EnumDiscr` when `Self` is an enum, for comparing the enum discriminants. //! - `StaticEnum` and `StaticStruct` for static methods, where the type //! being derived upon is either an enum or struct respectively. (Any //! argument with type Self is just grouped among the non-self @@ -143,11 +143,11 @@ //! ) //! ``` //! -//! For the tags, +//! For the discriminants, //! //! ```text -//! EnumTag( -//! &[, ], +//! EnumDiscr( +//! &[, ], //! , //! ) //! ``` @@ -174,13 +174,13 @@ //! ) //! ``` -pub use StaticFields::*; -pub use SubstructureFields::*; +pub(crate) use StaticFields::*; +pub(crate) use SubstructureFields::*; use crate::{deriving, errors}; use rustc_ast::ptr::P; use rustc_ast::{ - self as ast, BindingAnnotation, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, + self as ast, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, Mutability, PatKind, TyKind, VariantData, }; use rustc_attr as attr; @@ -195,9 +195,9 @@ use std::vec; use thin_vec::{thin_vec, ThinVec}; use ty::{Bounds, Path, Ref, Self_, Ty}; -pub mod ty; +pub(crate) mod ty; -pub struct TraitDef<'a> { +pub(crate) struct TraitDef<'a> { /// The span for the current #[derive(Foo)] header. pub span: Span, @@ -224,7 +224,7 @@ pub struct TraitDef<'a> { pub is_const: bool, } -pub struct MethodDef<'a> { +pub(crate) struct MethodDef<'a> { /// name of the method pub name: Symbol, /// List of generics, e.g., `R: rand::Rng` @@ -248,7 +248,7 @@ pub struct MethodDef<'a> { /// How to handle fieldless enum variants. #[derive(PartialEq)] -pub enum FieldlessVariantsStrategy { +pub(crate) enum FieldlessVariantsStrategy { /// Combine fieldless variants into a single match arm. /// This assumes that relevant information has been handled /// by looking at the enum's discriminant. @@ -263,7 +263,7 @@ pub enum FieldlessVariantsStrategy { } /// All the data about the data structure/method being derived upon. -pub struct Substructure<'a> { +pub(crate) struct Substructure<'a> { /// ident of self pub type_ident: Ident, /// Verbatim access to any non-selflike arguments, i.e. arguments that @@ -273,7 +273,7 @@ pub struct Substructure<'a> { } /// Summary of the relevant parts of a struct/enum field. -pub struct FieldInfo { +pub(crate) struct FieldInfo { pub span: Span, /// None for tuple structs/normal enum variants, Some for normal /// structs/struct enum variants. @@ -287,13 +287,13 @@ pub struct FieldInfo { } #[derive(Copy, Clone)] -pub enum IsTuple { +pub(crate) enum IsTuple { No, Yes, } /// Fields for a static method -pub enum StaticFields { +pub(crate) enum StaticFields { /// Tuple and unit structs/enum variants like this. Unnamed(Vec, IsTuple), /// Normal structs/struct variants. @@ -301,7 +301,7 @@ pub enum StaticFields { } /// A summary of the possible sets of fields. -pub enum SubstructureFields<'a> { +pub(crate) enum SubstructureFields<'a> { /// A non-static method where `Self` is a struct. Struct(&'a ast::VariantData, Vec), @@ -310,15 +310,15 @@ pub enum SubstructureFields<'a> { /// variants has any fields). AllFieldlessEnum(&'a ast::EnumDef), - /// Matching variants of the enum: variant index, variant count, ast::Variant, + /// Matching variants of the enum: variant index, ast::Variant, /// fields: the field name is only non-`None` in the case of a struct /// variant. - EnumMatching(usize, usize, &'a ast::Variant, Vec), + EnumMatching(usize, &'a ast::Variant, Vec), - /// The tag of an enum. The first field is a `FieldInfo` for the tags, as + /// The discriminant of an enum. The first field is a `FieldInfo` for the discriminants, as /// if they were fields. The second field is the expression to combine the - /// tag expression with; it will be `None` if no match is necessary. - EnumTag(FieldInfo, Option>), + /// discriminant expression with; it will be `None` if no match is necessary. + EnumDiscr(FieldInfo, Option>), /// A static method where `Self` is a struct. StaticStruct(&'a ast::VariantData, StaticFields), @@ -329,10 +329,10 @@ pub enum SubstructureFields<'a> { /// Combine the values of all the fields together. The last argument is /// all the fields of all the structures. -pub type CombineSubstructureFunc<'a> = - Box, Span, &Substructure<'_>) -> BlockOrExpr + 'a>; +pub(crate) type CombineSubstructureFunc<'a> = + Box, Span, &Substructure<'_>) -> BlockOrExpr + 'a>; -pub fn combine_substructure( +pub(crate) fn combine_substructure( f: CombineSubstructureFunc<'_>, ) -> RefCell> { RefCell::new(f) @@ -349,7 +349,7 @@ struct TypeParameter { /// avoiding the insertion of any unnecessary blocks. /// /// The statements come before the expression. -pub struct BlockOrExpr(ThinVec, Option>); +pub(crate) struct BlockOrExpr(ThinVec, Option>); impl BlockOrExpr { pub fn new_stmts(stmts: ThinVec) -> BlockOrExpr { @@ -454,7 +454,7 @@ fn find_type_parameters( impl<'a> TraitDef<'a> { pub fn expand( self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, mitem: &ast::MetaItem, item: &'a Annotatable, push: &mut dyn FnMut(Annotatable), @@ -464,7 +464,7 @@ impl<'a> TraitDef<'a> { pub fn expand_ext( self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, mitem: &ast::MetaItem, item: &'a Annotatable, push: &mut dyn FnMut(Annotatable), @@ -577,7 +577,7 @@ impl<'a> TraitDef<'a> { /// therefore does not get bound by the derived trait. fn create_derived_impl( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, type_ident: Ident, generics: &Generics, field_tys: Vec>, @@ -802,7 +802,7 @@ impl<'a> TraitDef<'a> { fn expand_struct_def( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, struct_def: &'a VariantData, type_ident: Ident, generics: &Generics, @@ -856,7 +856,7 @@ impl<'a> TraitDef<'a> { fn expand_enum_def( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, enum_def: &'a EnumDef, type_ident: Ident, generics: &Generics, @@ -914,7 +914,7 @@ impl<'a> TraitDef<'a> { impl<'a> MethodDef<'a> { fn call_substructure_method( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, nonselflike_args: &[P], @@ -929,7 +929,7 @@ impl<'a> MethodDef<'a> { fn get_ret_ty( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, generics: &Generics, type_ident: Ident, @@ -950,7 +950,7 @@ impl<'a> MethodDef<'a> { // `&self`. fn extract_arg_details( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, generics: &Generics, @@ -986,7 +986,7 @@ impl<'a> MethodDef<'a> { fn create_method( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, generics: &Generics, @@ -1077,7 +1077,7 @@ impl<'a> MethodDef<'a> { /// ``` fn expand_struct_method_body<'b>( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'b>, struct_def: &'b VariantData, type_ident: Ident, @@ -1100,7 +1100,7 @@ impl<'a> MethodDef<'a> { fn expand_static_struct_method_body( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, struct_def: &VariantData, type_ident: Ident, @@ -1137,9 +1137,9 @@ impl<'a> MethodDef<'a> { /// impl ::core::cmp::PartialEq for A { /// #[inline] /// fn eq(&self, other: &A) -> bool { - /// let __self_tag = ::core::intrinsics::discriminant_value(self); - /// let __arg1_tag = ::core::intrinsics::discriminant_value(other); - /// __self_tag == __arg1_tag + /// let __self_discr = ::core::intrinsics::discriminant_value(self); + /// let __arg1_discr = ::core::intrinsics::discriminant_value(other); + /// __self_discr == __arg1_discr /// && match (self, other) { /// (A::A2(__self_0), A::A2(__arg1_0)) => *__self_0 == *__arg1_0, /// _ => true, @@ -1148,13 +1148,13 @@ impl<'a> MethodDef<'a> { /// } /// ``` /// - /// Creates a tag check combined with a match for a tuple of all + /// Creates a discriminant check combined with a match for a tuple of all /// `selflike_args`, with an arm for each variant with fields, possibly an /// arm for each fieldless variant (if `unify_fieldless_variants` is not /// `Unify`), and possibly a default arm. fn expand_enum_method_body<'b>( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'b>, enum_def: &'b EnumDef, type_ident: Ident, @@ -1169,7 +1169,7 @@ impl<'a> MethodDef<'a> { let span = trait_.span; let variants = &enum_def.variants; - // Traits that unify fieldless variants always use the tag(s). + // Traits that unify fieldless variants always use the discriminant(s). let unify_fieldless_variants = self.fieldless_variants_strategy == FieldlessVariantsStrategy::Unify; @@ -1199,25 +1199,25 @@ impl<'a> MethodDef<'a> { // // e.g. for `PartialEq::eq` builds two statements: // ``` - // let __self_tag = ::core::intrinsics::discriminant_value(self); - // let __arg1_tag = ::core::intrinsics::discriminant_value(other); + // let __self_discr = ::core::intrinsics::discriminant_value(self); + // let __arg1_discr = ::core::intrinsics::discriminant_value(other); // ``` - let get_tag_pieces = |cx: &ExtCtxt<'_>| { - let tag_idents: Vec<_> = prefixes + let get_discr_pieces = |cx: &ExtCtxt<'_>| { + let discr_idents: Vec<_> = prefixes .iter() - .map(|name| Ident::from_str_and_span(&format!("{name}_tag"), span)) + .map(|name| Ident::from_str_and_span(&format!("{name}_discr"), span)) .collect(); - let mut tag_exprs: Vec<_> = tag_idents + let mut discr_exprs: Vec<_> = discr_idents .iter() .map(|&ident| cx.expr_addr_of(span, cx.expr_ident(span, ident))) .collect(); - let self_expr = tag_exprs.remove(0); - let other_selflike_exprs = tag_exprs; - let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs }; + let self_expr = discr_exprs.remove(0); + let other_selflike_exprs = discr_exprs; + let discr_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs }; - let tag_let_stmts: ThinVec<_> = iter::zip(&tag_idents, &selflike_args) + let discr_let_stmts: ThinVec<_> = iter::zip(&discr_idents, &selflike_args) .map(|(&ident, selflike_arg)| { let variant_value = deriving::call_intrinsic( cx, @@ -1229,7 +1229,7 @@ impl<'a> MethodDef<'a> { }) .collect(); - (tag_field, tag_let_stmts) + (discr_field, discr_let_stmts) }; // There are some special cases involving fieldless enums where no @@ -1239,19 +1239,19 @@ impl<'a> MethodDef<'a> { if variants.len() > 1 { match self.fieldless_variants_strategy { FieldlessVariantsStrategy::Unify => { - // If the type is fieldless and the trait uses the tag and + // If the type is fieldless and the trait uses the discriminant and // there are multiple variants, we need just an operation on - // the tag(s). - let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx); - let mut tag_check = self.call_substructure_method( + // the discriminant(s). + let (discr_field, mut discr_let_stmts) = get_discr_pieces(cx); + let mut discr_check = self.call_substructure_method( cx, trait_, type_ident, nonselflike_args, - &EnumTag(tag_field, None), + &EnumDiscr(discr_field, None), ); - tag_let_stmts.append(&mut tag_check.0); - return BlockOrExpr(tag_let_stmts, tag_check.1); + discr_let_stmts.append(&mut discr_check.0); + return BlockOrExpr(discr_let_stmts, discr_check.1); } FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless => { return self.call_substructure_method( @@ -1266,13 +1266,13 @@ impl<'a> MethodDef<'a> { } } else if variants.len() == 1 { // If there is a single variant, we don't need an operation on - // the tag(s). Just use the most degenerate result. + // the discriminant(s). Just use the most degenerate result. return self.call_substructure_method( cx, trait_, type_ident, nonselflike_args, - &EnumMatching(0, 1, &variants[0], Vec::new()), + &EnumMatching(0, &variants[0], Vec::new()), ); } } @@ -1318,7 +1318,7 @@ impl<'a> MethodDef<'a> { // expressions for referencing every field of every // Self arg, assuming all are instances of VariantK. // Build up code associated with such a case. - let substructure = EnumMatching(index, variants.len(), variant, fields); + let substructure = EnumMatching(index, variant, fields); let arm_expr = self .call_substructure_method( cx, @@ -1346,7 +1346,7 @@ impl<'a> MethodDef<'a> { trait_, type_ident, nonselflike_args, - &EnumMatching(0, variants.len(), v, Vec::new()), + &EnumMatching(0, v, Vec::new()), ) .into_expr(cx, span), ) @@ -1380,22 +1380,22 @@ impl<'a> MethodDef<'a> { cx.expr_match(span, match_arg, match_arms) }; - // If the trait uses the tag and there are multiple variants, we need - // to add a tag check operation before the match. Otherwise, the match + // If the trait uses the discriminant and there are multiple variants, we need + // to add a discriminant check operation before the match. Otherwise, the match // is enough. if unify_fieldless_variants && variants.len() > 1 { - let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx); + let (discr_field, mut discr_let_stmts) = get_discr_pieces(cx); - // Combine a tag check with the match. - let mut tag_check_plus_match = self.call_substructure_method( + // Combine a discriminant check with the match. + let mut discr_check_plus_match = self.call_substructure_method( cx, trait_, type_ident, nonselflike_args, - &EnumTag(tag_field, Some(get_match_expr(selflike_args))), + &EnumDiscr(discr_field, Some(get_match_expr(selflike_args))), ); - tag_let_stmts.append(&mut tag_check_plus_match.0); - BlockOrExpr(tag_let_stmts, tag_check_plus_match.1) + discr_let_stmts.append(&mut discr_check_plus_match.0); + BlockOrExpr(discr_let_stmts, discr_check_plus_match.1) } else { BlockOrExpr(ThinVec::new(), Some(get_match_expr(selflike_args))) } @@ -1403,7 +1403,7 @@ impl<'a> MethodDef<'a> { fn expand_static_enum_method_body( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>, enum_def: &EnumDef, type_ident: Ident, @@ -1430,7 +1430,7 @@ impl<'a> MethodDef<'a> { // general helper methods. impl<'a> TraitDef<'a> { - fn summarise_struct(&self, cx: &mut ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { + fn summarise_struct(&self, cx: &ExtCtxt<'_>, struct_def: &VariantData) -> StaticFields { let mut named_idents = Vec::new(); let mut just_spans = Vec::new(); for field in struct_def.fields() { @@ -1460,7 +1460,7 @@ impl<'a> TraitDef<'a> { fn create_struct_patterns( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, struct_path: ast::Path, struct_def: &'a VariantData, prefixes: &[String], @@ -1479,11 +1479,7 @@ impl<'a> TraitDef<'a> { struct_field.ident, cx.pat( path.span, - PatKind::Ident( - BindingAnnotation(by_ref, Mutability::Not), - path, - None, - ), + PatKind::Ident(BindingMode(by_ref, Mutability::Not), path, None), ), ) }); @@ -1553,7 +1549,7 @@ impl<'a> TraitDef<'a> { fn create_struct_pattern_fields( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, struct_def: &'a VariantData, prefixes: &[String], ) -> Vec { @@ -1570,7 +1566,7 @@ impl<'a> TraitDef<'a> { fn create_struct_field_access_fields( &self, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, selflike_args: &[P], struct_def: &'a VariantData, is_packed: bool, @@ -1651,7 +1647,7 @@ impl<'a> TraitDef<'a> { /// The function passed to `cs_fold` is called repeatedly with a value of this /// type. It describes one part of the code generation. The result is always an /// expression. -pub enum CsFold<'a> { +pub(crate) enum CsFold<'a> { /// The basic case: a field expression for one or more selflike args. E.g. /// for `PartialEq::eq` this is something like `self.x == other.x`. Single(&'a FieldInfo), @@ -1666,15 +1662,15 @@ pub enum CsFold<'a> { /// Folds over fields, combining the expressions for each field in a sequence. /// Statics may not be folded over. -pub fn cs_fold( +pub(crate) fn cs_fold( use_foldl: bool, - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, trait_span: Span, substructure: &Substructure<'_>, mut f: F, ) -> P where - F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P, + F: FnMut(&ExtCtxt<'_>, CsFold<'_>) -> P, { match substructure.fields { EnumMatching(.., all_fields) | Struct(_, all_fields) => { @@ -1701,16 +1697,16 @@ where rest.iter().rfold(base_expr, op) } } - EnumTag(tag_field, match_expr) => { - let tag_check_expr = f(cx, CsFold::Single(tag_field)); + EnumDiscr(discr_field, match_expr) => { + let discr_check_expr = f(cx, CsFold::Single(discr_field)); if let Some(match_expr) = match_expr { if use_foldl { - f(cx, CsFold::Combine(trait_span, tag_check_expr, match_expr.clone())) + f(cx, CsFold::Combine(trait_span, discr_check_expr, match_expr.clone())) } else { - f(cx, CsFold::Combine(trait_span, match_expr.clone(), tag_check_expr)) + f(cx, CsFold::Combine(trait_span, match_expr.clone(), discr_check_expr)) } } else { - tag_check_expr + discr_check_expr } } StaticEnum(..) | StaticStruct(..) => { diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index 603cefdd38629..f01d586033e09 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -1,28 +1,27 @@ //! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use //! when specifying impls to be derived. -pub use Ty::*; +pub(crate) use Ty::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Expr, GenericArg, GenericParamKind, Generics, SelfKind}; use rustc_expand::base::ExtCtxt; use rustc_span::source_map::respan; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::Span; -use rustc_span::DUMMY_SP; +use rustc_span::{Span, DUMMY_SP}; use thin_vec::ThinVec; /// A path, e.g., `::std::option::Option::` (global). Has support /// for type parameters. #[derive(Clone)] -pub struct Path { +pub(crate) struct Path { path: Vec, params: Vec>, kind: PathKind, } #[derive(Clone)] -pub enum PathKind { +pub(crate) enum PathKind { Local, Global, Std, @@ -73,7 +72,7 @@ impl Path { /// A type. Supports pointers, Self, and literals. #[derive(Clone)] -pub enum Ty { +pub(crate) enum Ty { Self_, /// A reference. Ref(Box, ast::Mutability), @@ -84,7 +83,7 @@ pub enum Ty { Unit, } -pub fn self_ref() -> Ty { +pub(crate) fn self_ref() -> Ty { Ref(Box::new(Self_), ast::Mutability::Not) } @@ -164,7 +163,7 @@ fn mk_ty_param( /// Bounds on type parameters. #[derive(Clone)] -pub struct Bounds { +pub(crate) struct Bounds { pub bounds: Vec<(Symbol, Vec)>, } @@ -197,7 +196,7 @@ impl Bounds { } } -pub fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (P, ast::ExplicitSelf) { +pub(crate) fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (P, ast::ExplicitSelf) { // This constructs a fresh `self` path. let self_path = cx.expr_self(span); let self_ty = respan(span, SelfKind::Region(None, ast::Mutability::Not)); diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index dd6149cf61496..dcd9281986511 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -7,8 +7,8 @@ use rustc_span::symbol::sym; use rustc_span::Span; use thin_vec::thin_vec; -pub fn expand_deriving_hash( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_deriving_hash( + cx: &ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, @@ -46,11 +46,7 @@ pub fn expand_deriving_hash( hash_trait_def.expand(cx, mitem, item, push); } -fn hash_substructure( - cx: &mut ExtCtxt<'_>, - trait_span: Span, - substr: &Substructure<'_>, -) -> BlockOrExpr { +fn hash_substructure(cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let [state_expr] = substr.nonselflike_args else { cx.dcx().span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"); }; @@ -70,9 +66,9 @@ fn hash_substructure( fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect(); (stmts, None) } - EnumTag(tag_field, match_expr) => { - assert!(tag_field.other_selflike_exprs.is_empty()); - let stmts = thin_vec![call_hash(tag_field.span, tag_field.self_expr.clone())]; + EnumDiscr(discr_field, match_expr) => { + assert!(discr_field.other_selflike_exprs.is_empty()); + let stmts = thin_vec![call_hash(discr_field.span, discr_field.self_expr.clone())]; (stmts, match_expr.clone()) } _ => cx.dcx().span_bug(trait_span, "impossible substructure in `derive(Hash)`"), diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 8a3375cba9dc1..d6e2d1d4d07f6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -20,27 +20,27 @@ macro path_std($($x:tt)*) { generic::ty::Path::new( pathvec_std!( $($x)* ) ) } -pub mod bounds; -pub mod clone; -pub mod debug; -pub mod decodable; -pub mod default; -pub mod encodable; -pub mod hash; +pub(crate) mod bounds; +pub(crate) mod clone; +pub(crate) mod debug; +pub(crate) mod decodable; +pub(crate) mod default; +pub(crate) mod encodable; +pub(crate) mod hash; #[path = "cmp/eq.rs"] -pub mod eq; +pub(crate) mod eq; #[path = "cmp/ord.rs"] -pub mod ord; +pub(crate) mod ord; #[path = "cmp/partial_eq.rs"] -pub mod partial_eq; +pub(crate) mod partial_eq; #[path = "cmp/partial_ord.rs"] -pub mod partial_ord; +pub(crate) mod partial_ord; -pub mod generic; +pub(crate) mod generic; pub(crate) type BuiltinDeriveFn = - fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool); + fn(&ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool); pub(crate) struct BuiltinDerive(pub(crate) BuiltinDeriveFn); @@ -117,7 +117,7 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P { } fn assert_ty_bounds( - cx: &mut ExtCtxt<'_>, + cx: &ExtCtxt<'_>, stmts: &mut ThinVec, ty: P, span: Span, diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs index fa22e91164232..cc385bade4703 100644 --- a/compiler/rustc_builtin_macros/src/edition_panic.rs +++ b/compiler/rustc_builtin_macros/src/edition_panic.rs @@ -16,7 +16,7 @@ use rustc_span::Span; /// /// `$crate` will refer to either the `std` or `core` crate depending on which /// one we're expanding from. -pub fn expand_panic<'cx>( +pub(crate) fn expand_panic<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -29,7 +29,7 @@ pub fn expand_panic<'cx>( /// - `$crate::panic::unreachable_2015!(...)` or /// - `$crate::panic::unreachable_2021!(...)` /// depending on the edition. -pub fn expand_unreachable<'cx>( +pub(crate) fn expand_unreachable<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -40,7 +40,7 @@ pub fn expand_unreachable<'cx>( fn expand<'cx>( mac: rustc_span::Symbol, - cx: &'cx mut ExtCtxt<'_>, + cx: &'cx ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { @@ -69,7 +69,7 @@ fn expand<'cx>( )) } -pub fn use_panic_2021(mut span: Span) -> bool { +pub(crate) fn use_panic_2021(mut span: Span) -> bool { // To determine the edition, we check the first span up the expansion // stack that does not have #[allow_internal_unstable(edition_panic)]. // (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.) diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index bce710e5cab12..b03e14cf2630d 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -3,29 +3,29 @@ // interface. // +use crate::errors; +use crate::util::{expr_to_string, get_exprs_from_tts, get_single_str_from_tts}; use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AstDeref, ExprKind, GenericArg, Mutability}; -use rustc_expand::base::{expr_to_string, get_exprs_from_tts, get_single_str_from_tts}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::env; +use std::env::VarError; use thin_vec::thin_vec; -use crate::errors; - -fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Option { +fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result { let var = var.as_str(); if let Some(value) = cx.sess.opts.logical_env.get(var) { - return Some(Symbol::intern(value)); + return Ok(Symbol::intern(value)); } // If the environment variable was not defined with the `--env-set` option, we try to retrieve it // from rustc's environment. - env::var(var).ok().as_deref().map(Symbol::intern) + Ok(Symbol::intern(&env::var(var)?)) } -pub fn expand_option_env<'cx>( +pub(crate) fn expand_option_env<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -39,7 +39,7 @@ pub fn expand_option_env<'cx>( }; let sp = cx.with_def_site_ctxt(sp); - let value = lookup_env(cx, var); + let value = lookup_env(cx, var).ok(); cx.sess.psess.env_depinfo.borrow_mut().insert((var, value)); let e = match value { None => { @@ -65,7 +65,7 @@ pub fn expand_option_env<'cx>( ExpandResult::Ready(MacEager::expr(e)) } -pub fn expand_env<'cx>( +pub(crate) fn expand_env<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -108,9 +108,9 @@ pub fn expand_env<'cx>( let span = cx.with_def_site_ctxt(sp); let value = lookup_env(cx, var); - cx.sess.psess.env_depinfo.borrow_mut().insert((var, value)); + cx.sess.psess.env_depinfo.borrow_mut().insert((var, value.as_ref().ok().copied())); let e = match value { - None => { + Err(err) => { let ExprKind::Lit(token::Lit { kind: LitKind::Str | LitKind::StrRaw(..), symbol, .. }) = &var_expr.kind @@ -118,25 +118,33 @@ pub fn expand_env<'cx>( unreachable!("`expr_to_string` ensures this is a string lit") }; - let guar = if let Some(msg_from_user) = custom_msg { - cx.dcx().emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) - } else if is_cargo_env_var(var.as_str()) { - cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { - span, - var: *symbol, - var_expr: var_expr.ast_deref(), - }) - } else { - cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { - span, - var: *symbol, - var_expr: var_expr.ast_deref(), - }) + let guar = match err { + VarError::NotPresent => { + if let Some(msg_from_user) = custom_msg { + cx.dcx() + .emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }) + } else if is_cargo_env_var(var.as_str()) { + cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }) + } else { + cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }) + } + } + VarError::NotUnicode(_) => { + cx.dcx().emit_err(errors::EnvNotUnicode { span, var: *symbol }) + } }; return ExpandResult::Ready(DummyResult::any(sp, guar)); } - Some(value) => cx.expr_str(span, value), + Ok(value) => cx.expr_str(span, value), }; ExpandResult::Ready(MacEager::expr(e)) } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 377aff8fb6c00..d157703723beb 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -458,6 +458,14 @@ pub(crate) enum EnvNotDefined<'a> { }, } +#[derive(Diagnostic)] +#[diag(builtin_macros_env_not_unicode)] +pub(crate) struct EnvNotUnicode { + #[primary_span] + pub(crate) span: Span, + pub(crate) var: Symbol, +} + #[derive(Diagnostic)] #[diag(builtin_macros_format_requires_string)] pub(crate) struct FormatRequiresString { @@ -571,7 +579,7 @@ pub(crate) struct FormatUnknownTrait<'a> { style = "tool-only", applicability = "maybe-incorrect" )] -pub struct FormatUnknownTraitSugg { +pub(crate) struct FormatUnknownTraitSugg { #[primary_span] pub span: Span, pub fmt: &'static str, @@ -593,7 +601,7 @@ impl Subdiagnostic for FormatUnusedArg { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - f: F, + f: &F, ) { diag.arg("named", self.named); let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into()); @@ -834,3 +842,26 @@ pub(crate) struct ExpectedRegisterClassOrExplicitRegister { #[primary_span] pub(crate) span: Span, } + +#[derive(Diagnostic)] +#[diag(builtin_macros_expected_comma_in_list)] +pub(crate) struct ExpectedCommaInList { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_only_one_argument)] +pub(crate) struct OnlyOneArgument<'a> { + #[primary_span] + pub span: Span, + pub name: &'a str, +} + +#[derive(Diagnostic)] +#[diag(builtin_macros_takes_no_arguments)] +pub(crate) struct TakesNoArguments<'a> { + #[primary_span] + pub span: Span, + pub name: &'a str, +} diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 6f031f270cae5..2c717661a1c43 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1,3 +1,5 @@ +use crate::errors; +use crate::util::expr_to_spanned_string; use parse::Position::ArgumentNamed; use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; @@ -10,14 +12,13 @@ use rustc_ast::{ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans}; use rustc_expand::base::*; +use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; +use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId}; use rustc_parse::parser::Recovered; use rustc_parse_format as parse; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{BytePos, ErrorGuaranteed, InnerSpan, Span}; -use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; -use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId}; - // The format_args!() macro is expanded in three steps: // 1. First, `parse_args` will parse the `(literal, arg, arg, name=arg, name=arg)` syntax, // but doesn't parse the template (the literal) itself. @@ -38,8 +39,6 @@ enum PositionUsedAs { } use PositionUsedAs::*; -use crate::errors; - #[derive(Debug)] struct MacroInput { fmtstr: P, @@ -65,7 +64,7 @@ struct MacroInput { /// ```text /// Ok((fmtstr, parsed arguments)) /// ``` -fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> { +fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> { let mut args = FormatArguments::new(); let mut p = ecx.new_parser_from_tts(tts); @@ -604,7 +603,7 @@ fn invalid_placeholder_type_error( } fn report_missing_placeholders( - ecx: &mut ExtCtxt<'_>, + ecx: &ExtCtxt<'_>, unused: Vec<(Span, bool)>, used: &[bool], args: &FormatArguments, @@ -734,7 +733,7 @@ fn report_missing_placeholders( /// This function detects and reports unused format!() arguments that are /// redundant due to implicit captures (e.g. `format!("{x}", x)`). fn report_redundant_format_arguments<'a>( - ecx: &mut ExtCtxt<'a>, + ecx: &ExtCtxt<'a>, args: &FormatArguments, used: &[bool], placeholders: Vec<(Span, &str)>, @@ -806,7 +805,7 @@ fn report_redundant_format_arguments<'a>( /// there are named arguments or numbered positional arguments in the /// format string. fn report_invalid_references( - ecx: &mut ExtCtxt<'_>, + ecx: &ExtCtxt<'_>, invalid_refs: &[(usize, Option, PositionUsedAs, FormatArgPositionKind)], template: &[FormatArgsPiece], fmt_span: Span, @@ -1001,7 +1000,7 @@ fn expand_format_args_impl<'cx>( }) } -pub fn expand_format_args<'cx>( +pub(crate) fn expand_format_args<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -1009,7 +1008,7 @@ pub fn expand_format_args<'cx>( expand_format_args_impl(ecx, sp, tts, false) } -pub fn expand_format_args_nl<'cx>( +pub(crate) fn expand_format_args_nl<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 099defd511b5b..a1630ad1379e4 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -12,7 +12,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use thin_vec::{thin_vec, ThinVec}; -pub fn expand( +pub(crate) fn expand( ecx: &mut ExtCtxt<'_>, _span: Span, meta_item: &ast::MetaItem, diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index f344dbcd10c06..744c7f9d09006 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -19,11 +19,7 @@ extern crate proc_macro; -#[macro_use] -extern crate tracing; - use crate::deriving::*; - use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind}; use rustc_expand::proc_macro::BangProcMacro; use rustc_span::symbol::sym; @@ -46,17 +42,17 @@ mod format; mod format_foreign; mod global_allocator; mod log_syntax; +mod pattern_type; mod source_util; mod test; mod trace_macros; -mod type_ascribe; -mod util; pub mod asm; pub mod cmdline_attrs; pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; +pub mod util; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -96,10 +92,10 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, option_env: env::expand_option_env, + pattern_type: pattern_type::expand, std_panic: edition_panic::expand_panic, stringify: source_util::expand_stringify, trace_macros: trace_macros::expand_trace_macros, - type_ascribe: type_ascribe::expand_type_ascribe, unreachable: edition_panic::expand_unreachable, // tidy-alphabetical-end } @@ -109,8 +105,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, cfg_eval: cfg_eval::expand, - derive: derive::Expander(false), - derive_const: derive::Expander(true), + derive: derive::Expander { is_const: false }, + derive_const: derive::Expander { is_const: true }, global_allocator: global_allocator::expand, test: test::expand_test, test_case: test::expand_test_case, diff --git a/compiler/rustc_builtin_macros/src/log_syntax.rs b/compiler/rustc_builtin_macros/src/log_syntax.rs index 288a475ac241c..205f21ae7c9d3 100644 --- a/compiler/rustc_builtin_macros/src/log_syntax.rs +++ b/compiler/rustc_builtin_macros/src/log_syntax.rs @@ -2,7 +2,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; -pub fn expand_log_syntax<'cx>( +pub(crate) fn expand_log_syntax<'cx>( _cx: &'cx mut ExtCtxt<'_>, sp: rustc_span::Span, tts: TokenStream, diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs new file mode 100644 index 0000000000000..31f5656df135e --- /dev/null +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -0,0 +1,29 @@ +use rustc_ast::{ast, ptr::P, tokenstream::TokenStream, Pat, Ty}; +use rustc_errors::PResult; +use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; +use rustc_span::{sym, Span}; + +pub(crate) fn expand<'cx>( + cx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> MacroExpanderResult<'cx> { + let (ty, pat) = match parse_pat_ty(cx, tts) { + Ok(parsed) => parsed, + Err(err) => { + return ExpandResult::Ready(DummyResult::any(sp, err.emit())); + } + }; + + ExpandResult::Ready(base::MacEager::ty(cx.ty(sp, ast::TyKind::Pat(ty, pat)))) +} + +fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P, P)> { + let mut parser = cx.new_parser_from_tts(stream); + + let ty = parser.parse_ty()?; + parser.eat_keyword(sym::is); + let pat = parser.parse_pat_no_top_alt(None, None)?; + + Ok((ty, pat)) +} diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index dbb86df6811da..47b2ee975ca89 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -1,19 +1,24 @@ +use crate::util::{ + check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, +}; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_expand::base::{check_zero_tts, get_single_str_from_tts, parse_expr, resolve_path}; -use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt}; -use rustc_expand::base::{MacEager, MacResult, MacroExpanderResult}; +use rustc_data_structures::sync::Lrc; +use rustc_expand::base::{ + resolve_path, DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, +}; use rustc_expand::module::DirOwnership; use rustc_parse::new_parser_from_file; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; +use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; use rustc_span::{Pos, Span}; - use smallvec::SmallVec; +use std::path::{Path, PathBuf}; use std::rc::Rc; // These macros all relate to the file system; they either return @@ -21,7 +26,7 @@ use std::rc::Rc; // a given file into the current one. /// line!(): expands to the current line number -pub fn expand_line( +pub(crate) fn expand_line( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -36,7 +41,7 @@ pub fn expand_line( } /* column!(): expands to the current column number */ -pub fn expand_column( +pub(crate) fn expand_column( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -53,7 +58,7 @@ pub fn expand_column( /// file!(): expands to the current filename */ /// The source_file (`loc.file`) contains a bunch more information we could spit /// out if we wanted. -pub fn expand_file( +pub(crate) fn expand_file( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -73,7 +78,7 @@ pub fn expand_file( ))) } -pub fn expand_stringify( +pub(crate) fn expand_stringify( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -83,7 +88,7 @@ pub fn expand_stringify( ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&s)))) } -pub fn expand_mod( +pub(crate) fn expand_mod( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -99,7 +104,7 @@ pub fn expand_mod( /// include! : parse the given file as an expr /// This is generally a bad idea because it's going to behave /// unhygienically. -pub fn expand_include<'cx>( +pub(crate) fn expand_include<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -176,72 +181,162 @@ pub fn expand_include<'cx>( } /// `include_str!`: read the given file, insert it as a literal string expr -pub fn expand_include_str( +pub(crate) fn expand_include_str( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'static> { let sp = cx.with_def_site_ctxt(sp); - let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "include_str!") else { + let ExpandResult::Ready(mac) = get_single_str_spanned_from_tts(cx, sp, tts, "include_str!") + else { return ExpandResult::Retry(()); }; - let file = match mac { - Ok(file) => file, + let (path, path_span) = match mac { + Ok(res) => res, Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), }; - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, - Err(err) => { - let guar = err.emit(); - return ExpandResult::Ready(DummyResult::any(sp, guar)); - } - }; - ExpandResult::Ready(match cx.source_map().load_binary_file(&file) { - Ok(bytes) => match std::str::from_utf8(&bytes) { + ExpandResult::Ready(match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { + 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!("{} wasn't a utf-8 file", file.display())); + let guar = cx.dcx().span_err(sp, format!("`{path}` wasn't a utf-8 file")); DummyResult::any(sp, guar) } }, - Err(e) => { - let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - DummyResult::any(sp, guar) - } + Err(dummy) => dummy, }) } -pub fn expand_include_bytes( +pub(crate) fn expand_include_bytes( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'static> { let sp = cx.with_def_site_ctxt(sp); - let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "include_bytes!") else { + let ExpandResult::Ready(mac) = get_single_str_spanned_from_tts(cx, sp, tts, "include_bytes!") + else { return ExpandResult::Retry(()); }; - let file = match mac { - Ok(file) => file, + let (path, path_span) = match mac { + Ok(res) => res, Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), }; - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, + ExpandResult::Ready(match load_binary_file(cx, path.as_str().as_ref(), sp, path_span) { + 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) + } + Err(dummy) => dummy, + }) +} + +fn load_binary_file( + cx: &ExtCtxt<'_>, + original_path: &Path, + macro_span: Span, + path_span: Span, +) -> Result<(Lrc<[u8]>, Span), Box> { + let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) { + Ok(path) => path, Err(err) => { let guar = err.emit(); - return ExpandResult::Ready(DummyResult::any(sp, guar)); + return Err(DummyResult::any(macro_span, guar)); } }; - ExpandResult::Ready(match cx.source_map().load_binary_file(&file) { - Ok(bytes) => { - let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes)); - MacEager::expr(expr) + match cx.source_map().load_binary_file(&resolved_path) { + Ok(data) => Ok(data), + Err(io_err) => { + let mut err = cx.dcx().struct_span_err( + macro_span, + format!("couldn't read `{}`: {io_err}", resolved_path.display()), + ); + + if original_path.is_relative() { + let source_map = cx.sess.source_map(); + let new_path = source_map + .span_to_filename(macro_span.source_callsite()) + .into_local_path() + .and_then(|src| find_path_suggestion(source_map, src.parent()?, original_path)) + .and_then(|path| path.into_os_string().into_string().ok()); + + if let Some(new_path) = new_path { + err.span_suggestion( + path_span, + "there is a file with the same name in a different directory", + format!("\"{}\"", new_path.replace('\\', "/").escape_debug()), + rustc_lint_defs::Applicability::MachineApplicable, + ); + } + } + let guar = err.emit(); + Err(DummyResult::any(macro_span, guar)) + } + } +} + +fn find_path_suggestion( + source_map: &SourceMap, + base_dir: &Path, + wanted_path: &Path, +) -> Option { + // Fix paths that assume they're relative to cargo manifest dir + let mut base_c = base_dir.components(); + let mut wanted_c = wanted_path.components(); + let mut without_base = None; + while let Some(wanted_next) = wanted_c.next() { + if wanted_c.as_path().file_name().is_none() { + break; } - Err(e) => { - let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - DummyResult::any(sp, guar) + // base_dir may be absolute + while let Some(base_next) = base_c.next() { + if base_next == wanted_next { + without_base = Some(wanted_c.as_path()); + break; + } } + } + let root_absolute = without_base.into_iter().map(PathBuf::from); + + let base_dir_components = base_dir.components().count(); + // Avoid going all the way to the root dir + let max_parent_components = if base_dir.is_relative() { + base_dir_components + 1 + } else { + base_dir_components.saturating_sub(1) + }; + + // Try with additional leading ../ + let mut prefix = PathBuf::new(); + let add = std::iter::from_fn(|| { + prefix.push(".."); + Some(prefix.join(wanted_path)) }) + .take(max_parent_components.min(3)); + + // Try without leading directories + let mut trimmed_path = wanted_path; + let remove = std::iter::from_fn(|| { + let mut components = trimmed_path.components(); + let removed = components.next()?; + trimmed_path = components.as_path(); + let _ = trimmed_path.file_name()?; // ensure there is a file name left + Some([ + Some(trimmed_path.to_path_buf()), + (removed != std::path::Component::ParentDir) + .then(|| Path::new("..").join(trimmed_path)), + ]) + }) + .flatten() + .flatten() + .take(4); + + root_absolute + .chain(add) + .chain(remove) + .find(|new_path| source_map.file_exists(&base_dir.join(&new_path))) } diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 81ac78dd58fa6..1e4bf4611cfb2 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -12,6 +12,7 @@ use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Span}; use std::assert_matches::assert_matches; use std::iter; use thin_vec::{thin_vec, ThinVec}; +use tracing::debug; /// #[test_case] is used by custom test authors to mark tests /// When building for test, it needs to make the item public and gensym the name @@ -20,7 +21,7 @@ use thin_vec::{thin_vec, ThinVec}; /// /// We mark item with an inert attribute "rustc_test_marker" which the test generation /// logic will pick up on. -pub fn expand_test_case( +pub(crate) fn expand_test_case( ecx: &mut ExtCtxt<'_>, attr_sp: Span, meta_item: &ast::MetaItem, @@ -73,7 +74,7 @@ pub fn expand_test_case( vec![ret] } -pub fn expand_test( +pub(crate) fn expand_test( cx: &mut ExtCtxt<'_>, attr_sp: Span, meta_item: &ast::MetaItem, @@ -84,7 +85,7 @@ pub fn expand_test( expand_test_or_bench(cx, attr_sp, item, false) } -pub fn expand_bench( +pub(crate) fn expand_bench( cx: &mut ExtCtxt<'_>, attr_sp: Span, meta_item: &ast::MetaItem, @@ -95,8 +96,8 @@ pub fn expand_bench( expand_test_or_bench(cx, attr_sp, item, true) } -pub fn expand_test_or_bench( - cx: &mut ExtCtxt<'_>, +pub(crate) fn expand_test_or_bench( + cx: &ExtCtxt<'_>, attr_sp: Span, item: Annotatable, is_bench: bool, diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs index 696d99004ba01..4833ec32f76e1 100644 --- a/compiler/rustc_builtin_macros/src/trace_macros.rs +++ b/compiler/rustc_builtin_macros/src/trace_macros.rs @@ -4,7 +4,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult use rustc_span::symbol::kw; use rustc_span::Span; -pub fn expand_trace_macros( +pub(crate) fn expand_trace_macros( cx: &mut ExtCtxt<'_>, sp: Span, tt: TokenStream, diff --git a/compiler/rustc_builtin_macros/src/type_ascribe.rs b/compiler/rustc_builtin_macros/src/type_ascribe.rs deleted file mode 100644 index f3e66ffc759db..0000000000000 --- a/compiler/rustc_builtin_macros/src/type_ascribe.rs +++ /dev/null @@ -1,35 +0,0 @@ -use rustc_ast::ptr::P; -use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{token, Expr, ExprKind, Ty}; -use rustc_errors::PResult; -use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; -use rustc_span::Span; - -pub fn expand_type_ascribe( - cx: &mut ExtCtxt<'_>, - span: Span, - tts: TokenStream, -) -> MacroExpanderResult<'static> { - let (expr, ty) = match parse_ascribe(cx, tts) { - Ok(parsed) => parsed, - Err(err) => { - let guar = err.emit(); - return ExpandResult::Ready(DummyResult::any(span, guar)); - } - }; - - let asc_expr = cx.expr(span, ExprKind::Type(expr, ty)); - - ExpandResult::Ready(MacEager::expr(asc_expr)) -} - -fn parse_ascribe<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P, P)> { - let mut parser = cx.new_parser_from_tts(stream); - - let expr = parser.parse_expr()?; - parser.expect(&token::Comma)?; - - let ty = parser.parse_ty()?; - - Ok((expr, ty)) -} diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index ad6b09ba57458..8dc7bc14ec3e6 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -1,11 +1,16 @@ -use rustc_ast::{attr, AttrStyle, Attribute, MetaItem}; -use rustc_expand::base::{Annotatable, ExtCtxt}; +use crate::errors; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{self as ast, attr, ptr::P, token, AttrStyle, Attribute, MetaItem}; +use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; +use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt}; +use rustc_expand::expand::AstFragment; use rustc_feature::AttributeTemplate; use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; -use rustc_parse::validate_attr; -use rustc_span::Symbol; +use rustc_parse::{parser, validate_attr}; +use rustc_session::errors::report_lit_error; +use rustc_span::{BytePos, Span, Symbol}; -pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) { +pub(crate) fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) { // All the built-in macro attributes are "words" at the moment. let template = AttributeTemplate { word: true, ..Default::default() }; validate_attr::check_builtin_meta_item( @@ -19,7 +24,7 @@ pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, na /// Emit a warning if the item is annotated with the given attribute. This is used to diagnose when /// an attribute may have been mistakenly duplicated. -pub fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, name: Symbol) { +pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, name: Symbol) { let attrs: Option<&[Attribute]> = match item { Annotatable::Item(item) => Some(&item.attrs), Annotatable::TraitItem(item) => Some(&item.attrs), @@ -46,3 +51,178 @@ pub fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, name: } } } + +/// `Ok` represents successfully retrieving the string literal at the correct +/// position, e.g., `println("abc")`. +type ExprToSpannedStringResult<'a> = Result<(Symbol, ast::StrStyle, Span), UnexpectedExprKind<'a>>; + +/// - `Ok` is returned when the conversion to a string literal is unsuccessful, +/// but another type of expression is obtained instead. +/// - `Err` is returned when the conversion process fails. +type UnexpectedExprKind<'a> = Result<(Diag<'a>, bool /* has_suggestions */), ErrorGuaranteed>; + +/// Extracts a string literal from the macro expanded version of `expr`, +/// returning a diagnostic error of `err_msg` if `expr` is not a string literal. +/// The returned bool indicates whether an applicable suggestion has already been +/// added to the diagnostic to avoid emitting multiple suggestions. `Err(Err(ErrorGuaranteed))` +/// indicates that an ast error was encountered. +// FIXME(Nilstrieb) Make this function setup translatable +#[allow(rustc::untranslatable_diagnostic)] +pub(crate) fn expr_to_spanned_string<'a>( + cx: &'a mut ExtCtxt<'_>, + expr: P, + err_msg: &'static str, +) -> ExpandResult, ()> { + if !cx.force_mode + && let ast::ExprKind::MacCall(m) = &expr.kind + && cx.resolver.macro_accessible(cx.current_expansion.id, &m.path).is_err() + { + return ExpandResult::Retry(()); + } + + // Perform eager expansion on the expression. + // We want to be able to handle e.g., `concat!("foo", "bar")`. + let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); + + ExpandResult::Ready(Err(match expr.kind { + ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { + Ok(ast::LitKind::Str(s, style)) => { + return ExpandResult::Ready(Ok((s, style, expr.span))); + } + Ok(ast::LitKind::ByteStr(..)) => { + let mut err = cx.dcx().struct_span_err(expr.span, err_msg); + let span = expr.span.shrink_to_lo(); + err.span_suggestion( + span.with_hi(span.lo() + BytePos(1)), + "consider removing the leading `b`", + "", + Applicability::MaybeIncorrect, + ); + Ok((err, true)) + } + Ok(ast::LitKind::Err(guar)) => Err(guar), + Err(err) => Err(report_lit_error(&cx.sess.psess, err, token_lit, expr.span)), + _ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)), + }, + ast::ExprKind::Err(guar) => Err(guar), + ast::ExprKind::Dummy => { + cx.dcx().span_bug(expr.span, "tried to get a string literal from `ExprKind::Dummy`") + } + _ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)), + })) +} + +/// Extracts a string literal from the macro expanded version of `expr`, +/// emitting `err_msg` if `expr` is not a string literal. This does not stop +/// compilation on error, merely emits a non-fatal error and returns `Err`. +pub(crate) fn expr_to_string( + cx: &mut ExtCtxt<'_>, + expr: P, + err_msg: &'static str, +) -> ExpandResult, ()> { + expr_to_spanned_string(cx, expr, err_msg).map(|res| { + res.map_err(|err| match err { + Ok((err, _)) => err.emit(), + Err(guar) => guar, + }) + .map(|(symbol, style, _)| (symbol, style)) + }) +} + +/// Non-fatally assert that `tts` is empty. Note that this function +/// returns even when `tts` is non-empty, macros that *need* to stop +/// compilation should call `cx.diagnostic().abort_if_errors()` +/// (this should be done as rarely as possible). +pub(crate) fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) { + if !tts.is_empty() { + cx.dcx().emit_err(errors::TakesNoArguments { span, name }); + } +} + +/// Parse an expression. On error, emit it, advancing to `Eof`, and return `Err`. +pub(crate) fn parse_expr(p: &mut parser::Parser<'_>) -> Result, ErrorGuaranteed> { + let guar = match p.parse_expr() { + Ok(expr) => return Ok(expr), + Err(err) => err.emit(), + }; + while p.token != token::Eof { + p.bump(); + } + Err(guar) +} + +/// Interpreting `tts` as a comma-separated sequence of expressions, +/// expect exactly one string literal, or emit an error and return `Err`. +pub(crate) fn get_single_str_from_tts( + cx: &mut ExtCtxt<'_>, + span: Span, + tts: TokenStream, + name: &str, +) -> ExpandResult, ()> { + get_single_str_spanned_from_tts(cx, span, tts, name).map(|res| res.map(|(s, _)| s)) +} + +pub(crate) fn get_single_str_spanned_from_tts( + cx: &mut ExtCtxt<'_>, + span: Span, + tts: TokenStream, + name: &str, +) -> ExpandResult, ()> { + let mut p = cx.new_parser_from_tts(tts); + if p.token == token::Eof { + let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); + return ExpandResult::Ready(Err(guar)); + } + let ret = match parse_expr(&mut p) { + Ok(ret) => ret, + Err(guar) => return ExpandResult::Ready(Err(guar)), + }; + let _ = p.eat(&token::Comma); + + if p.token != token::Eof { + cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); + } + expr_to_spanned_string(cx, ret, "argument must be a string literal").map(|res| { + res.map_err(|err| match err { + Ok((err, _)) => err.emit(), + Err(guar) => guar, + }) + .map(|(symbol, _style, span)| (symbol, span)) + }) +} + +/// Extracts comma-separated expressions from `tts`. +/// On error, emit it, and return `Err`. +pub(crate) fn get_exprs_from_tts( + cx: &mut ExtCtxt<'_>, + tts: TokenStream, +) -> ExpandResult>, ErrorGuaranteed>, ()> { + let mut p = cx.new_parser_from_tts(tts); + let mut es = Vec::new(); + while p.token != token::Eof { + let expr = match parse_expr(&mut p) { + Ok(expr) => expr, + Err(guar) => return ExpandResult::Ready(Err(guar)), + }; + if !cx.force_mode + && let ast::ExprKind::MacCall(m) = &expr.kind + && cx.resolver.macro_accessible(cx.current_expansion.id, &m.path).is_err() + { + return ExpandResult::Retry(()); + } + + // Perform eager expansion on the expression. + // We want to be able to handle e.g., `concat!("foo", "bar")`. + let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); + + es.push(expr); + if p.eat(&token::Comma) { + continue; + } + if p.token != token::Eof { + let guar = cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span }); + return ExpandResult::Ready(Err(guar)); + } + } + ExpandResult::Ready(Ok(es)) +} diff --git a/compiler/rustc_codegen_cranelift/.cirrus.yml b/compiler/rustc_codegen_cranelift/.cirrus.yml index aa1a2bad2cf2a..5a464bfac3644 100644 --- a/compiler/rustc_codegen_cranelift/.cirrus.yml +++ b/compiler/rustc_codegen_cranelift/.cirrus.yml @@ -7,10 +7,14 @@ 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 test_script: - . $HOME/.cargo/env - - ./y.sh test + # Disabling incr comp reduces cache size and incr comp doesn't save as much + # on CI anyway. + - export CARGO_BUILD_INCREMENTAL=false + # 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 6fcfca34ea776..1eb2b7f23b26c 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/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index e6bf944f5527b..a745f2801cc4e 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml @@ -3,6 +3,8 @@ name: Abi-cafe on: - push +permissions: {} + jobs: abi_cafe: runs-on: ${{ matrix.os }} diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 526871d0c05f4..14aa850ff5cb7 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -4,6 +4,20 @@ on: - push - pull_request +defaults: + run: + shell: bash + +permissions: {} + +env: + # Disabling incr comp reduces cache size and incr comp doesn't save as much + # on CI anyway. + CARGO_BUILD_INCREMENTAL: false + # Rust's CI denies warnings. Deny them here too to ensure subtree syncs don't + # fail because of warnings. + RUSTFLAGS: "-Dwarnings" + jobs: rustfmt: runs-on: ubuntu-latest @@ -23,15 +37,15 @@ jobs: cargo fmt --check rustfmt --check build_system/main.rs rustfmt --check example/* + rustfmt --check scripts/*.rs test: runs-on: ${{ matrix.os }} timeout-minutes: 60 - defaults: - run: - shell: bash + env: + CG_CLIF_EXPENSIVE_CHECKS: 1 strategy: fail-fast: false @@ -47,15 +61,19 @@ jobs: - os: ubuntu-latest env: TARGET_TRIPLE: x86_64-pc-windows-gnu + apt_deps: gcc-mingw-w64-x86-64 wine-stable - os: ubuntu-latest env: TARGET_TRIPLE: aarch64-unknown-linux-gnu + apt_deps: gcc-aarch64-linux-gnu qemu-user - os: ubuntu-latest env: TARGET_TRIPLE: s390x-unknown-linux-gnu + apt_deps: gcc-s390x-linux-gnu qemu-user - os: ubuntu-latest env: TARGET_TRIPLE: riscv64gc-unknown-linux-gnu + apt_deps: gcc-riscv64-linux-gnu qemu-user - os: windows-latest env: TARGET_TRIPLE: x86_64-pc-windows-msvc @@ -80,29 +98,11 @@ jobs: if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' run: rustup set default-host x86_64-pc-windows-gnu - - name: Install MinGW toolchain and wine - if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' - run: | - sudo apt-get update - sudo apt-get install -y gcc-mingw-w64-x86-64 wine-stable - - - name: Install AArch64 toolchain and qemu - if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'aarch64-unknown-linux-gnu' - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu qemu-user - - - name: Install s390x toolchain and qemu - if: matrix.env.TARGET_TRIPLE == 's390x-unknown-linux-gnu' + - name: Install toolchain and emulator + if: matrix.apt_deps != null run: | sudo apt-get update - sudo apt-get install -y gcc-s390x-linux-gnu qemu-user - - - name: Install riscv64gc toolchain and qemu - if: matrix.env.TARGET_TRIPLE == 'riscv64gc-unknown-linux-gnu' - run: | - sudo apt-get update - sudo apt-get install -y gcc-riscv64-linux-gnu qemu-user + sudo apt-get install -y ${{ matrix.apt_deps }} - name: Prepare dependencies run: ./y.sh prepare @@ -142,10 +142,6 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 - defaults: - run: - shell: bash - steps: - uses: actions/checkout@v4 @@ -168,10 +164,6 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 - defaults: - run: - shell: bash - steps: - uses: actions/checkout@v4 @@ -193,20 +185,16 @@ jobs: run: ./y.sh prepare - name: Build - run: CI_OPT=1 ./y.sh build --sysroot none + run: ./y.sh build --sysroot none - name: Benchmark - run: CI_OPT=1 ./y.sh bench + run: ./y.sh bench dist: runs-on: ${{ matrix.os }} timeout-minutes: 60 - defaults: - run: - shell: bash - strategy: fail-fast: false matrix: @@ -252,10 +240,10 @@ jobs: run: ./y.sh prepare - name: Build backend - run: CI_OPT=1 ./y.sh build --sysroot none + run: ./y.sh build --sysroot none - name: Build sysroot - run: CI_OPT=1 ./y.sh build + run: ./y.sh build - name: Package prebuilt cg_clif run: tar cvfJ cg_clif.tar.xz dist @@ -280,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/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml index 930d025b73edc..75ea94ee79790 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml @@ -3,6 +3,8 @@ name: Various rustc tests on: - push +permissions: {} + jobs: bootstrap_rustc: runs-on: ubuntu-latest diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index e308cf80284ad..33fe52ddbdd64 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "arbitrary" @@ -34,9 +34,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cfg-if" @@ -46,18 +46,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9515fcc42b6cb5137f76b84c1a6f819782d0cf12473d145d3bc5cd67eedc8bc2" +checksum = "79b27922a6879b5b5361d0a084cb0b1941bf109a98540addcb932da13b68bed4" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad827c6071bfe6d22de1bc331296a29f9ddc506ff926d8415b435ec6a6efce0" +checksum = "304c455b28bf56372729acb356afbb55d622f2b0f2f7837aa5e57c138acaac4d" dependencies = [ "bumpalo", "cranelift-bforest", @@ -67,7 +67,7 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "log", "regalloc2", "smallvec", @@ -76,39 +76,39 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10e6b36237a9ca2ce2fb4cc7741d418a080afa1327402138412ef85d5367bef1" +checksum = "1653c56b99591d07f67c5ca7f9f25888948af3f4b97186bff838d687d666f613" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36bf4bfb86898a94ccfa773a1f86e8a5346b1983ff72059bdd2db4600325251" +checksum = "f5b6a9cf6b6eb820ee3f973a0db313c05dc12d370f37b4fe9630286e1672573f" [[package]] name = "cranelift-control" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cbf36560e7a6bd1409ca91e7b43b2cc7ed8429f343d7605eadf9046e8fac0d0" +checksum = "d9d06e6bf30075fb6bed9e034ec046475093392eea1aff90eb5c44c4a033d19a" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71e11061a75b1184c09bea97c026a88f08b59ade96a7bb1f259d4ea0df2e942" +checksum = "29be04f931b73cdb9694874a295027471817f26f26d2f0ebe5454153176b6e3a" [[package]] name = "cranelift-frontend" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af5d4da63143ee3485c7bcedde0a818727d737d1083484a0ceedb8950c89e495" +checksum = "a07fd7393041d7faa2f37426f5dc7fc04003b70988810e8c063beefeff1cd8f9" dependencies = [ "cranelift-codegen", "log", @@ -118,15 +118,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457a9832b089e26f5eea70dcf49bed8ec6edafed630ce7c83161f24d46ab8085" +checksum = "f341d7938caa6dff8149dac05bb2b53fc680323826b83b4cf175ab9f5139a3c9" [[package]] name = "cranelift-jit" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0af95fe68d5a10919012c8db82b1d59820405b8001c8c6d05f94b08031334fa9" +checksum = "42733555e06433f1461570e09dbd756dafc228b4dac75c597cdbdc518de07522" dependencies = [ "anyhow", "cranelift-codegen", @@ -139,14 +139,14 @@ dependencies = [ "region", "target-lexicon", "wasmtime-jit-icache-coherence", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "cranelift-module" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b0b201fa10a4014062d4c56c307c8d18fdf9a84cb5279efe6080241f42c7a7" +checksum = "84950af02bb85f3da764d53a953b43bb29a732e793d4fe24637a61591be9a024" dependencies = [ "anyhow", "cranelift-codegen", @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b490d579df1ce365e1ea359e24ed86d82289fa785153327c2f6a69a59a731e4" +checksum = "82af6066e6448d26eeabb7aa26a43f7ff79f8217b06bade4ee6ef230aecc8880" dependencies = [ "cranelift-codegen", "libc", @@ -166,9 +166,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.105.2" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7e821ac6db471bcdbd004e5a4fa0d374f1046bd3a2ce278c332e0b0c01ca63" +checksum = "00af56107039ed150391df6f753298c7b08f2b6a2e0727d216b5fa599d684d8b" dependencies = [ "anyhow", "cranelift-codegen", @@ -181,9 +181,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -222,21 +222,21 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", ] [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.3", ] [[package]] @@ -247,19 +247,19 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets", ] [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "mach" @@ -272,42 +272,42 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.3" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "object" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d" dependencies = [ "crc32fast", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "indexmap", "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "proc-macro2" -version = "1.0.75" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -369,9 +369,9 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "stable_deref_trait" @@ -381,9 +381,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" -version = "2.0.47" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -392,9 +392,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.12" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "unicode-ident" @@ -410,13 +410,13 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasmtime-jit-icache-coherence" -version = "18.0.2" +version = "20.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33f4121cb29dda08139b2824a734dd095d83ce843f2d613a84eb580b9cfc17ac" +checksum = "7a9f93a3289057b26dc75eb84d6e60d7694f7d169c7c09597495de6e016a13ff" dependencies = [ "cfg-if", "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -441,137 +441,78 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "zerocopy" diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index c0b9e27b179d3..2015cdbcc2a74 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,15 +8,15 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.105.2", default-features = false, features = ["std", "unwind", "all-arch"] } -cranelift-frontend = { version = "0.105.2" } -cranelift-module = { version = "0.105.2" } -cranelift-native = { version = "0.105.2" } -cranelift-jit = { version = "0.105.2", optional = true } -cranelift-object = { version = "0.105.2" } +cranelift-codegen = { version = "0.107.0", default-features = false, features = ["std", "unwind", "all-arch"] } +cranelift-frontend = { version = "0.107.0" } +cranelift-module = { version = "0.107.0" } +cranelift-native = { version = "0.107.0" } +cranelift-jit = { version = "0.107.0", optional = true } +cranelift-object = { version = "0.107.0" } target-lexicon = "0.12.0" gimli = { version = "0.28", default-features = false, features = ["write"]} -object = { version = "0.32", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } +object = { version = "0.33", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } indexmap = "2.0.0" libloading = { version = "0.8.0", optional = true } diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index a297b22326f37..00ea15cb38cc2 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -101,27 +101,7 @@ For additional ways to use rustc_codegen_cranelift like the JIT mode see [usage. ## Building and testing with changes in rustc code -This is useful when changing code in `rustc_codegen_cranelift` as part of changing [main Rust repository](https://github.com/rust-lang/rust/). -This can happen, for example, when you are implementing a new compiler intrinsic. - -Instruction below uses `$RustCheckoutDir` as substitute for any folder where you cloned Rust repository. - -You need to do this steps to successfully compile and use the cranelift backend with your changes in rustc code: - -1. `cd $RustCheckoutDir` -2. Run `python x.py setup` and choose option for compiler (`b`). -3. Build compiler and necessary tools: `python x.py build --stage=2 compiler library/std src/tools/rustdoc src/tools/rustfmt` - * (Optional) You can also build cargo by adding `src/tools/cargo` to previous command. -4. Copy cargo from a nightly toolchain: `cp $(rustup +nightly which cargo) ./build/host/stage2/bin/cargo`. Note that you would need to do this every time you rebuilt `rust` repository. -5. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`. -6. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`. -7. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc. - * `rustup run stage2 ./y.sh prepare` - * `rustup run stage2 ./y.sh build` - * (Optional) run tests: `rustup run stage2 ./y.sh test` -8. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`. - -You can also set `rust-analyzer.rustc.source` to your rust workspace to get rust-analyzer to understand your changes. +See [rustc_testing.md](docs/rustc_testing.md). ## Not yet supported diff --git a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs index 2e7ba1b2060b6..ecf303c30b6c3 100644 --- a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs +++ b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs @@ -43,7 +43,13 @@ pub(crate) fn run( let mut cmd = ABI_CAFE.run(bootstrap_host_compiler, dirs); cmd.arg("--"); cmd.arg("--pairs"); - cmd.args(pairs); + cmd.args( + if cfg!(not(any(target_os = "macos", all(target_os = "windows", target_env = "msvc")))) { + &pairs[..] + } else { + &pairs[..2] + }, + ); cmd.arg("--add-rustc-codegen-backend"); match cg_clif_dylib { CodegenBackend::Local(path) => { diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index d90111adf7761..129713e574ad0 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -1,9 +1,10 @@ +use std::env; use std::path::PathBuf; use crate::path::{Dirs, RelPath}; use crate::rustc_info::get_file_name; use crate::shared_utils::{rustflags_from_env, rustflags_to_cmd_env}; -use crate::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler, LogGroup}; +use crate::utils::{CargoProject, Compiler, LogGroup}; pub(crate) static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif"); @@ -16,20 +17,15 @@ pub(crate) fn build_backend( let _group = LogGroup::guard("Build backend"); let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs); - maybe_incremental(&mut cmd); let mut rustflags = rustflags_from_env("RUSTFLAGS"); rustflags.push("-Zallow-features=rustc_private".to_owned()); - if is_ci() { - // Deny warnings on CI - rustflags.push("-Dwarnings".to_owned()); - - if !is_ci_opt() { - cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true"); - cmd.env("CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS", "true"); - } + if env::var("CG_CLIF_EXPENSIVE_CHECKS").is_ok() { + // Enabling debug assertions implicitly enables the clif ir verifier + cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true"); + cmd.env("CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS", "true"); } if use_unstable_features { diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 1ed896c6bf0e4..10c3f9cfa2ce3 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -6,8 +6,7 @@ use std::process::Command; use crate::path::{Dirs, RelPath}; use crate::rustc_info::get_file_name; use crate::utils::{ - maybe_incremental, remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler, - LogGroup, + remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler, LogGroup, }; use crate::{config, CodegenBackend, SysrootKind}; @@ -270,7 +269,6 @@ fn build_clif_sysroot_for_triple( } compiler.rustflags.extend(rustflags); let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); - maybe_incremental(&mut build_cmd); if channel == "release" { build_cmd.arg("--release"); } diff --git a/compiler/rustc_codegen_cranelift/build_system/main.rs b/compiler/rustc_codegen_cranelift/build_system/main.rs index e8cf486e966ed..cdd2bae03f8f1 100644 --- a/compiler/rustc_codegen_cranelift/build_system/main.rs +++ b/compiler/rustc_codegen_cranelift/build_system/main.rs @@ -6,7 +6,7 @@ use std::env; use std::path::PathBuf; use std::process; -use self::utils::{is_ci, is_ci_opt, Compiler}; +use self::utils::Compiler; mod abi_cafe; mod bench; @@ -60,14 +60,9 @@ fn main() { } env::set_var("CG_CLIF_DISABLE_INCR_CACHE", "1"); - if is_ci() { - // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway - env::set_var("CARGO_BUILD_INCREMENTAL", "false"); - - if !is_ci_opt() { - // Enable the Cranelift verifier - env::set_var("CG_CLIF_ENABLE_VERIFIER", "1"); - } + // Force incr comp even in release mode unless in CI or incremental builds are explicitly disabled + if env::var_os("CARGO_BUILD_INCREMENTAL").is_none() { + env::set_var("CARGO_BUILD_INCREMENTAL", "true"); } let mut args = env::args().skip(1); diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index 3677d0a7d3607..5525a5f63e93b 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -15,7 +15,6 @@ pub(crate) fn prepare(dirs: &Dirs) { RelPath::DOWNLOAD.ensure_exists(dirs); crate::tests::RAND_REPO.fetch(dirs); crate::tests::REGEX_REPO.fetch(dirs); - crate::tests::PORTABLE_SIMD_REPO.fetch(dirs); } pub(crate) fn prepare_stdlib(dirs: &Dirs, rustc: &Path) { diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 1c3e615c7aba2..76104901474c2 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -130,16 +130,10 @@ pub(crate) static REGEX_REPO: GitRepo = GitRepo::github( pub(crate) static REGEX: CargoProject = CargoProject::new(®EX_REPO.source_dir(), "regex_target"); -pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github( - "rust-lang", - "portable-simd", - "5794c837bc605c4cd9dbb884285976dfdb293cce", - "a64d8fdd0ed0d9c4", - "portable-simd", -); +pub(crate) static PORTABLE_SIMD_SRC: RelPath = RelPath::BUILD.join("coretests"); pub(crate) static PORTABLE_SIMD: CargoProject = - CargoProject::new(&PORTABLE_SIMD_REPO.source_dir(), "portable-simd_target"); + CargoProject::new(&PORTABLE_SIMD_SRC, "portable-simd_target"); static LIBCORE_TESTS_SRC: RelPath = RelPath::BUILD.join("coretests"); @@ -221,7 +215,12 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ } }), TestCase::custom("test.portable-simd", &|runner| { - PORTABLE_SIMD_REPO.patch(&runner.dirs); + apply_patches( + &runner.dirs, + "portable-simd", + &runner.stdlib_source.join("library/portable-simd"), + &PORTABLE_SIMD_SRC.to_path(&runner.dirs), + ); PORTABLE_SIMD.clean(&runner.dirs); @@ -291,7 +290,7 @@ pub(crate) fn run_tests( && !skip_tests.contains(&"testsuite.extended_sysroot"); if run_base_sysroot || run_extended_sysroot { - let mut target_compiler = build_sysroot::build_sysroot( + let target_compiler = build_sysroot::build_sysroot( dirs, channel, sysroot_kind, @@ -300,11 +299,8 @@ pub(crate) fn run_tests( rustup_toolchain_name, target_triple.clone(), ); - // Rust's build system denies a couple of lints that trigger on several of the test - // projects. Changing the code to fix them is not worth it, so just silence all lints. - target_compiler.rustflags.push("--cap-lints=allow".to_owned()); - let runner = TestRunner::new( + let mut runner = TestRunner::new( dirs.clone(), target_compiler, use_unstable_features, @@ -320,6 +316,9 @@ pub(crate) fn run_tests( } if run_extended_sysroot { + // Rust's build system denies a couple of lints that trigger on several of the test + // projects. Changing the code to fix them is not worth it, so just silence all lints. + runner.target_compiler.rustflags.push("--cap-lints=allow".to_owned()); runner.run_testsuite(EXTENDED_SYSROOT_SUITE); } else { eprintln!("[SKIP] extended_sysroot tests"); diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index 149f1618f5c0e..9f95122b341c5 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -254,14 +254,6 @@ pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) { } } -pub(crate) fn is_ci() -> bool { - env::var("CI").is_ok() -} - -pub(crate) fn is_ci_opt() -> bool { - env::var("CI_OPT").is_ok() -} - static IN_GROUP: AtomicBool = AtomicBool::new(false); pub(crate) struct LogGroup { is_gha: bool, @@ -288,13 +280,3 @@ impl Drop for LogGroup { IN_GROUP.store(false, Ordering::SeqCst); } } - -pub(crate) fn maybe_incremental(cmd: &mut Command) { - if is_ci() || std::env::var("CARGO_BUILD_INCREMENTAL").map_or(false, |val| val == "false") { - // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway - cmd.env("CARGO_BUILD_INCREMENTAL", "false"); - } else { - // Force incr comp even in release mode unless in CI or incremental builds are explicitly disabled - cmd.env("CARGO_BUILD_INCREMENTAL", "true"); - } -} diff --git a/compiler/rustc_codegen_cranelift/docs/rustc_testing.md b/compiler/rustc_codegen_cranelift/docs/rustc_testing.md new file mode 100644 index 0000000000000..88c555572318b --- /dev/null +++ b/compiler/rustc_codegen_cranelift/docs/rustc_testing.md @@ -0,0 +1,23 @@ +# Building and testing with changes in rustc code + +This is useful when changing code in `rustc_codegen_cranelift` as part of changing [main Rust repository](https://github.com/rust-lang/rust/). +This can happen, for example, when you are implementing a new compiler intrinsic. + +Instruction below uses `$RustCheckoutDir` as substitute for any folder where you cloned Rust repository. + +You need to do this steps to successfully compile and use the cranelift backend with your changes in rustc code: + +1. `cd $RustCheckoutDir` +2. Run `python x.py setup` and choose option for compiler (`b`). +3. Build compiler and necessary tools: `python x.py build --stage=2 compiler library/std src/tools/rustdoc src/tools/rustfmt` + * (Optional) You can also build cargo by adding `src/tools/cargo` to previous command. +4. Copy cargo from a nightly toolchain: `cp $(rustup +nightly which cargo) ./build/host/stage2/bin/cargo`. Note that you would need to do this every time you rebuilt `rust` repository. +5. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`. +6. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`. +7. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc. + * `rustup run stage2 ./y.sh prepare` + * `rustup run stage2 ./y.sh build` + * (Optional) run tests: `rustup run stage2 ./y.sh test` +8. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`. + +You can also set `rust-analyzer.rustc.source` to your rust workspace to get rust-analyzer to understand your changes. diff --git a/compiler/rustc_codegen_cranelift/example/alloc_example.rs b/compiler/rustc_codegen_cranelift/example/alloc_example.rs index 117eed5afd8ab..da70ca7943983 100644 --- a/compiler/rustc_codegen_cranelift/example/alloc_example.rs +++ b/compiler/rustc_codegen_cranelift/example/alloc_example.rs @@ -1,4 +1,5 @@ #![feature(start, core_intrinsics, alloc_error_handler, lang_items)] +#![allow(internal_features)] #![no_std] extern crate alloc; diff --git a/compiler/rustc_codegen_cranelift/example/alloc_system.rs b/compiler/rustc_codegen_cranelift/example/alloc_system.rs index e64daf96b01c9..441f3cd298743 100644 --- a/compiler/rustc_codegen_cranelift/example/alloc_system.rs +++ b/compiler/rustc_codegen_cranelift/example/alloc_system.rs @@ -80,7 +80,6 @@ mod platform { extern "system" { fn GetProcessHeap() -> HANDLE; fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; - fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; fn GetLastError() -> DWORD; } @@ -111,7 +110,7 @@ mod platform { allocate_with_flags(layout, HEAP_ZERO_MEMORY) } #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { let header = get_header(ptr); let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); diff --git a/compiler/rustc_codegen_cranelift/example/example.rs b/compiler/rustc_codegen_cranelift/example/example.rs index 885e55bc76423..1ef2aa5dd8ea4 100644 --- a/compiler/rustc_codegen_cranelift/example/example.rs +++ b/compiler/rustc_codegen_cranelift/example/example.rs @@ -149,7 +149,7 @@ pub fn array_as_slice(arr: &[u8; 3]) -> &[u8] { arr } -pub unsafe fn use_ctlz_nonzero(a: u16) -> u16 { +pub unsafe fn use_ctlz_nonzero(a: u16) -> u32 { intrinsics::ctlz_nonzero(a) } diff --git a/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs b/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs index a71217a554b57..c54574d801d3a 100644 --- a/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs +++ b/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs @@ -5,7 +5,7 @@ // Test that the simd_f{min,max} intrinsics produce the correct results. #![feature(repr_simd, core_intrinsics)] -#![allow(non_camel_case_types)] +#![allow(internal_features, non_camel_case_types)] #[repr(simd)] #[derive(Copy, Clone, PartialEq, Debug)] diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 39988cf64e54b..5e535ff62e173 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -90,8 +90,9 @@ unsafe impl Sync for i16 {} unsafe impl Sync for i32 {} unsafe impl Sync for isize {} unsafe impl Sync for char {} +unsafe impl Sync for f32 {} unsafe impl<'a, T: ?Sized> Sync for &'a T {} -unsafe impl Sync for [u8; 16] {} +unsafe impl Sync for [T; N] {} #[lang = "freeze"] unsafe auto trait Freeze {} @@ -465,6 +466,35 @@ pub fn panic(_msg: &'static str) -> ! { } } +macro_rules! panic_const { + ($($lang:ident = $message:expr,)+) => { + pub mod panic_const { + use super::*; + + $( + #[track_caller] + #[lang = stringify!($lang)] + pub fn $lang() -> ! { + panic($message); + } + )+ + } + } +} + +panic_const! { + panic_const_add_overflow = "attempt to add with overflow", + panic_const_sub_overflow = "attempt to subtract with overflow", + panic_const_mul_overflow = "attempt to multiply with overflow", + panic_const_div_overflow = "attempt to divide with overflow", + panic_const_rem_overflow = "attempt to calculate the remainder with overflow", + panic_const_neg_overflow = "attempt to negate with overflow", + panic_const_shr_overflow = "attempt to shift right with overflow", + panic_const_shl_overflow = "attempt to shift left with overflow", + panic_const_div_by_zero = "attempt to divide by zero", + panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", +} + #[lang = "panic_bounds_check"] #[track_caller] fn panic_bounds_check(index: usize, len: usize) -> ! { @@ -597,7 +627,7 @@ pub mod intrinsics { pub fn min_align_of_val(val: *const T) -> usize; pub fn copy(src: *const T, dst: *mut T, count: usize); pub fn transmute(e: T) -> U; - pub fn ctlz_nonzero(x: T) -> T; + pub fn ctlz_nonzero(x: T) -> u32; #[rustc_safe_intrinsic] pub fn needs_drop() -> bool; #[rustc_safe_intrinsic] diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 8b0b9123ac7df..efa4be7e15ac3 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,4 +1,13 @@ -#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)] +#![feature( + no_core, + lang_items, + never_type, + linkage, + extern_types, + thread_local, + repr_simd, + raw_ref_op +)] #![no_core] #![allow(dead_code, non_camel_case_types, internal_features)] @@ -112,9 +121,7 @@ fn start( static mut NUM: u8 = 6 * 7; -// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint -#[allow(static_mut_refs)] -static NUM_REF: &'static u8 = unsafe { &NUM }; +static NUM_REF: &'static u8 = unsafe { &*&raw const NUM }; unsafe fn zeroed() -> T { let mut uninit = MaybeUninit { uninit: () }; diff --git a/compiler/rustc_codegen_cranelift/example/mod_bench.rs b/compiler/rustc_codegen_cranelift/example/mod_bench.rs index f15e48acc41e5..11a3e8fc72d8d 100644 --- a/compiler/rustc_codegen_cranelift/example/mod_bench.rs +++ b/compiler/rustc_codegen_cranelift/example/mod_bench.rs @@ -1,4 +1,5 @@ #![feature(start, core_intrinsics, lang_items)] +#![allow(internal_features)] #![no_std] #[cfg_attr(unix, link(name = "c"))] diff --git a/compiler/rustc_codegen_cranelift/example/neon.rs b/compiler/rustc_codegen_cranelift/example/neon.rs index bad26947967dc..00e437d7e546a 100644 --- a/compiler/rustc_codegen_cranelift/example/neon.rs +++ b/compiler/rustc_codegen_cranelift/example/neon.rs @@ -4,7 +4,9 @@ #[cfg(target_arch = "aarch64")] use std::arch::aarch64::*; +#[cfg(target_arch = "aarch64")] use std::mem::transmute; +#[cfg(target_arch = "aarch64")] use std::simd::*; #[cfg(target_arch = "aarch64")] diff --git a/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs b/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs index c965b34e13b90..407da94c0f092 100644 --- a/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs +++ b/compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs @@ -1,4 +1,4 @@ -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::Coroutine; use std::pin::Pin; @@ -8,7 +8,8 @@ fn main() { } fn run_coroutine() { - let mut coroutine = || { + let mut coroutine = #[coroutine] + || { yield; return; }; diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 2fee912e52ccf..90d4ab721daef 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -1,12 +1,14 @@ #![feature( core_intrinsics, coroutines, + stmt_expr_attributes, coroutine_trait, is_sorted, repr_simd, tuple_trait, unboxed_closures )] +#![allow(internal_features)] #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; @@ -122,9 +124,12 @@ fn main() { test_simd(); } - Box::pin(move |mut _task_context| { - yield (); - }) + Box::pin( + #[coroutine] + move |mut _task_context| { + yield (); + }, + ) .as_mut() .resume(0); diff --git a/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch b/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch index 0e5e7cdfcdf1a..77716c5139978 100644 --- a/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch +++ b/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch @@ -11,7 +11,7 @@ diff --git a/src/report.rs b/src/report.rs index eeec614..f582867 100644 --- a/src/report.rs +++ b/src/report.rs -@@ -48,6 +48,12 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl +@@ -48,6 +48,15 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl // // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES @@ -19,6 +19,9 @@ index eeec614..f582867 100644 + if cfg!(all(target_os = "windows", target_env = "gnu")) && test.test_name == "ui128" { + result.run = Link; + result.check = Pass(Link); ++ } else if test.test_name == "ui128" { ++ result.run == Check; ++ result.check = Pass(Check); + } + // END OF VENDOR RESERVED AREA diff --git a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch index 5442c3cef9ec2..7cf7f86700eaf 100644 --- a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch @@ -39,6 +39,6 @@ index 42a26ae..5ac1042 100644 +#![cfg(test)] #![feature(alloc_layout_extra)] #![feature(array_chunks)] - #![feature(array_windows)] + #![feature(array_ptr_get)] -- 2.21.0 (Apple Git-122) 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 646928893e96a..a3f370af91699 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/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch b/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch new file mode 100644 index 0000000000000..21f5ee9cc6ea0 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch @@ -0,0 +1,37 @@ +From 0d741cf82c3c908616abd39dc84ebf7d8702e0c3 Mon Sep 17 00:00:00 2001 +From: Chris Denton +Date: Tue, 16 Apr 2024 15:51:34 +0000 +Subject: [PATCH] Revert use raw-dylib for Windows futex APIs + +--- + library/std/src/sys/pal/windows/c.rs | 14 +------------- + 1 file changed, 1 insertion(+), 13 deletions(-) + +diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs +index 9d58ce05f01..1c828bac4b6 100644 +--- a/library/std/src/sys/pal/windows/c.rs ++++ b/library/std/src/sys/pal/windows/c.rs +@@ -357,19 +357,7 @@ pub fn GetTempPath2W(bufferlength: u32, buffer: PWSTR) -> u32 { + } + + #[cfg(not(target_vendor = "win7"))] +-// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library. +-#[cfg_attr( +- target_arch = "x86", +- link( +- name = "api-ms-win-core-synch-l1-2-0", +- kind = "raw-dylib", +- import_name_type = "undecorated" +- ) +-)] +-#[cfg_attr( +- not(target_arch = "x86"), +- link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib") +-)] ++#[link(name = "synchronization")] + extern "system" { + pub fn WaitOnAddress( + address: *const c_void, +-- +2.42.0.windows.2 + diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index f3cd4cbe49354..de340cf8c35cc 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-03-16" +channel = "nightly-2024-04-23" components = ["rust-src", "rustc-dev", "llvm-tools"] diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs index 03912b18ea5f2..0252d5b334036 100755 --- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -1,5 +1,5 @@ #!/usr/bin/env bash -#![forbid(unsafe_code)]/* This line is ignored by bash +#![rustfmt::skip]/* This line is ignored by bash # This block is ignored by rustc pushd $(dirname "$0")/../ RUSTC="$(pwd)/dist/rustc-clif" @@ -26,11 +26,8 @@ fn main() -> Result<(), Box> { } let profile = std::fs::read_to_string(profile_name) .map_err(|err| format!("Failed to read profile {}", err))?; - let mut output = std::fs::OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(output_name)?; + let mut output = + std::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_name)?; for line in profile.lines() { let mut stack = &line[..line.rfind(" ").unwrap()]; diff --git a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs index 550f20515536e..92defd21cd9bc 100644 --- a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs @@ -26,9 +26,10 @@ fn main() { codegen_backend_arg.push(cg_clif_dylib_path); args.push(codegen_backend_arg); } - if !passed_args.iter().any(|arg| { - arg == "--sysroot" || arg.to_str().is_some_and(|s| s.starts_with("--sysroot=")) - }) { + if !passed_args + .iter() + .any(|arg| arg == "--sysroot" || arg.to_str().is_some_and(|s| s.starts_with("--sysroot="))) + { args.push(OsString::from("--sysroot")); args.push(OsString::from(sysroot.to_str().unwrap())); } diff --git a/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs index f7d1bdbc4c6ff..1cad312bb7918 100644 --- a/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs @@ -26,12 +26,18 @@ fn main() { codegen_backend_arg.push(cg_clif_dylib_path); args.push(codegen_backend_arg); } - if !passed_args.iter().any(|arg| { - arg == "--sysroot" || arg.to_str().is_some_and(|s| s.starts_with("--sysroot=")) - }) { + if !passed_args + .iter() + .any(|arg| arg == "--sysroot" || arg.to_str().is_some_and(|s| s.starts_with("--sysroot="))) + { args.push(OsString::from("--sysroot")); args.push(OsString::from(sysroot.to_str().unwrap())); } + if passed_args.is_empty() { + // Don't pass any arguments when the user didn't pass any arguments + // either to ensure the help message is shown. + args.clear(); + } args.extend(passed_args); let rustdoc = if let Some(rustdoc) = option_env!("RUSTDOC") { diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 9b360fb303624..8580f4557e883 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -10,22 +10,13 @@ pushd rust command -v rg >/dev/null 2>&1 || cargo install ripgrep -# FIXME(rust-lang/rust#122196) fix stage0 rmake.rs run-make tests and remove -# this workaround -for test in $(ls tests/run-make); do - if [[ -e "tests/run-make/$test/rmake.rs" ]]; then - rm -r "tests/run-make/$test" - fi -done - -# 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 @@ -41,23 +32,7 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR # missing features # ================ -# requires stack unwinding -# FIXME add needs-unwind to these tests -rm -r tests/run-make/libtest-junit -rm tests/ui/asm/may_unwind.rs -rm tests/ui/stable-mir-print/basic_function.rs - -# extra warning about -Cpanic=abort for proc macros -rm tests/ui/proc-macro/crt-static.rs -rm tests/ui/proc-macro/proc-macro-deprecated-attr.rs -rm tests/ui/proc-macro/quote-debug.rs -rm tests/ui/proc-macro/no-missing-docs.rs -rm tests/ui/rust-2018/proc-macro-crate-in-paths.rs -rm tests/ui/proc-macro/allowed-signatures.rs -rm tests/ui/proc-macro/no-mangle-in-proc-macro-issue-111888.rs - # vendor intrinsics -rm tests/ui/simd/array-type.rs # "Index argument for `simd_insert` is not a constant" rm tests/ui/asm/x86_64/evex512-implicit-feature.rs # unimplemented AVX512 x86 vendor intrinsic # exotic linkages @@ -74,12 +49,9 @@ rm -r tests/run-make/c-link-to-rust-va-list-fn # requires callee side vararg sup rm -r tests/run-pass-valgrind/unsized-locals # misc unimplemented things -rm tests/ui/intrinsics/intrinsic-nearby.rs # unimplemented nearbyintf32 and nearbyintf64 intrinsics rm tests/ui/target-feature/missing-plusminus.rs # error not implemented -rm -r tests/run-make/emit-named-files # requires full --emit support rm -r tests/run-make/repr128-dwarf # debuginfo test rm -r tests/run-make/split-debuginfo # same -rm -r tests/run-make/symbols-include-type-name # --emit=asm not supported rm -r tests/run-make/target-specs # i686 not supported by Cranelift rm -r tests/run-make/mismatching-target-triples # same rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't work entirely correctly @@ -106,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 @@ -117,6 +88,17 @@ rm tests/ui/abi/stack-protector.rs # requires stack protector support rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific +# requires asm, llvm-ir and/or llvm-bc emit support +# ============================================= +rm -r tests/run-make/emit-named-files +rm -r tests/run-make/issue-30063 +rm -r tests/run-make/multiple-emits +rm -r tests/run-make/output-type-permutations +rm -r tests/run-make/emit-to-stdout +rm -r tests/run-make/compressed-debuginfo +rm -r tests/run-make/symbols-include-type-name + + # giving different but possibly correct results # ============================================= rm tests/ui/mir/mir_misc_casts.rs # depends on deduplication of constants @@ -124,37 +106,20 @@ rm tests/ui/mir/mir_raw_fat_ptr.rs # same rm tests/ui/consts/issue-33537.rs # same rm tests/ui/consts/const-mut-refs-crate.rs # same -# rustdoc-clif passes extra args, suppressing the help message when no args are passed -rm -r tests/run-make/issue-88756-default-output - # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended # ============================================================ rm -r tests/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump +rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contains the standard library source # 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/issue-51671 # wrong filename given in case of --emit=obj -rm -r tests/run-make/issue-30063 # same -rm -r tests/run-make/multiple-emits # same -rm -r tests/run-make/output-type-permutations # same -rm -r tests/run-make/used # same -rm -r tests/run-make/no-alloc-shim -rm -r tests/run-make/emit-to-stdout -rm -r tests/run-make/compressed-debuginfo - rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported - -rm tests/ui/codegen/subtyping-enforces-type-equality.rs # assert_assignable bug with Coroutine's +rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort # bugs in the test suite # ====================== -rm tests/ui/backtrace.rs # TODO warning rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue -rm tests/ui/async-await/async-closures/once.rs # FIXME bug in the rustc FnAbi calculation code rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd @@ -176,6 +141,19 @@ index ea06b620c4c..b969d0009c6 100644 ifdef RUSTC_LINKER RUSTC := \$(RUSTC) -Clinker='\$(RUSTC_LINKER)' RUSTDOC := \$(RUSTDOC) -Clinker='\$(RUSTC_LINKER)' +diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs +index 9607ff02f96..b7d97caf9a2 100644 +--- a/src/tools/run-make-support/src/rustdoc.rs ++++ b/src/tools/run-make-support/src/rustdoc.rs +@@ -34,8 +34,6 @@ pub fn bare() -> Self { + /// Construct a \`rustdoc\` invocation with \`-L \$(TARGET_RPATH_DIR)\` set. + pub fn new() -> Self { + let mut cmd = setup_common(); +- let target_rpath_dir = env::var_os("TARGET_RPATH_DIR").unwrap(); +- cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); + Self { cmd } + } + EOF echo "[TEST] rustc test suite" diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 6e846d721f2e2..6f346af25c6dd 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -7,9 +7,14 @@ mod returning; use std::borrow::Cow; use cranelift_codegen::ir::SigRef; +use cranelift_codegen::isa::CallConv; use cranelift_module::ModuleError; +use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::TypeVisitableExt; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_target::abi::call::{Conv, FnAbi}; @@ -219,17 +224,15 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ Spread(Vec>>), } - let fn_abi = fx.fn_abi.take().unwrap(); - // FIXME implement variadics in cranelift - if fn_abi.c_variadic { + if fx.fn_abi.c_variadic { fx.tcx.dcx().span_fatal( fx.mir.span, "Defining variadic functions is not yet supported by Cranelift", ); } - let mut arg_abis_iter = fn_abi.args.iter(); + let mut arg_abis_iter = fx.fn_abi.args.iter(); let func_params = fx .mir @@ -276,7 +279,6 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ } assert!(arg_abis_iter.next().is_none(), "ArgAbi left behind"); - fx.fn_abi = Some(fn_abi); assert!(block_params_iter.next().is_none(), "arg_value left behind"); self::comments::add_locals_header_comment(fx); @@ -372,6 +374,17 @@ pub(crate) fn codegen_terminator_call<'tcx>( ty::Instance::expect_resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, fn_args) .polymorphize(fx.tcx); + if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { + if target.is_some() { + let caller = with_no_trimmed_paths!(fx.tcx.def_path_str(fx.instance.def_id())); + let callee = with_no_trimmed_paths!(fx.tcx.def_path_str(def_id)); + fx.tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee }); + } else { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } + } + if fx.tcx.symbol_name(instance).name.starts_with("llvm.") { crate::intrinsics::codegen_llvm_intrinsic_call( fx, @@ -663,11 +676,7 @@ pub(crate) fn codegen_drop<'tcx>( let arg_value = drop_place.place_ref( fx, - fx.layout_of(Ty::new_ref( - fx.tcx, - fx.tcx.lifetimes.re_erased, - TypeAndMut { ty, mutbl: crate::rustc_hir::Mutability::Mut }, - )), + fx.layout_of(Ty::new_mut_ref(fx.tcx, fx.tcx.lifetimes.re_erased, ty)), ); let arg_value = adjust_arg_for_abi(fx, arg_value, &fn_abi.args[0], true); diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs index 0799a22c6e168..e0f399e616e5d 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/returning.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs @@ -12,27 +12,15 @@ pub(super) fn codegen_return_param<'tcx>( ssa_analyzed: &rustc_index::IndexSlice, block_params_iter: &mut impl Iterator, ) -> CPlace<'tcx> { - let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.as_ref().unwrap().ret.mode { + let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.ret.mode { PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast { .. } => { - let is_ssa = - ssa_analyzed[RETURN_PLACE].is_ssa(fx, fx.fn_abi.as_ref().unwrap().ret.layout.ty); - ( - super::make_local_place( - fx, - RETURN_PLACE, - fx.fn_abi.as_ref().unwrap().ret.layout, - is_ssa, - ), - smallvec![], - ) + let is_ssa = ssa_analyzed[RETURN_PLACE].is_ssa(fx, fx.fn_abi.ret.layout.ty); + (super::make_local_place(fx, RETURN_PLACE, fx.fn_abi.ret.layout, is_ssa), smallvec![]) } PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { let ret_param = block_params_iter.next().unwrap(); assert_eq!(fx.bcx.func.dfg.value_type(ret_param), fx.pointer_type); - ( - CPlace::for_ptr(Pointer::new(ret_param), fx.fn_abi.as_ref().unwrap().ret.layout), - smallvec![ret_param], - ) + (CPlace::for_ptr(Pointer::new(ret_param), fx.fn_abi.ret.layout), smallvec![ret_param]) } PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { unreachable!("unsized return value") @@ -45,8 +33,8 @@ pub(super) fn codegen_return_param<'tcx>( Some(RETURN_PLACE), None, &ret_param, - &fx.fn_abi.as_ref().unwrap().ret.mode, - fx.fn_abi.as_ref().unwrap().ret.layout, + &fx.fn_abi.ret.mode, + fx.fn_abi.ret.layout, ); ret_place @@ -115,7 +103,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>( /// Codegen a return instruction with the right return value(s) if any. pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, '_>) { - match fx.fn_abi.as_ref().unwrap().ret.mode { + match fx.fn_abi.ret.mode { PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => { fx.bcx.ins().return_(&[]); } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 2415c2c90b22c..e3d050df4cd1b 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -2,15 +2,18 @@ use cranelift_codegen::ir::UserFuncName; use cranelift_codegen::CodegenError; +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_module::ModuleError; use rustc_ast::InlineAsmOptions; use rustc_index::IndexVec; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::TypeVisitableExt; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use crate::constant::ConstantCx; -use crate::debuginfo::FunctionDebugContext; +use crate::debuginfo::{FunctionDebugContext, TypeDebugContext}; use crate::prelude::*; use crate::pretty_clif::CommentWriter; @@ -25,6 +28,7 @@ pub(crate) struct CodegenedFunction { pub(crate) fn codegen_fn<'tcx>( tcx: TyCtxt<'tcx>, cx: &mut crate::CodegenCx, + type_dbg: &mut TypeDebugContext<'tcx>, cached_func: Function, module: &mut dyn Module, instance: Instance<'tcx>, @@ -68,8 +72,10 @@ pub(crate) fn codegen_fn<'tcx>( let pointer_type = target_config.pointer_type(); let clif_comments = crate::pretty_clif::CommentWriter::new(tcx, instance); + let fn_abi = RevealAllLayoutCx(tcx).fn_abi_of_instance(instance, ty::List::empty()); + let func_debug_cx = if let Some(debug_context) = &mut cx.debug_context { - Some(debug_context.define_function(tcx, &symbol_name, mir.span)) + Some(debug_context.define_function(tcx, type_dbg, instance, fn_abi, &symbol_name, mir.span)) } else { None }; @@ -86,7 +92,7 @@ pub(crate) fn codegen_fn<'tcx>( instance, symbol_name, mir, - fn_abi: Some(RevealAllLayoutCx(tcx).fn_abi_of_instance(instance, ty::List::empty())), + fn_abi, bcx, block_map, @@ -94,7 +100,6 @@ pub(crate) fn codegen_fn<'tcx>( caller_location: None, // set by `codegen_fn_prelude` clif_comments, - last_source_file: None, next_ssa_var: 0, }; @@ -264,10 +269,19 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { .generic_activity("codegen prelude") .run(|| crate::abi::codegen_fn_prelude(fx, start_block)); + 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; @@ -369,8 +383,14 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ); } _ => { - let msg_str = msg.description(); - codegen_panic(fx, msg_str, source_info); + let location = fx.get_caller_location(source_info).load_scalar(fx); + + codegen_panic_inner( + fx, + msg.panic_function(), + &[location], + Some(source_info.span), + ); } } } @@ -640,8 +660,8 @@ fn codegen_stmt<'tcx>( | CastKind::IntToFloat | CastKind::FnPtrToPtr | CastKind::PtrToPtr - | CastKind::PointerExposeAddress - | CastKind::PointerFromExposedAddress, + | CastKind::PointerExposeProvenance + | CastKind::PointerWithExposedProvenance, ref operand, to_ty, ) => { @@ -779,8 +799,8 @@ fn codegen_stmt<'tcx>( NullOp::OffsetOf(fields) => { layout.offset_of_subfield(fx, fields.iter()).bytes() } - NullOp::UbCheck(_) => { - let val = fx.tcx.sess.opts.debug_assertions; + NullOp::UbChecks => { + let val = fx.tcx.sess.ub_checks(); let val = CValue::by_val( fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), fx.layout_of(fx.tcx.types.bool), @@ -795,6 +815,25 @@ fn codegen_stmt<'tcx>( ); lval.write_cvalue(fx, val); } + Rvalue::Aggregate(ref kind, ref operands) + if matches!(**kind, AggregateKind::RawPtr(..)) => + { + let ty = to_place_and_rval.1.ty(&fx.mir.local_decls, fx.tcx); + let layout = fx.layout_of(fx.monomorphize(ty)); + let [data, meta] = &*operands.raw else { + bug!("RawPtr fields: {operands:?}"); + }; + let data = codegen_operand(fx, data); + let meta = codegen_operand(fx, meta); + assert!(data.layout().ty.is_unsafe_ptr()); + assert!(layout.ty.is_unsafe_ptr()); + let ptr_val = if meta.layout().is_zst() { + data.cast_pointer_to(layout) + } else { + CValue::by_val_pair(data.load_scalar(fx), meta.load_scalar(fx), layout) + }; + lval.write_cvalue(fx, ptr_val); + } Rvalue::Aggregate(ref kind, ref operands) => { let (variant_index, variant_dest, active_field_index) = match **kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { @@ -954,20 +993,6 @@ pub(crate) fn codegen_operand<'tcx>( } } -pub(crate) fn codegen_panic<'tcx>( - fx: &mut FunctionCx<'_, '_, 'tcx>, - msg_str: &str, - source_info: mir::SourceInfo, -) { - let location = fx.get_caller_location(source_info).load_scalar(fx); - - let msg_ptr = fx.anonymous_str(msg_str); - let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap()); - let args = [msg_ptr, msg_len, location]; - - codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, Some(source_info.span)); -} - pub(crate) fn codegen_panic_nounwind<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, msg_str: &str, @@ -999,6 +1024,12 @@ fn codegen_panic_inner<'tcx>( let def_id = fx.tcx.require_lang_item(lang_item, span); let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + + if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } + let symbol_name = fx.tcx.symbol_name(instance).name; fx.lib_call( diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index b2bc289a5b6ba..4a5ef352151f3 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -68,7 +68,7 @@ pub(crate) fn maybe_codegen<'tcx>( Some(CValue::by_val(ret_val, lhs.layout())) } } - BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None, + BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, } } @@ -134,6 +134,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), BinOp::Div | BinOp::Rem => unreachable!(), + BinOp::Cmp => unreachable!(), BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(), BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(), } diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 7e29d407a1fa3..2a24d6150bd6e 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -1,12 +1,11 @@ use cranelift_codegen::isa::TargetFrontendConfig; -use gimli::write::FileId; -use rustc_data_structures::sync::Lrc; +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_index::IndexVec; use rustc_middle::ty::layout::{ - FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, + self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, }; +use rustc_middle::ty::TypeFoldable; use rustc_span::source_map::Spanned; -use rustc_span::SourceFile; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Integer, Primitive}; use rustc_target::spec::{HasTargetSpec, Target}; @@ -69,7 +68,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option unimplemented!("f16_f128"), }, ty::FnPtr(_) => pointer_ty(tcx), - ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => { + ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { if has_ptr_meta(tcx, *pointee_ty) { return None; } else { @@ -89,7 +88,7 @@ fn clif_pair_type_from_ty<'tcx>( ty::Tuple(types) if types.len() == 2 => { (clif_type_from_ty(tcx, types[0])?, clif_type_from_ty(tcx, types[1])?) } - ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => { + ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { if has_ptr_meta(tcx, *pointee_ty) { (pointer_ty(tcx), pointer_ty(tcx)) } else { @@ -294,7 +293,7 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) instance: Instance<'tcx>, pub(crate) symbol_name: String, pub(crate) mir: &'tcx Body<'tcx>, - pub(crate) fn_abi: Option<&'tcx FnAbi<'tcx, Ty<'tcx>>>, + pub(crate) fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, pub(crate) bcx: FunctionBuilder<'clif>, pub(crate) block_map: IndexVec, @@ -305,11 +304,6 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) clif_comments: crate::pretty_clif::CommentWriter, - /// Last accessed source file and it's debuginfo file id. - /// - /// For optimization purposes only - pub(crate) last_source_file: Option<(Lrc, FileId)>, - /// This should only be accessed by `CPlace::new_var`. pub(crate) next_ssa_var: u32, } @@ -419,25 +413,8 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { pub(crate) fn set_debug_loc(&mut self, source_info: mir::SourceInfo) { if let Some(debug_context) = &mut self.cx.debug_context { - let (file, line, column) = - DebugContext::get_span_loc(self.tcx, self.mir.span, source_info.span); - - // add_source_file is very slow. - // Optimize for the common case of the current file not being changed. - let mut cached_file_id = None; - if let Some((ref last_source_file, last_file_id)) = self.last_source_file { - // If the allocations are not equal, the files may still be equal, but that - // doesn't matter, as this is just an optimization. - if rustc_data_structures::sync::Lrc::ptr_eq(last_source_file, &file) { - cached_file_id = Some(last_file_id); - } - } - - let file_id = if let Some(file_id) = cached_file_id { - file_id - } else { - debug_context.add_source_file(&file) - }; + let (file_id, line, column) = + debug_context.get_span_loc(self.tcx, self.mir.span, source_info.span); let source_loc = self.func_debug_cx.as_mut().unwrap().add_dbg_loc(file_id, line, column); diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 39fa277fedc55..cdf499a22f8dd 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -6,17 +6,16 @@ use cranelift_module::*; use rustc_data_structures::fx::FxHashSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{read_target_uint, AllocId, GlobalAlloc, Scalar}; -use rustc_middle::ty::ScalarInt; +use rustc_middle::ty::{Binder, ExistentialTraitRef, ScalarInt}; use crate::prelude::*; pub(crate) struct ConstantCx { todo: Vec, - done: FxHashSet, anon_allocs: FxHashMap, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] enum TodoItem { Alloc(AllocId), Static(DefId), @@ -24,19 +23,24 @@ enum TodoItem { impl ConstantCx { pub(crate) fn new() -> Self { - ConstantCx { todo: vec![], done: FxHashSet::default(), anon_allocs: FxHashMap::default() } + ConstantCx { todo: vec![], anon_allocs: FxHashMap::default() } } pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut dyn Module) { define_all_allocs(tcx, module, &mut self); - self.done.clear(); } } -pub(crate) fn codegen_static(tcx: TyCtxt<'_>, module: &mut dyn Module, def_id: DefId) { +pub(crate) fn codegen_static(tcx: TyCtxt<'_>, module: &mut dyn Module, def_id: DefId) -> DataId { let mut constants_cx = ConstantCx::new(); constants_cx.todo.push(TodoItem::Static(def_id)); constants_cx.finalize(tcx, module); + + data_id_for_static( + tcx, module, def_id, false, + // For a declaration the stated mutability doesn't matter. + false, + ) } pub(crate) fn codegen_tls_ref<'tcx>( @@ -74,7 +78,7 @@ pub(crate) fn eval_mir_constant<'tcx>( let cv = fx.monomorphize(constant.const_); // This cannot fail because we checked all required_consts in advance. let val = cv - .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(constant.span)) + .eval(fx.tcx, ty::ParamEnv::reveal_all(), constant.span) .expect("erroneous constant missed by mono item collection"); (val, cv.ty()) } @@ -106,7 +110,7 @@ pub(crate) fn codegen_const_value<'tcx>( if fx.clif_type(layout.ty).is_some() { return CValue::const_val(fx, layout, int); } else { - let raw_val = int.size().truncate(int.to_bits(int.size()).unwrap()); + let raw_val = int.size().truncate(int.assert_bits(int.size())); let val = match int.size().bytes() { 1 => fx.bcx.ins().iconst(types::I8, raw_val as i64), 2 => fx.bcx.ins().iconst(types::I16, raw_val as i64), @@ -133,18 +137,23 @@ pub(crate) fn codegen_const_value<'tcx>( let alloc_id = prov.alloc_id(); let base_addr = match fx.tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => { - let data_id = data_id_for_alloc_id( - &mut fx.constants_cx, - fx.module, - alloc_id, - alloc.inner().mutability, - ); - let local_data_id = - fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); - if fx.clif_comments.enabled() { - fx.add_comment(local_data_id, format!("{:?}", alloc_id)); + if alloc.inner().len() == 0 { + assert_eq!(offset, Size::ZERO); + fx.bcx.ins().iconst(fx.pointer_type, alloc.inner().align.bytes() as i64) + } else { + let data_id = data_id_for_alloc_id( + &mut fx.constants_cx, + fx.module, + alloc_id, + alloc.inner().mutability, + ); + let local_data_id = + fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + if fx.clif_comments.enabled() { + fx.add_comment(local_data_id, format!("{:?}", alloc_id)); + } + fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } - fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } GlobalAlloc::Function(instance) => { let func_id = crate::abi::import_function(fx.tcx, fx.module, instance); @@ -153,14 +162,12 @@ pub(crate) fn codegen_const_value<'tcx>( fx.bcx.ins().func_addr(fx.pointer_type, local_func_id) } GlobalAlloc::VTable(ty, trait_ref) => { - let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref)); - let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory(); - // FIXME: factor this common code with the `Memory` arm into a function? - let data_id = data_id_for_alloc_id( + let data_id = data_id_for_vtable( + fx.tcx, &mut fx.constants_cx, fx.module, - alloc_id, - alloc.inner().mutability, + ty, + trait_ref, ); let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); @@ -208,12 +215,8 @@ fn pointer_for_allocation<'tcx>( alloc_id: AllocId, ) -> crate::pointer::Pointer { let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory(); - let data_id = data_id_for_alloc_id( - &mut fx.constants_cx, - &mut *fx.module, - alloc_id, - alloc.inner().mutability, - ); + let data_id = + data_id_for_alloc_id(&mut fx.constants_cx, fx.module, alloc_id, alloc.inner().mutability); let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); if fx.clif_comments.enabled() { @@ -235,6 +238,17 @@ pub(crate) fn data_id_for_alloc_id( .or_insert_with(|| module.declare_anonymous_data(mutability.is_mut(), false).unwrap()) } +pub(crate) fn data_id_for_vtable<'tcx>( + tcx: TyCtxt<'tcx>, + cx: &mut ConstantCx, + module: &mut dyn Module, + ty: Ty<'tcx>, + trait_ref: Option>>, +) -> DataId { + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not) +} + fn data_id_for_static( tcx: TyCtxt<'_>, module: &mut dyn Module, @@ -327,7 +341,12 @@ fn data_id_for_static( } fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut ConstantCx) { + let mut done = FxHashSet::default(); while let Some(todo_item) = cx.todo.pop() { + if !done.insert(todo_item) { + continue; + } + let (data_id, alloc, section_name) = match todo_item { TodoItem::Alloc(alloc_id) => { let alloc = match tcx.global_alloc(alloc_id) { @@ -358,10 +377,6 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant } }; - if cx.done.contains(&data_id) { - continue; - } - let mut data = DataDescription::new(); let alloc = alloc.inner(); data.set_align(alloc.align.bytes()); @@ -384,13 +399,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant } let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec(); - if bytes.is_empty() { - // FIXME(bytecodealliance/wasmtime#7918) cranelift-jit has a bug where it causes UB on - // empty data objects - data.define(Box::new([0])); - } else { - data.define(bytes.into_boxed_slice()); - } + data.define(bytes.into_boxed_slice()); for &(offset, prov) in alloc.provenance().ptrs().iter() { let alloc_id = prov.alloc_id(); @@ -418,8 +427,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability) } GlobalAlloc::VTable(ty, trait_ref) => { - let alloc_id = tcx.vtable_allocation((ty, trait_ref)); - data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not) + data_id_for_vtable(tcx, cx, module, ty, trait_ref) } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) @@ -446,7 +454,6 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant } module.define_data(data_id, &data).unwrap(); - cx.done.insert(data_id); } assert!(cx.todo.is_empty(), "{:?}", cx.todo); @@ -489,27 +496,24 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( return None; } let scalar_int = mir_operand_get_const_val(fx, operand)?; - let scalar_int = match fx - .layout_of(*ty) - .size - .cmp(&scalar_int.size()) - { - Ordering::Equal => scalar_int, - Ordering::Less => match ty.kind() { - ty::Uint(_) => ScalarInt::try_from_uint( - scalar_int.try_to_uint(scalar_int.size()).unwrap(), - fx.layout_of(*ty).size, - ) - .unwrap(), - ty::Int(_) => ScalarInt::try_from_int( - scalar_int.try_to_int(scalar_int.size()).unwrap(), - fx.layout_of(*ty).size, - ) - .unwrap(), - _ => unreachable!(), - }, - Ordering::Greater => return None, - }; + let scalar_int = + match fx.layout_of(*ty).size.cmp(&scalar_int.size()) { + Ordering::Equal => scalar_int, + Ordering::Less => match ty.kind() { + ty::Uint(_) => ScalarInt::try_from_uint( + scalar_int.assert_uint(scalar_int.size()), + fx.layout_of(*ty).size, + ) + .unwrap(), + ty::Int(_) => ScalarInt::try_from_int( + scalar_int.assert_int(scalar_int.size()), + fx.layout_of(*ty).size, + ) + .unwrap(), + _ => unreachable!(), + }, + Ordering::Greater => return None, + }; computed_scalar_int = Some(scalar_int); } Rvalue::Use(operand) => { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs index 81b819a554647..36af7d4450d04 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs @@ -1,5 +1,6 @@ //! Write the debuginfo into an object file. +use cranelift_module::{DataId, FuncId}; use cranelift_object::ObjectProduct; use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer}; use gimli::{RunTimeEndian, SectionId}; @@ -8,6 +9,18 @@ use rustc_data_structures::fx::FxHashMap; use super::object::WriteDebugInfo; use super::DebugContext; +pub(super) fn address_for_func(func_id: FuncId) -> Address { + let symbol = func_id.as_u32(); + assert!(symbol & 1 << 31 == 0); + Address::Symbol { symbol: symbol as usize, addend: 0 } +} + +pub(super) fn address_for_data(data_id: DataId) -> Address { + let symbol = data_id.as_u32(); + assert!(symbol & 1 << 31 == 0); + Address::Symbol { symbol: (symbol | 1 << 31) as usize, addend: 0 } +} + impl DebugContext { pub(crate) fn emit(&mut self, product: &mut ObjectProduct) { let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone()); @@ -171,6 +184,7 @@ impl Writer for WriterRelocate { gimli::DW_EH_PE_pcrel => { let size = match eh_pe.format() { gimli::DW_EH_PE_sdata4 => 4, + gimli::DW_EH_PE_sdata8 => 8, _ => return Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)), }; self.relocs.push(DebugReloc { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index d1b21d0a0b6c5..78b3d5a58f4a2 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -5,14 +5,12 @@ use std::path::{Component, Path}; use cranelift_codegen::binemit::CodeOffset; use cranelift_codegen::MachSrcLoc; -use gimli::write::{ - Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable, -}; -use rustc_data_structures::sync::Lrc; +use gimli::write::{AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable}; use rustc_span::{ - FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm, + hygiene, FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm, }; +use crate::debuginfo::emit::address_for_func; use crate::debuginfo::FunctionDebugContext; use crate::prelude::*; @@ -60,72 +58,64 @@ fn make_file_info(hash: SourceFileHash) -> Option { impl DebugContext { pub(crate) fn get_span_loc( + &mut self, tcx: TyCtxt<'_>, function_span: Span, span: Span, - ) -> (Lrc, u64, u64) { - // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131 - // In order to have a good line stepping behavior in debugger, we overwrite debug - // locations of macro expansions with that of the outermost expansion site (when the macro is - // annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). - let span = tcx.collapsed_debuginfo(span, function_span); + ) -> (FileId, u64, u64) { + // Match behavior of `FunctionCx::adjusted_span_and_dbg_scope`. + let span = hygiene::walk_chain_collapsed(span, function_span); match tcx.sess.source_map().lookup_line(span.lo()) { Ok(SourceFileAndLine { sf: file, line }) => { + let file_id = self.add_source_file(&file); let line_pos = file.lines()[line]; let col = file.relative_position(span.lo()) - line_pos; - (file, u64::try_from(line).unwrap() + 1, u64::from(col.to_u32()) + 1) + (file_id, u64::try_from(line).unwrap() + 1, u64::from(col.to_u32()) + 1) } - Err(file) => (file, 0, 0), + Err(file) => (self.add_source_file(&file), 0, 0), } } pub(crate) fn add_source_file(&mut self, source_file: &SourceFile) -> FileId { - let line_program: &mut LineProgram = &mut self.dwarf.unit.line_program; - let line_strings: &mut LineStringTable = &mut self.dwarf.line_strings; - - match &source_file.name { - FileName::Real(path) => { - let (dir_path, file_name) = - split_path_dir_and_file(if self.should_remap_filepaths { - path.remapped_path_if_available() + let cache_key = (source_file.stable_id, source_file.src_hash); + *self.created_files.entry(cache_key).or_insert_with(|| { + let line_program: &mut LineProgram = &mut self.dwarf.unit.line_program; + let line_strings: &mut LineStringTable = &mut self.dwarf.line_strings; + + match &source_file.name { + FileName::Real(path) => { + let (dir_path, file_name) = + split_path_dir_and_file(path.to_path(self.filename_display_preference)); + let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); + let file_name = osstr_as_utf8_bytes(file_name); + + let dir_id = if !dir_name.is_empty() { + let dir_name = + LineString::new(dir_name, line_program.encoding(), line_strings); + line_program.add_directory(dir_name) } else { - path.local_path_if_available() - }); - let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); - let file_name = osstr_as_utf8_bytes(file_name); - - let dir_id = if !dir_name.is_empty() { - let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings); - line_program.add_directory(dir_name) - } else { - line_program.default_directory() - }; - let file_name = LineString::new(file_name, line_program.encoding(), line_strings); - - let info = make_file_info(source_file.src_hash); - - line_program.file_has_md5 &= info.is_some(); - line_program.add_file(file_name, dir_id, info) + line_program.default_directory() + }; + let file_name = + LineString::new(file_name, line_program.encoding(), line_strings); + + let info = make_file_info(source_file.src_hash); + + line_program.file_has_md5 &= info.is_some(); + line_program.add_file(file_name, dir_id, info) + } + filename => { + let dir_id = line_program.default_directory(); + let dummy_file_name = LineString::new( + filename.display(self.filename_display_preference).to_string().into_bytes(), + line_program.encoding(), + line_strings, + ); + line_program.add_file(dummy_file_name, dir_id, None) + } } - // FIXME give more appropriate file names - filename => { - let dir_id = line_program.default_directory(); - let dummy_file_name = LineString::new( - filename - .display(if self.should_remap_filepaths { - FileNameDisplayPreference::Remapped - } else { - FileNameDisplayPreference::Local - }) - .to_string() - .into_bytes(), - line_program.encoding(), - line_strings, - ); - line_program.add_file(dummy_file_name, dir_id, None) - } - } + }) } } @@ -138,7 +128,7 @@ impl FunctionDebugContext { pub(super) fn create_debug_lines( &mut self, debug_context: &mut DebugContext, - symbol: usize, + func_id: FuncId, context: &Context, ) -> CodeOffset { let create_row_for_span = @@ -151,11 +141,7 @@ impl FunctionDebugContext { debug_context.dwarf.unit.line_program.generate_row(); }; - debug_context - .dwarf - .unit - .line_program - .begin_sequence(Some(Address::Symbol { symbol, addend: 0 })); + debug_context.dwarf.unit.line_program.begin_sequence(Some(address_for_func(func_id))); let mut func_end = 0; @@ -178,10 +164,7 @@ impl FunctionDebugContext { assert_ne!(func_end, 0); let entry = debug_context.dwarf.unit.get_mut(self.entry_id); - entry.set( - gimli::DW_AT_low_pc, - AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), - ); + entry.set(gimli::DW_AT_low_pc, AttributeValue::Address(address_for_func(func_id))); entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(func_end))); func_end diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 2d9c2ecdbc2be..f0b78e5d7c674 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -3,20 +3,29 @@ mod emit; mod line_info; mod object; +mod types; mod unwind; use cranelift_codegen::ir::Endianness; use cranelift_codegen::isa::TargetIsa; +use cranelift_module::DataId; use gimli::write::{ - Address, AttributeValue, DwarfUnit, FileId, LineProgram, LineString, Range, RangeList, - UnitEntryId, + Address, AttributeValue, DwarfUnit, Expression, FileId, LineProgram, LineString, Range, + RangeList, UnitEntryId, }; -use gimli::{Encoding, Format, LineEncoding, RunTimeEndian}; +use gimli::{AArch64, Encoding, Format, LineEncoding, Register, RiscV, RunTimeEndian, X86_64}; use indexmap::IndexSet; +use rustc_codegen_ssa::debuginfo::type_names; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefIdMap; use rustc_session::Session; +use rustc_span::{FileNameDisplayPreference, SourceFileHash, StableSourceFileId}; +use rustc_target::abi::call::FnAbi; pub(crate) use self::emit::{DebugReloc, DebugRelocName}; +pub(crate) use self::types::TypeDebugContext; pub(crate) use self::unwind::UnwindContext; +use crate::debuginfo::emit::{address_for_data, address_for_func}; use crate::prelude::*; pub(crate) fn producer(sess: &Session) -> String { @@ -28,8 +37,12 @@ pub(crate) struct DebugContext { dwarf: DwarfUnit, unit_range_list: RangeList, + created_files: FxHashMap<(StableSourceFileId, SourceFileHash), FileId>, + stack_pointer_register: Register, + namespace_map: DefIdMap, + array_size_type: UnitEntryId, - should_remap_filepaths: bool, + filename_display_preference: FileNameDisplayPreference, } pub(crate) struct FunctionDebugContext { @@ -39,7 +52,7 @@ pub(crate) struct FunctionDebugContext { } impl DebugContext { - pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa) -> Self { + pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa, cgu_name: &str) -> Self { let encoding = Encoding { format: Format::Dwarf32, // FIXME this should be configurable @@ -60,24 +73,29 @@ impl DebugContext { Endianness::Big => RunTimeEndian::Big, }; + let stack_pointer_register = match isa.triple().architecture { + target_lexicon::Architecture::Aarch64(_) => AArch64::SP, + target_lexicon::Architecture::Riscv64(_) => RiscV::SP, + target_lexicon::Architecture::X86_64 | target_lexicon::Architecture::X86_64h => { + X86_64::RSP + } + _ => Register(u16::MAX), + }; + let mut dwarf = DwarfUnit::new(encoding); - let should_remap_filepaths = tcx.sess.should_prefer_remapped_for_codegen(); + use rustc_session::config::RemapPathScopeComponents; + + let filename_display_preference = + tcx.sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO); let producer = producer(tcx.sess); - let comp_dir = tcx - .sess - .opts - .working_dir - .to_string_lossy(if should_remap_filepaths { - FileNameDisplayPreference::Remapped - } else { - FileNameDisplayPreference::Local - }) - .into_owned(); + let comp_dir = + tcx.sess.opts.working_dir.to_string_lossy(filename_display_preference).to_string(); + let (name, file_info) = match tcx.sess.local_crate_source_file() { Some(path) => { - let name = path.to_string_lossy().into_owned(); + let name = path.to_string_lossy(filename_display_preference).to_string(); (name, None) } None => (tcx.crate_name(LOCAL_CRATE).to_string(), None), @@ -95,7 +113,7 @@ impl DebugContext { dwarf.unit.line_program = line_program; { - let name = dwarf.strings.add(name); + let name = dwarf.strings.add(format!("{name}/@/{cgu_name}")); let comp_dir = dwarf.strings.add(comp_dir); let root = dwarf.unit.root(); @@ -103,41 +121,134 @@ impl DebugContext { root.set(gimli::DW_AT_producer, AttributeValue::StringRef(dwarf.strings.add(producer))); root.set(gimli::DW_AT_language, AttributeValue::Language(gimli::DW_LANG_Rust)); root.set(gimli::DW_AT_name, AttributeValue::StringRef(name)); + + // This will be replaced when emitting the debuginfo. It is only + // defined here to ensure that the order of the attributes matches + // rustc. + root.set(gimli::DW_AT_stmt_list, AttributeValue::Udata(0)); + root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir)); root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0))); } + let array_size_type = dwarf.unit.add(dwarf.unit.root(), gimli::DW_TAG_base_type); + let array_size_type_entry = dwarf.unit.get_mut(array_size_type); + array_size_type_entry.set( + gimli::DW_AT_name, + AttributeValue::StringRef(dwarf.strings.add("__ARRAY_SIZE_TYPE__")), + ); + array_size_type_entry + .set(gimli::DW_AT_encoding, AttributeValue::Encoding(gimli::DW_ATE_unsigned)); + array_size_type_entry.set( + gimli::DW_AT_byte_size, + AttributeValue::Udata(isa.frontend_config().pointer_bytes().into()), + ); + DebugContext { endian, dwarf, unit_range_list: RangeList(Vec::new()), - should_remap_filepaths, + created_files: FxHashMap::default(), + stack_pointer_register, + namespace_map: DefIdMap::default(), + array_size_type, + filename_display_preference, + } + } + + fn item_namespace(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> UnitEntryId { + if let Some(&scope) = self.namespace_map.get(&def_id) { + return scope; } + + let def_key = tcx.def_key(def_id); + let parent_scope = def_key + .parent + .map(|parent| self.item_namespace(tcx, DefId { krate: def_id.krate, index: parent })) + .unwrap_or(self.dwarf.unit.root()); + + let namespace_name = { + let mut output = String::new(); + type_names::push_item_name(tcx, def_id, false, &mut output); + output + }; + let namespace_name_id = self.dwarf.strings.add(namespace_name); + + let scope = self.dwarf.unit.add(parent_scope, gimli::DW_TAG_namespace); + let scope_entry = self.dwarf.unit.get_mut(scope); + scope_entry.set(gimli::DW_AT_name, AttributeValue::StringRef(namespace_name_id)); + + self.namespace_map.insert(def_id, scope); + scope } - pub(crate) fn define_function( + pub(crate) fn define_function<'tcx>( &mut self, - tcx: TyCtxt<'_>, - name: &str, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + instance: Instance<'tcx>, + fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, + linkage_name: &str, function_span: Span, ) -> FunctionDebugContext { - let (file, line, column) = DebugContext::get_span_loc(tcx, function_span, function_span); + let (file_id, line, column) = self.get_span_loc(tcx, function_span, function_span); + + let scope = self.item_namespace(tcx, tcx.parent(instance.def_id())); + + let mut name = String::new(); + type_names::push_item_name(tcx, instance.def_id(), false, &mut name); - let file_id = self.add_source_file(&file); + // Find the enclosing function, in case this is a closure. + let enclosing_fn_def_id = tcx.typeck_root_def_id(instance.def_id()); - // FIXME: add to appropriate scope instead of root - let scope = self.dwarf.unit.root(); + // We look up the generics of the enclosing function and truncate the args + // to their length in order to cut off extra stuff that might be in there for + // closures or coroutines. + let generics = tcx.generics_of(enclosing_fn_def_id); + let args = instance.args.truncate_to(tcx, generics); + + type_names::push_generic_params( + tcx, + tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), args), + enclosing_fn_def_id, + &mut name, + ); let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_subprogram); let entry = self.dwarf.unit.get_mut(entry_id); + let linkage_name_id = + if name != linkage_name { Some(self.dwarf.strings.add(linkage_name)) } else { None }; let name_id = self.dwarf.strings.add(name); + + // These will be replaced in FunctionDebugContext::finalize. They are + // only defined here to ensure that the order of the attributes matches + // rustc. + entry.set(gimli::DW_AT_low_pc, AttributeValue::Udata(0)); + entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(0)); + + let mut frame_base_expr = Expression::new(); + frame_base_expr.op_reg(self.stack_pointer_register); + entry.set(gimli::DW_AT_frame_base, AttributeValue::Exprloc(frame_base_expr)); + + if let Some(linkage_name_id) = linkage_name_id { + entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(linkage_name_id)); + } // Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped. entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id)); - entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id)); entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(line)); - entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(column)); + + if !fn_abi.ret.is_ignore() { + let return_dw_ty = self.debug_type(tcx, type_dbg, fn_abi.ret.layout.ty); + let entry = self.dwarf.unit.get_mut(entry_id); + entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(return_dw_ty)); + } + + if tcx.is_reachable_non_generic(instance.def_id()) { + let entry = self.dwarf.unit.get_mut(entry_id); + entry.set(gimli::DW_AT_external, AttributeValue::FlagPresent); + } FunctionDebugContext { entry_id, @@ -145,6 +256,62 @@ impl DebugContext { source_loc_set: IndexSet::new(), } } + + // Adapted from https://github.com/rust-lang/rust/blob/10a7aa14fed9b528b74b0f098c4899c37c09a9c7/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs#L1288-L1346 + pub(crate) fn define_static<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + def_id: DefId, + data_id: DataId, + ) { + let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; + if nested { + return; + } + + let scope = self.item_namespace(tcx, tcx.parent(def_id)); + + let span = tcx.def_span(def_id); + let (file_id, line, _column) = self.get_span_loc(tcx, span, span); + + let static_type = Instance::mono(tcx, def_id).ty(tcx, ty::ParamEnv::reveal_all()); + let static_layout = tcx.layout_of(ty::ParamEnv::reveal_all().and(static_type)).unwrap(); + // FIXME use the actual type layout + let type_id = self.debug_type(tcx, type_dbg, static_type); + + let name = tcx.item_name(def_id); + let linkage_name = tcx.symbol_name(Instance::mono(tcx, def_id)).name; + + let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_variable); + let entry = self.dwarf.unit.get_mut(entry_id); + let linkage_name_id = if name.as_str() != linkage_name { + Some(self.dwarf.strings.add(linkage_name)) + } else { + None + }; + let name_id = self.dwarf.strings.add(name.as_str()); + + entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id)); + entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(type_id)); + + if tcx.is_reachable_non_generic(def_id) { + entry.set(gimli::DW_AT_external, AttributeValue::FlagPresent); + } + + entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); + entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(line)); + + entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(static_layout.align.pref.bytes())); + + let mut expr = Expression::new(); + expr.op_addr(address_for_data(data_id)); + entry.set(gimli::DW_AT_location, AttributeValue::Exprloc(expr)); + + if let Some(linkage_name_id) = linkage_name_id { + entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(linkage_name_id)); + } + } } impl FunctionDebugContext { @@ -154,21 +321,16 @@ impl FunctionDebugContext { func_id: FuncId, context: &Context, ) { - let symbol = func_id.as_u32() as usize; - - let end = self.create_debug_lines(debug_context, symbol, context); + let end = self.create_debug_lines(debug_context, func_id, context); - debug_context.unit_range_list.0.push(Range::StartLength { - begin: Address::Symbol { symbol, addend: 0 }, - length: u64::from(end), - }); + debug_context + .unit_range_list + .0 + .push(Range::StartLength { begin: address_for_func(func_id), length: u64::from(end) }); let func_entry = debug_context.dwarf.unit.get_mut(self.entry_id); // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped. - func_entry.set( - gimli::DW_AT_low_pc, - AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), - ); + func_entry.set(gimli::DW_AT_low_pc, AttributeValue::Address(address_for_func(func_id))); // Using Udata for DW_AT_high_pc requires at least DWARF4 func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end))); } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs index f1840a7bf7303..65f4c67b21f13 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs @@ -1,8 +1,8 @@ -use cranelift_module::FuncId; +use cranelift_module::{DataId, FuncId}; use cranelift_object::ObjectProduct; use gimli::SectionId; use object::write::{Relocation, StandardSegment}; -use object::{RelocationEncoding, SectionKind}; +use object::{RelocationEncoding, RelocationFlags, SectionKind}; use rustc_data_structures::fx::FxHashMap; use crate::debuginfo::{DebugReloc, DebugRelocName}; @@ -57,10 +57,13 @@ impl WriteDebugInfo for ObjectProduct { let (symbol, symbol_offset) = match reloc.name { DebugRelocName::Section(id) => (section_map.get(&id).unwrap().1, 0), DebugRelocName::Symbol(id) => { - let symbol_id = self.function_symbol(FuncId::from_u32(id.try_into().unwrap())); - self.object - .symbol_section_and_offset(symbol_id) - .expect("Debug reloc for undef sym???") + let id = id.try_into().unwrap(); + let symbol_id = if id & 1 << 31 == 0 { + self.function_symbol(FuncId::from_u32(id)) + } else { + self.data_symbol(DataId::from_u32(id & !(1 << 31))) + }; + self.object.symbol_section_and_offset(symbol_id).unwrap_or((symbol_id, 0)) } }; self.object @@ -69,9 +72,11 @@ impl WriteDebugInfo for ObjectProduct { Relocation { offset: u64::from(reloc.offset), symbol, - kind: reloc.kind, - encoding: RelocationEncoding::Generic, - size: reloc.size * 8, + flags: RelocationFlags::Generic { + kind: reloc.kind, + encoding: RelocationEncoding::Generic, + size: reloc.size * 8, + }, addend: i64::try_from(symbol_offset).unwrap() + reloc.addend, }, ) diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs new file mode 100644 index 0000000000000..7baf0a3868d2c --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs @@ -0,0 +1,204 @@ +// Adapted from https://github.com/rust-lang/rust/blob/10a7aa14fed9b528b74b0f098c4899c37c09a9c7/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs + +use gimli::write::{AttributeValue, UnitEntryId}; +use rustc_codegen_ssa::debuginfo::type_names; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +use crate::{has_ptr_meta, DebugContext, RevealAllLayoutCx}; + +#[derive(Default)] +pub(crate) struct TypeDebugContext<'tcx> { + type_map: FxHashMap, UnitEntryId>, +} + +/// Returns from the enclosing function if the type debuginfo node with the given +/// unique ID can be found in the type map. +macro_rules! return_if_type_created_in_meantime { + ($type_dbg:expr, $ty:expr) => { + if let Some(&type_id) = $type_dbg.type_map.get(&$ty) { + return type_id; + } + }; +} + +impl DebugContext { + pub(crate) fn debug_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + ty: Ty<'tcx>, + ) -> UnitEntryId { + if let Some(&type_id) = type_dbg.type_map.get(&ty) { + return type_id; + } + + let type_id = match ty.kind() { + ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { + self.basic_type(tcx, ty) + } + ty::Tuple(elems) if elems.is_empty() => self.basic_type(tcx, ty), + ty::Array(elem_ty, len) => self.array_type( + tcx, + type_dbg, + ty, + *elem_ty, + len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()), + ), + // ty::Slice(_) | ty::Str + // ty::Dynamic + // ty::Foreign + ty::RawPtr(pointee_type, _) | ty::Ref(_, pointee_type, _) => { + self.pointer_type(tcx, type_dbg, ty, *pointee_type) + } + // ty::Adt(def, args) if def.is_box() && args.get(1).map_or(true, |arg| cx.layout_of(arg.expect_ty()).is_1zst()) + // ty::FnDef(..) | ty::FnPtr(..) + // ty::Closure(..) + // ty::Adt(def, ..) + ty::Tuple(components) => self.tuple_type(tcx, type_dbg, ty, *components), + // ty::Param(_) + // FIXME implement remaining types and add unreachable!() to the fallback branch + _ => self.placeholder_for_type(tcx, type_dbg, ty), + }; + + type_dbg.type_map.insert(ty, type_id); + + type_id + } + + fn basic_type<'tcx>(&mut self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> UnitEntryId { + let (name, encoding) = match ty.kind() { + ty::Never => ("!", gimli::DW_ATE_unsigned), + ty::Tuple(elems) if elems.is_empty() => ("()", gimli::DW_ATE_unsigned), + ty::Bool => ("bool", gimli::DW_ATE_boolean), + ty::Char => ("char", gimli::DW_ATE_UTF), + ty::Int(int_ty) => (int_ty.name_str(), gimli::DW_ATE_signed), + ty::Uint(uint_ty) => (uint_ty.name_str(), gimli::DW_ATE_unsigned), + ty::Float(float_ty) => (float_ty.name_str(), gimli::DW_ATE_float), + _ => unreachable!(), + }; + + let type_id = self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_base_type); + let type_entry = self.dwarf.unit.get_mut(type_id); + type_entry.set(gimli::DW_AT_name, AttributeValue::StringRef(self.dwarf.strings.add(name))); + type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(encoding)); + type_entry.set( + gimli::DW_AT_byte_size, + AttributeValue::Udata(RevealAllLayoutCx(tcx).layout_of(ty).size.bytes()), + ); + + type_id + } + + fn array_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + array_ty: Ty<'tcx>, + elem_ty: Ty<'tcx>, + len: u64, + ) -> UnitEntryId { + let elem_dw_ty = self.debug_type(tcx, type_dbg, elem_ty); + + return_if_type_created_in_meantime!(type_dbg, array_ty); + + let array_type_id = self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_array_type); + let array_type_entry = self.dwarf.unit.get_mut(array_type_id); + array_type_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(elem_dw_ty)); + + let subrange_id = self.dwarf.unit.add(array_type_id, gimli::DW_TAG_subrange_type); + let subrange_entry = self.dwarf.unit.get_mut(subrange_id); + subrange_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(self.array_size_type)); + subrange_entry.set(gimli::DW_AT_lower_bound, AttributeValue::Udata(0)); + subrange_entry.set(gimli::DW_AT_count, AttributeValue::Udata(len)); + + array_type_id + } + + fn pointer_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + ptr_type: Ty<'tcx>, + pointee_type: Ty<'tcx>, + ) -> UnitEntryId { + let pointee_dw_ty = self.debug_type(tcx, type_dbg, pointee_type); + + return_if_type_created_in_meantime!(type_dbg, ptr_type); + + let name = type_names::compute_debuginfo_type_name(tcx, ptr_type, true); + + if !has_ptr_meta(tcx, ptr_type) { + let pointer_type_id = + self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_pointer_type); + let pointer_entry = self.dwarf.unit.get_mut(pointer_type_id); + pointer_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(pointee_dw_ty)); + pointer_entry + .set(gimli::DW_AT_name, AttributeValue::StringRef(self.dwarf.strings.add(name))); + + pointer_type_id + } else { + // FIXME implement debuginfo for fat pointers + self.placeholder_for_type(tcx, type_dbg, ptr_type) + } + } + + fn tuple_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + tuple_type: Ty<'tcx>, + components: &'tcx [Ty<'tcx>], + ) -> UnitEntryId { + let components = components + .into_iter() + .map(|&ty| (ty, self.debug_type(tcx, type_dbg, ty))) + .collect::>(); + + return_if_type_created_in_meantime!(type_dbg, tuple_type); + + let name = type_names::compute_debuginfo_type_name(tcx, tuple_type, false); + let layout = RevealAllLayoutCx(tcx).layout_of(tuple_type); + + let tuple_type_id = + self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_structure_type); + let tuple_entry = self.dwarf.unit.get_mut(tuple_type_id); + tuple_entry.set(gimli::DW_AT_name, AttributeValue::StringRef(self.dwarf.strings.add(name))); + tuple_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes())); + tuple_entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(layout.align.pref.bytes())); + + for (i, (ty, dw_ty)) in components.into_iter().enumerate() { + let member_id = self.dwarf.unit.add(tuple_type_id, gimli::DW_TAG_member); + let member_entry = self.dwarf.unit.get_mut(member_id); + member_entry.set( + gimli::DW_AT_name, + AttributeValue::StringRef(self.dwarf.strings.add(format!("__{i}"))), + ); + member_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); + member_entry.set( + gimli::DW_AT_alignment, + AttributeValue::Udata(RevealAllLayoutCx(tcx).layout_of(ty).align.pref.bytes()), + ); + member_entry.set( + gimli::DW_AT_data_member_location, + AttributeValue::Udata(layout.fields.offset(i).bytes()), + ); + } + + tuple_type_id + } + + fn placeholder_for_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + ty: Ty<'tcx>, + ) -> UnitEntryId { + self.debug_type( + tcx, + type_dbg, + Ty::new_array(tcx, tcx.types.u8, RevealAllLayoutCx(tcx).layout_of(ty).size.bytes()), + ) + } +} diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index 35278e6fb29d3..eebd181341d08 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -3,9 +3,10 @@ use cranelift_codegen::ir::Endianness; use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa}; use cranelift_object::ObjectProduct; -use gimli::write::{Address, CieId, EhFrame, FrameTable, Section}; +use gimli::write::{CieId, EhFrame, FrameTable, Section}; use gimli::RunTimeEndian; +use super::emit::address_for_func; use super::object::WriteDebugInfo; use crate::prelude::*; @@ -37,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() { @@ -47,11 +56,8 @@ impl UnwindContext { match unwind_info { UnwindInfo::SystemV(unwind_info) => { - self.frame_table.add_fde( - self.cie_id.unwrap(), - unwind_info - .to_fde(Address::Symbol { symbol: func_id.as_u32() as usize, addend: 0 }), - ); + self.frame_table + .add_fde(self.cie_id.unwrap(), unwind_info.to_fde(address_for_func(func_id))); } UnwindInfo::WindowsX64(_) => { // FIXME implement this diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 757082a5fed1c..e8c96486041b1 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -1,25 +1,29 @@ //! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a //! standalone executable. -use std::fs::File; -use std::path::PathBuf; +use std::fs::{self, File}; +use std::path::{Path, PathBuf}; use std::sync::Arc; use std::thread::JoinHandle; use cranelift_object::{ObjectBuilder, ObjectModule}; use rustc_codegen_ssa::assert_module_sources::CguReuse; +use rustc_codegen_ssa::back::link::ensure_removed; use rustc_codegen_ssa::back::metadata::create_compressed_metadata_file; use rustc_codegen_ssa::base::determine_cgu_reuse; +use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_metadata::fs::copy_to_stdout; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; -use rustc_session::config::{DebugInfo, OutputFilenames, OutputType}; +use rustc_session::config::{DebugInfo, OutFileName, OutputFilenames, OutputType}; use rustc_session::Session; use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; +use crate::debuginfo::TypeDebugContext; use crate::global_asm::GlobalAsmConfig; use crate::{prelude::*, BackendConfig}; @@ -53,6 +57,7 @@ impl OngoingCodegen { pub(crate) fn join( self, sess: &Session, + outputs: &OutputFilenames, backend_config: &BackendConfig, ) -> (CodegenResults, FxIndexMap) { let mut work_products = FxIndexMap::default(); @@ -110,19 +115,185 @@ impl OngoingCodegen { sess.dcx().abort_if_errors(); - ( - CodegenResults { - modules, - allocator_module: self.allocator_module, - metadata_module: self.metadata_module, - metadata: self.metadata, - crate_info: self.crate_info, - }, - work_products, - ) + let codegen_results = CodegenResults { + modules, + allocator_module: self.allocator_module, + metadata_module: self.metadata_module, + metadata: self.metadata, + crate_info: self.crate_info, + }; + + produce_final_output_artifacts(sess, &codegen_results, outputs); + + (codegen_results, work_products) } } +// Adapted from https://github.com/rust-lang/rust/blob/73476d49904751f8d90ce904e16dfbc278083d2c/compiler/rustc_codegen_ssa/src/back/write.rs#L547C1-L706C2 +fn produce_final_output_artifacts( + sess: &Session, + codegen_results: &CodegenResults, + crate_output: &OutputFilenames, +) { + let user_wants_bitcode = false; + let mut user_wants_objects = false; + + // Produce final compile outputs. + let copy_gracefully = |from: &Path, to: &OutFileName| match to { + OutFileName::Stdout => { + if let Err(e) = copy_to_stdout(from) { + sess.dcx().emit_err(ssa_errors::CopyPath::new(from, to.as_path(), e)); + } + } + OutFileName::Real(path) => { + if let Err(e) = fs::copy(from, path) { + sess.dcx().emit_err(ssa_errors::CopyPath::new(from, path, e)); + } + } + }; + + let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| { + if codegen_results.modules.len() == 1 { + // 1) Only one codegen unit. In this case it's no difficulty + // to copy `foo.0.x` to `foo.x`. + let module_name = Some(&codegen_results.modules[0].name[..]); + let path = crate_output.temp_path(output_type, module_name); + let output = crate_output.path(output_type); + if !output_type.is_text_output() && output.is_tty() { + sess.dcx() + .emit_err(ssa_errors::BinaryOutputToTty { shorthand: output_type.shorthand() }); + } else { + copy_gracefully(&path, &output); + } + if !sess.opts.cg.save_temps && !keep_numbered { + // The user just wants `foo.x`, not `foo.#module-name#.x`. + ensure_removed(sess.dcx(), &path); + } + } else { + let extension = crate_output + .temp_path(output_type, None) + .extension() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + + if crate_output.outputs.contains_explicit_name(&output_type) { + // 2) Multiple codegen units, with `--emit foo=some_name`. We have + // no good solution for this case, so warn the user. + sess.dcx().emit_warn(ssa_errors::IgnoringEmitPath { extension }); + } else if crate_output.single_output_file.is_some() { + // 3) Multiple codegen units, with `-o some_name`. We have + // no good solution for this case, so warn the user. + sess.dcx().emit_warn(ssa_errors::IgnoringOutput { extension }); + } else { + // 4) Multiple codegen units, but no explicit name. We + // just leave the `foo.0.x` files in place. + // (We don't have to do any work in this case.) + } + } + }; + + // Flag to indicate whether the user explicitly requested bitcode. + // Otherwise, we produced it only as a temporary output, and will need + // to get rid of it. + for output_type in crate_output.outputs.keys() { + match *output_type { + OutputType::Bitcode => { + // Cranelift doesn't have bitcode + // user_wants_bitcode = true; + // // Copy to .bc, but always keep the .0.bc. There is a later + // // check to figure out if we should delete .0.bc files, or keep + // // them for making an rlib. + // copy_if_one_unit(OutputType::Bitcode, true); + } + OutputType::LlvmAssembly => { + // Cranelift IR text already emitted during codegen + // copy_if_one_unit(OutputType::LlvmAssembly, false); + } + OutputType::Assembly => { + // Currently no support for emitting raw assembly files + // copy_if_one_unit(OutputType::Assembly, false); + } + OutputType::Object => { + user_wants_objects = true; + copy_if_one_unit(OutputType::Object, true); + } + OutputType::Mir | OutputType::Metadata | OutputType::Exe | OutputType::DepInfo => {} + } + } + + // Clean up unwanted temporary files. + + // We create the following files by default: + // - #crate#.#module-name#.bc + // - #crate#.#module-name#.o + // - #crate#.crate.metadata.bc + // - #crate#.crate.metadata.o + // - #crate#.o (linked from crate.##.o) + // - #crate#.bc (copied from crate.##.bc) + // We may create additional files if requested by the user (through + // `-C save-temps` or `--emit=` flags). + + if !sess.opts.cg.save_temps { + // Remove the temporary .#module-name#.o objects. If the user didn't + // explicitly request bitcode (with --emit=bc), and the bitcode is not + // needed for building an rlib, then we must remove .#module-name#.bc as + // well. + + // Specific rules for keeping .#module-name#.bc: + // - If the user requested bitcode (`user_wants_bitcode`), and + // codegen_units > 1, then keep it. + // - If the user requested bitcode but codegen_units == 1, then we + // can toss .#module-name#.bc because we copied it to .bc earlier. + // - If we're not building an rlib and the user didn't request + // bitcode, then delete .#module-name#.bc. + // If you change how this works, also update back::link::link_rlib, + // where .#module-name#.bc files are (maybe) deleted after making an + // rlib. + let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); + + let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units().as_usize() > 1; + + let keep_numbered_objects = + needs_crate_object || (user_wants_objects && sess.codegen_units().as_usize() > 1); + + for module in codegen_results.modules.iter() { + if let Some(ref path) = module.object { + if !keep_numbered_objects { + ensure_removed(sess.dcx(), path); + } + } + + if let Some(ref path) = module.dwarf_object { + if !keep_numbered_objects { + ensure_removed(sess.dcx(), path); + } + } + + if let Some(ref path) = module.bytecode { + if !keep_numbered_bitcode { + ensure_removed(sess.dcx(), path); + } + } + } + + if !user_wants_bitcode { + if let Some(ref allocator_module) = codegen_results.allocator_module { + if let Some(ref path) = allocator_module.bytecode { + ensure_removed(sess.dcx(), path); + } + } + } + } + + // We leave the following files around by default: + // - #crate#.o + // - #crate#.crate.metadata.o + // - #crate#.bc + // These are used in linking steps and will be cleaned up afterward. +} + fn make_module(sess: &Session, backend_config: &BackendConfig, name: String) -> ObjectModule { let isa = crate::build_isa(sess, backend_config); @@ -170,6 +341,8 @@ fn emit_cgu( object: Some(global_asm_object_file), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, }), existing_work_product: None, }) @@ -207,7 +380,15 @@ fn emit_module( prof.artifact_size("object_file", &*name, file.metadata().unwrap().len()); - Ok(CompiledModule { name, kind, object: Some(tmp_file), dwarf_object: None, bytecode: None }) + Ok(CompiledModule { + name, + kind, + object: Some(tmp_file), + dwarf_object: None, + bytecode: None, + assembly: None, + llvm_ir: None, + }) } fn reuse_workproduct_for_cgu( @@ -255,6 +436,8 @@ fn reuse_workproduct_for_cgu( object: Some(obj_out_regular), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, }, module_global_asm: has_global_asm.then(|| CompiledModule { name: cgu.name().to_string(), @@ -262,6 +445,8 @@ fn reuse_workproduct_for_cgu( object: Some(obj_out_global_asm), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, }), existing_work_product: Some((cgu.work_product_id(), work_product)), }) @@ -290,6 +475,7 @@ fn module_codegen( tcx.sess.opts.debuginfo != DebugInfo::None, cgu_name, ); + let mut type_dbg = TypeDebugContext::default(); super::predefine_mono_items(tcx, &mut module, &mono_items); let mut codegened_functions = vec![]; for (mono_item, _) in mono_items { @@ -298,6 +484,7 @@ fn module_codegen( let codegened_function = crate::base::codegen_fn( tcx, &mut cx, + &mut type_dbg, Function::new(), &mut module, inst, @@ -305,7 +492,10 @@ fn module_codegen( codegened_functions.push(codegened_function); } MonoItem::Static(def_id) => { - crate::constant::codegen_static(tcx, &mut module, def_id) + let data_id = crate::constant::codegen_static(tcx, &mut module, def_id); + if let Some(debug_context) = &mut cx.debug_context { + debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); + } } MonoItem::GlobalAsm(item_id) => { crate::global_asm::codegen_global_asm_item( @@ -502,6 +692,8 @@ pub(crate) fn run_aot( object: Some(tmp_file), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, }) } else { None diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 6b2b946db02b6..929fa92596dc6 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -6,12 +6,14 @@ use std::ffi::CString; use std::os::raw::{c_char, c_int}; use std::sync::{mpsc, Mutex, OnceLock}; +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_jit::{JITBuilder, JITModule}; use rustc_codegen_ssa::CrateInfo; use rustc_middle::mir::mono::MonoItem; use rustc_session::Session; use rustc_span::Symbol; +use crate::debuginfo::TypeDebugContext; use crate::{prelude::*, BackendConfig}; use crate::{CodegenCx, CodegenMode}; @@ -229,7 +231,14 @@ pub(crate) fn codegen_and_compile_fn<'tcx>( crate::PrintOnPanic(|| format!("{:?} {}", instance, tcx.symbol_name(instance).name)); let cached_func = std::mem::replace(&mut cached_context.func, Function::new()); - let codegened_func = crate::base::codegen_fn(tcx, cx, cached_func, module, instance); + let codegened_func = crate::base::codegen_fn( + tcx, + cx, + &mut TypeDebugContext::default(), + cached_func, + module, + instance, + ); crate::base::compile_fn(cx, cached_context, module, codegened_func); }); diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 44650898de897..5a0cd3990f2a7 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -8,6 +8,7 @@ use std::sync::Arc; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::{InlineAsmOperand, ItemId}; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_session::config::{OutputFilenames, OutputType}; use rustc_target::asm::InlineAsmArch; @@ -32,18 +33,27 @@ pub(crate) fn codegen_global_asm_item(tcx: TyCtxt<'_>, global_asm: &mut String, InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: op_sp } => { match asm.operands[operand_idx].0 { InlineAsmOperand::Const { ref anon_const } => { - let const_value = - tcx.const_eval_poly(anon_const.def_id.to_def_id()).unwrap_or_else( - |_| span_bug!(op_sp, "asm const cannot be resolved"), - ); - let ty = tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); - let string = rustc_codegen_ssa::common::asm_const_to_str( - tcx, - op_sp, - const_value, - RevealAllLayoutCx(tcx).layout_of(ty), - ); - global_asm.push_str(&string); + match tcx.const_eval_poly(anon_const.def_id.to_def_id()) { + Ok(const_value) => { + let ty = tcx + .typeck_body(anon_const.body) + .node_type(anon_const.hir_id); + let string = rustc_codegen_ssa::common::asm_const_to_str( + tcx, + op_sp, + const_value, + RevealAllLayoutCx(tcx).layout_of(ty), + ); + global_asm.push_str(&string); + } + Err(ErrorHandled::Reported { .. }) => { + // An error has already been reported and compilation is + // guaranteed to fail if execution hits this path. + } + Err(ErrorHandled::TooGeneric(_)) => { + span_bug!(op_sp, "asm const cannot be resolved; too generic"); + } + } } InlineAsmOperand::SymFn { anon_const } => { if cfg!(not(feature = "inline_asm_sym")) { diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 171ee88a11c75..28b92f730da34 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -2,6 +2,7 @@ use std::fmt::Write; +use cranelift_codegen::isa::CallConv; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_span::sym; use rustc_target::asm::*; @@ -785,9 +786,9 @@ fn call_inline_asm<'tcx>( for (offset, place) in outputs { let ty = if place.layout().ty.is_simd() { let (lane_count, lane_type) = place.layout().ty.simd_size_and_type(fx.tcx); - fx.clif_type(lane_type).unwrap().by(lane_count.try_into().unwrap()).unwrap() + asm_clif_type(fx, lane_type).unwrap().by(lane_count.try_into().unwrap()).unwrap() } else { - fx.clif_type(place.layout().ty).unwrap() + asm_clif_type(fx, place.layout().ty).unwrap() }; let value = stack_slot.offset(fx, i32::try_from(offset.bytes()).unwrap().into()).load( fx, @@ -797,3 +798,24 @@ fn call_inline_asm<'tcx>( place.write_cvalue(fx, CValue::by_val(value, place.layout())); } } + +fn asm_clif_type<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> Option { + match ty.kind() { + // Adapted from https://github.com/rust-lang/rust/blob/f3c66088610c1b80110297c2d9a8b5f9265b013f/compiler/rustc_hir_analysis/src/check/intrinsicck.rs#L136-L151 + ty::Adt(adt, args) if Some(adt.did()) == fx.tcx.lang_items().maybe_uninit() => { + let fields = &adt.non_enum_variant().fields; + let ty = fields[FieldIdx::from_u32(1)].ty(fx.tcx, args); + let ty::Adt(ty, args) = ty.kind() else { + unreachable!("expected first field of `MaybeUninit` to be an ADT") + }; + assert!( + ty.is_manually_drop(), + "expected first field of `MaybeUninit` to be `ManuallyDrop`" + ); + let fields = &ty.non_enum_variant().fields; + let ty = fields[FieldIdx::ZERO].ty(fx.tcx, args); + fx.clif_type(ty) + } + _ => fx.clif_type(ty), + } +} diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 1615dc5de697b..8df83c706a100 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -1393,7 +1393,7 @@ fn llvm_add_sub<'tcx>( // c + carry -> c + first intermediate carry or borrow respectively let int0 = crate::num::codegen_checked_int_binop(fx, bin_op, a, b); - let c = int0.value_field(fx, FieldIdx::new(0)); + let c = int0.value_field(fx, FieldIdx::ZERO); let cb0 = int0.value_field(fx, FieldIdx::new(1)).load_scalar(fx); // c + carry -> c + second intermediate carry or borrow respectively diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 8b86a116df1fd..79a90507fa2e1 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -26,6 +26,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Symbol}; pub(crate) use self::llvm::codegen_llvm_intrinsic_call; +use crate::cast::clif_intcast; use crate::prelude::*; fn bug_on_incorrect_arg_count(intrinsic: impl std::fmt::Display) -> ! { @@ -341,6 +342,8 @@ fn codegen_float_intrinsic_call<'tcx>( sym::roundf64 => ("round", 1, fx.tcx.types.f64, types::F64), sym::roundevenf32 => ("roundevenf", 1, fx.tcx.types.f32, types::F32), sym::roundevenf64 => ("roundeven", 1, fx.tcx.types.f64, types::F64), + sym::nearbyintf32 => ("nearbyintf", 1, fx.tcx.types.f32, types::F32), + sym::nearbyintf64 => ("nearbyint", 1, fx.tcx.types.f64, types::F64), sym::sinf32 => ("sinf", 1, fx.tcx.types.f32, types::F32), sym::sinf64 => ("sin", 1, fx.tcx.types.f64, types::F64), sym::cosf32 => ("cosf", 1, fx.tcx.types.f32, types::F32), @@ -392,6 +395,8 @@ fn codegen_float_intrinsic_call<'tcx>( | sym::ceilf64 | sym::truncf32 | sym::truncf64 + | sym::nearbyintf32 + | sym::nearbyintf64 | sym::sqrtf32 | sym::sqrtf64 => { let val = match intrinsic { @@ -399,6 +404,7 @@ fn codegen_float_intrinsic_call<'tcx>( sym::floorf32 | sym::floorf64 => fx.bcx.ins().floor(args[0]), sym::ceilf32 | sym::ceilf64 => fx.bcx.ins().ceil(args[0]), sym::truncf32 | sym::truncf64 => fx.bcx.ins().trunc(args[0]), + sym::nearbyintf32 | sym::nearbyintf64 => fx.bcx.ins().nearest(args[0]), sym::sqrtf32 | sym::sqrtf64 => fx.bcx.ins().sqrt(args[0]), _ => unreachable!(), }; @@ -622,7 +628,8 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME trap on `ctlz_nonzero` with zero arg. let res = fx.bcx.ins().clz(val); - let res = CValue::by_val(res, arg.layout()); + let res = clif_intcast(fx, res, types::I32, false); + let res = CValue::by_val(res, ret.layout()); ret.write_cvalue(fx, res); } sym::cttz | sym::cttz_nonzero => { @@ -631,7 +638,8 @@ fn codegen_regular_intrinsic_call<'tcx>( // FIXME trap on `cttz_nonzero` with zero arg. let res = fx.bcx.ins().ctz(val); - let res = CValue::by_val(res, arg.layout()); + let res = clif_intcast(fx, res, types::I32, false); + let res = CValue::by_val(res, ret.layout()); ret.write_cvalue(fx, res); } sym::ctpop => { @@ -639,7 +647,8 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = arg.load_scalar(fx); let res = fx.bcx.ins().popcnt(val); - let res = CValue::by_val(res, arg.layout()); + let res = clif_intcast(fx, res, types::I32, false); + let res = CValue::by_val(res, ret.layout()); ret.write_cvalue(fx, res); } sym::bitreverse => { @@ -728,8 +737,10 @@ fn codegen_regular_intrinsic_call<'tcx>( | sym::variant_count => { intrinsic_args!(fx, args => (); intrinsic); - let const_val = - fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap(); + let const_val = fx + .tcx + .const_eval_instance(ParamEnv::reveal_all(), instance, source_info.span) + .unwrap(); let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty); ret.write_cvalue(fx, val); } @@ -755,13 +766,6 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, val); } - sym::ptr_guaranteed_cmp => { - intrinsic_args!(fx, args => (a, b); intrinsic); - - let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b).load_scalar(fx); - ret.write_cvalue(fx, CValue::by_val(val, fx.layout_of(fx.tcx.types.u8))); - } - sym::caller_location => { intrinsic_args!(fx, args => (); intrinsic); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 79da970a58efe..67f9d83106294 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -131,7 +131,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let idx = generic_args[2] .expect_const() - .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(span)) + .eval(fx.tcx, ty::ParamEnv::reveal_all(), span) .unwrap() .unwrap_branch(); @@ -965,7 +965,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }); } - sym::simd_expose_addr | sym::simd_from_exposed_addr | sym::simd_cast_ptr => { + sym::simd_expose_provenance | sym::simd_with_exposed_provenance | sym::simd_cast_ptr => { intrinsic_args!(fx, args => (arg); intrinsic); ret.write_cvalue_transmute(fx, arg); } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 7e2e1f7c6ac07..c9f84b6999718 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -21,6 +21,7 @@ extern crate rustc_hir; extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_metadata; +extern crate rustc_monomorphize; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; @@ -86,21 +87,17 @@ mod prelude { AbiParam, Block, FuncRef, Inst, InstBuilder, MemFlags, Signature, SourceLoc, StackSlot, StackSlotData, StackSlotKind, TrapCode, Type, Value, }; - pub(crate) use cranelift_codegen::isa::{self, CallConv}; pub(crate) use cranelift_codegen::Context; - pub(crate) use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; pub(crate) use cranelift_module::{self, DataDescription, FuncId, Linkage, Module}; pub(crate) use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; pub(crate) use rustc_hir::def_id::{DefId, LOCAL_CRATE}; pub(crate) use rustc_index::Idx; - pub(crate) use rustc_middle::bug; pub(crate) use rustc_middle::mir::{self, *}; - pub(crate) use rustc_middle::ty::layout::{self, LayoutOf, TyAndLayout}; + pub(crate) use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; pub(crate) use rustc_middle::ty::{ - self, FloatTy, Instance, InstanceDef, IntTy, ParamEnv, Ty, TyCtxt, TypeAndMut, - TypeFoldable, TypeVisitableExt, UintTy, + self, FloatTy, Instance, InstanceDef, IntTy, ParamEnv, Ty, TyCtxt, TypeAndMut, UintTy, }; - pub(crate) use rustc_span::{FileNameDisplayPreference, Span}; + pub(crate) use rustc_span::Span; pub(crate) use rustc_target::abi::{Abi, FieldIdx, Scalar, Size, VariantIdx, FIRST_VARIANT}; pub(crate) use crate::abi::*; @@ -147,7 +144,7 @@ impl CodegenCx { let unwind_context = UnwindContext::new(isa, matches!(backend_config.codegen_mode, CodegenMode::Aot)); let debug_context = if debug_info && !tcx.sess.target.options.is_like_windows { - Some(DebugContext::new(tcx, isa)) + Some(DebugContext::new(tcx, isa, cgu_name.as_str())) } else { None }; @@ -232,12 +229,13 @@ impl CodegenBackend for CraneliftCodegenBackend { &self, ongoing_codegen: Box, sess: &Session, - _outputs: &OutputFilenames, + outputs: &OutputFilenames, ) -> (CodegenResults, FxIndexMap) { - ongoing_codegen - .downcast::() - .unwrap() - .join(sess, self.config.borrow().as_ref().unwrap()) + ongoing_codegen.downcast::().unwrap().join( + sess, + outputs, + self.config.borrow().as_ref().unwrap(), + ) } fn link( @@ -259,7 +257,7 @@ fn target_triple(sess: &Session) -> target_lexicon::Triple { } } -fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc { +fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc { use target_lexicon::BinaryFormat; let target_triple = crate::target_triple(sess); diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index 1abfded8b11f0..1f20ec42ddb3e 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -1,3 +1,4 @@ +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_hir::LangItem; use rustc_middle::ty::AssocKind; use rustc_middle::ty::GenericArg; diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index 8992f40fb903a..714858084ec9d 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -40,6 +40,22 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { }) } +fn codegen_three_way_compare<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + signed: bool, + lhs: Value, + rhs: Value, +) -> CValue<'tcx> { + // This emits `(lhs > rhs) - (lhs < rhs)`, which is cranelift's preferred form per + // + let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed).unwrap(); + let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed).unwrap(); + let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs); + let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs); + let val = fx.bcx.ins().isub(gt, lt); + CValue::by_val(val, fx.layout_of(fx.tcx.ty_ordering_enum(Some(fx.mir.span)))) +} + fn codegen_compare_bin_op<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, @@ -47,6 +63,10 @@ fn codegen_compare_bin_op<'tcx>( lhs: Value, rhs: Value, ) -> CValue<'tcx> { + if bin_op == BinOp::Cmp { + return codegen_three_way_compare(fx, signed, lhs, rhs); + } + let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap(); let val = fx.bcx.ins().icmp(intcc, lhs, rhs); CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)) @@ -59,7 +79,7 @@ pub(crate) fn codegen_binop<'tcx>( in_rhs: CValue<'tcx>, ) -> CValue<'tcx> { match bin_op { - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => { match in_lhs.layout().ty.kind() { ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { let signed = type_sign(in_lhs.layout().ty); @@ -110,7 +130,7 @@ pub(crate) fn codegen_int_binop<'tcx>( in_lhs: CValue<'tcx>, in_rhs: CValue<'tcx>, ) -> CValue<'tcx> { - if bin_op != BinOp::Shl && bin_op != BinOp::Shr { + if !matches!(bin_op, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked) { assert_eq!( in_lhs.layout().ty, in_rhs.layout().ty, @@ -160,7 +180,7 @@ pub(crate) fn codegen_int_binop<'tcx>( } BinOp::Offset => unreachable!("Offset is not an integer operation"), // Compare binops handles by `codegen_binop`. - BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => { + BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Cmp => { unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty); } }; diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 7b61dc64cb1ae..f33bacb99a3fa 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -70,10 +70,8 @@ fn unsize_ptr<'tcx>( ) -> (Value, Value) { match (&src_layout.ty.kind(), &dst_layout.ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) - | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) - | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - (src, unsized_info(fx, *a, *b, old_info)) - } + | (&ty::Ref(_, a, _), &ty::RawPtr(b, _)) + | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => (src, unsized_info(fx, *a, *b, old_info)), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index f016e6950d48d..dded6df7771df 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -2,6 +2,7 @@ use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::immediates::Offset32; +use cranelift_frontend::Variable; use rustc_middle::ty::FnSig; use crate::prelude::*; @@ -326,7 +327,7 @@ impl<'tcx> CValue<'tcx> { let val = match layout.ty.kind() { ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { - let const_val = const_val.to_bits(layout.size).unwrap(); + let const_val = const_val.assert_bits(layout.size); let lsb = fx.bcx.ins().iconst(types::I64, const_val as u64 as i64); let msb = fx.bcx.ins().iconst(types::I64, (const_val >> 64) as u64 as i64); fx.bcx.ins().iconcat(lsb, msb) @@ -338,7 +339,7 @@ impl<'tcx> CValue<'tcx> { | ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) => { - let raw_val = const_val.size().truncate(const_val.to_bits(layout.size).unwrap()); + let raw_val = const_val.size().truncate(const_val.assert_bits(layout.size)); fx.bcx.ins().iconst(clif_ty, raw_val as i64) } ty::Float(FloatTy::F32) => { @@ -865,15 +866,10 @@ pub(crate) fn assert_assignable<'tcx>( return; } match (from_ty.kind(), to_ty.kind()) { - (ty::Ref(_, a, _), ty::Ref(_, b, _)) - | ( - ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), - ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }), - ) => { + (ty::Ref(_, a, _), ty::Ref(_, b, _)) | (ty::RawPtr(a, _), ty::RawPtr(b, _)) => { assert_assignable(fx, *a, *b, limit - 1); } - (ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ })) - | (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => { + (ty::Ref(_, a, _), ty::RawPtr(b, _)) | (ty::RawPtr(a, _), ty::Ref(_, b, _)) => { assert_assignable(fx, *a, *b, limit - 1); } (ty::FnPtr(_), ty::FnPtr(_)) => { diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index d2254d4c15e6f..04e24320f9131 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -2,7 +2,7 @@ //! //! See `rustc_codegen_ssa/src/meth.rs` for reference. -use crate::constant::data_id_for_alloc_id; +use crate::constant::data_id_for_vtable; use crate::prelude::*; pub(crate) fn vtable_memflags() -> MemFlags { @@ -61,7 +61,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( if ty.is_dyn_star() { let inner_layout = fx.layout_of(arg.layout().ty.builtin_deref(true).unwrap().ty); let dyn_star = CPlace::for_ptr(Pointer::new(arg.load_scalar(fx)), inner_layout); - let ptr = dyn_star.place_field(fx, FieldIdx::new(0)).to_ptr(); + let ptr = dyn_star.place_field(fx, FieldIdx::ZERO).to_ptr(); let vtable = dyn_star.place_field(fx, FieldIdx::new(1)).to_cvalue(fx).load_scalar(fx); break 'block (ptr, vtable); @@ -92,12 +92,10 @@ pub(crate) fn get_vtable<'tcx>( ty: Ty<'tcx>, trait_ref: Option>, ) -> Value { - let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref)); - let data_id = - data_id_for_alloc_id(&mut fx.constants_cx, &mut *fx.module, alloc_id, Mutability::Not); + let data_id = data_id_for_vtable(fx.tcx, &mut fx.constants_cx, fx.module, ty, trait_ref); let local_data_id = fx.module.declare_data_in_func(data_id, fx.bcx.func); if fx.clif_comments.enabled() { - fx.add_comment(local_data_id, format!("vtable: {:?}", alloc_id)); + fx.add_comment(local_data_id, "vtable"); } fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index ab2c7ca8a47c8..3ecb0ef6b4d29 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -79,16 +79,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "1.0.0" -source = "git+https://github.com/antoyo/gccjit.rs#9f8f67edc006d543b17529a001803ffece48349e" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecaa4c3da2d74c1a991b4faff75d49ab1d0522d9a99d8e2614b3b04d226417ce" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "0.0.1" -source = "git+https://github.com/antoyo/gccjit.rs#9f8f67edc006d543b17529a001803ffece48349e" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406a66fba005f1a02661f2f9443e5693dd3a667b7c58e70aa4ccc4c8b50b4758" dependencies = [ "libc", ] diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 100c10ef1d7af..c5aa2eed1e004 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -22,7 +22,7 @@ master = ["gccjit/master"] default = ["master"] [dependencies] -gccjit = { git = "https://github.com/antoyo/gccjit.rs" } +gccjit = "2.0" # Local copy. #gccjit = { path = "../gccjit.rs" } diff --git a/compiler/rustc_codegen_gcc/example/example.rs b/compiler/rustc_codegen_gcc/example/example.rs index 5878e8548d926..7c21b73b630e8 100644 --- a/compiler/rustc_codegen_gcc/example/example.rs +++ b/compiler/rustc_codegen_gcc/example/example.rs @@ -153,9 +153,10 @@ fn array_as_slice(arr: &[u8; 3]) -> &[u8] { arr } -unsafe fn use_ctlz_nonzero(a: u16) -> u16 { - intrinsics::ctlz_nonzero(a) -} +// FIXME: fix the intrinsic implementation to work with the new ->u32 signature +// unsafe fn use_ctlz_nonzero(a: u16) -> u32 { +// intrinsics::ctlz_nonzero(a) +// } fn ptr_as_usize(ptr: *const u8) -> usize { ptr as usize diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index a868471ed1d4d..a48c0a4450c29 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -418,6 +418,35 @@ pub fn panic(_msg: &'static str) -> ! { } } +macro_rules! panic_const { + ($($lang:ident = $message:expr,)+) => { + pub mod panic_const { + use super::*; + + $( + #[track_caller] + #[lang = stringify!($lang)] + pub fn $lang() -> ! { + panic($message); + } + )+ + } + } +} + +panic_const! { + panic_const_add_overflow = "attempt to add with overflow", + panic_const_sub_overflow = "attempt to subtract with overflow", + panic_const_mul_overflow = "attempt to multiply with overflow", + panic_const_div_overflow = "attempt to divide with overflow", + panic_const_rem_overflow = "attempt to calculate the remainder with overflow", + panic_const_neg_overflow = "attempt to negate with overflow", + panic_const_shr_overflow = "attempt to shift right with overflow", + panic_const_shl_overflow = "attempt to shift left with overflow", + panic_const_div_by_zero = "attempt to divide by zero", + panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", +} + #[lang = "panic_cannot_unwind"] fn panic_cannot_unwind() -> ! { unsafe { @@ -563,7 +592,7 @@ pub mod intrinsics { pub fn min_align_of_val(val: *const T) -> usize; pub fn copy(src: *const T, dst: *mut T, count: usize); pub fn transmute(e: T) -> U; - pub fn ctlz_nonzero(x: T) -> T; + pub fn ctlz_nonzero(x: T) -> u32; #[rustc_safe_intrinsic] pub fn needs_drop() -> bool; #[rustc_safe_intrinsic] diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index add77880716c8..5a7ddc4cd7fa5 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -2,7 +2,7 @@ #![feature( no_core, unboxed_closures, start, lang_items, never_type, linkage, - extern_types, thread_local + extern_types, thread_local, raw_ref_op )] #![no_core] #![allow(dead_code, internal_features, non_camel_case_types)] @@ -99,9 +99,7 @@ fn start( static mut NUM: u8 = 6 * 7; -// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint -#[allow(static_mut_refs)] -static NUM_REF: &'static u8 = unsafe { &NUM }; +static NUM_REF: &'static u8 = unsafe { &* &raw const NUM }; macro_rules! assert { ($e:expr) => { diff --git a/compiler/rustc_codegen_gcc/example/std_example.rs b/compiler/rustc_codegen_gcc/example/std_example.rs index ad69409eb6590..8ab8fcc525e5c 100644 --- a/compiler/rustc_codegen_gcc/example/std_example.rs +++ b/compiler/rustc_codegen_gcc/example/std_example.rs @@ -1,5 +1,5 @@ #![allow(internal_features)] -#![feature(core_intrinsics, coroutines, coroutine_trait, is_sorted)] +#![feature(core_intrinsics, coroutines, coroutine_trait, is_sorted, stmt_expr_attributes)] #[cfg(feature="master")] #[cfg(target_arch="x86_64")] @@ -103,7 +103,7 @@ fn main() { test_simd(); } - Box::pin(move |mut _task_context| { + Box::pin(#[coroutine] move |mut _task_context| { yield (); }).as_mut().resume(0); diff --git a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch index 914ae986b50e7..36d0789d2a23b 100644 --- a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch +++ b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch @@ -14,7 +14,7 @@ index d0a119c..76fdece 100644 @@ -89,7 +89,6 @@ #![feature(never_type)] #![feature(unwrap_infallible)] - #![feature(pointer_is_aligned)] + #![feature(pointer_is_aligned_to)] -#![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(lazy_cell)] @@ -27,6 +27,6 @@ index d0a119c..76fdece 100644 mod slice; mod str; mod str_lossy; --- +-- 2.42.1 diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 9b679019e96ce..06b14a1f118a3 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -541,7 +541,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) }; - self.call(self.type_void(), None, None, builtin_unreachable, &[], None); + self.call(self.type_void(), None, None, builtin_unreachable, &[], None, None); } // Write results to outputs. diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 76a619a1af782..3ea5be1ee562b 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -158,6 +158,8 @@ pub(crate) unsafe fn codegen( config.emit_obj != EmitObj::None, cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked, config.emit_bc, + config.emit_asm, + config.emit_ir, &cgcx.output_filenames, )) } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f5cda81f6ab86..23a5e5ff8730c 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -25,13 +25,13 @@ use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout, }; -use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnv, Ty, TyCtxt, Instance}; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_target::abi::{ self, call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout, WrappingRange, }; -use rustc_target::spec::{HasTargetSpec, Target}; +use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi}; use crate::common::{type_is_pointer, SignType, TypeReflection}; use crate::context::CodegenCx; @@ -592,12 +592,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>, + instance: Option>, ) -> RValue<'gcc> { let try_block = self.current_func().new_block("try"); let current_block = self.block.clone(); self.block = try_block; - let call = self.call(typ, fn_attrs, None, func, args, None); // TODO(antoyo): use funclet here? + let call = self.call(typ, fn_attrs, None, func, args, None, instance); // TODO(antoyo): use funclet here? self.block = current_block; let return_value = @@ -897,26 +898,20 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { self.gcc_checked_binop(oop, typ, lhs, rhs) } - fn alloca(&mut self, ty: Type<'gcc>, align: Align) -> RValue<'gcc> { - // FIXME(antoyo): this check that we don't call get_aligned() a second time on a type. - // Ideally, we shouldn't need to do this check. - let aligned_type = if ty == self.cx.u128_type || ty == self.cx.i128_type { - ty - } else { - ty.get_aligned(align.bytes()) - }; + fn alloca(&mut self, size: Size, align: Align) -> RValue<'gcc> { + let ty = self.cx.type_array(self.cx.type_i8(), size.bytes()).get_aligned(align.bytes()); // TODO(antoyo): It might be better to return a LValue, but fixing the rustc API is non-trivial. self.stack_var_count.set(self.stack_var_count.get() + 1); self.current_func() .new_local( self.location, - aligned_type, + ty, &format!("stack_var_{}", self.stack_var_count.get()), ) .get_address(self.location) } - fn byte_array_alloca(&mut self, _len: RValue<'gcc>, _align: Align) -> RValue<'gcc> { + fn dynamic_alloca(&mut self, _len: RValue<'gcc>, _align: Align) -> RValue<'gcc> { unimplemented!(); } @@ -973,7 +968,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); @@ -998,10 +993,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); } @@ -1011,9 +1007,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); @@ -1026,11 +1022,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 } @@ -1044,8 +1040,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"); @@ -1063,7 +1059,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( @@ -1667,6 +1663,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>, + _instance: Option>, ) -> RValue<'gcc> { // FIXME(antoyo): remove when having a proper API. let gcc_func = unsafe { std::mem::transmute(func) }; @@ -2349,6 +2346,12 @@ impl<'tcx> HasTargetSpec for Builder<'_, '_, 'tcx> { } } +impl<'tcx> HasWasmCAbiOpt for Builder<'_, '_, 'tcx> { + fn wasm_c_abi_opt(&self) -> WasmCAbi { + self.cx.wasm_c_abi_opt() + } +} + pub trait ToGccComp { fn to_gcc_comparison(&self) -> ComparisonOp; } diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index d243d7088ada9..78d943192db07 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -94,6 +94,10 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i8(&self, i: i8) -> RValue<'gcc> { + self.const_int(self.type_i8(), i as i64) + } + fn const_u32(&self, i: u32) -> RValue<'gcc> { self.const_uint(self.type_u32(), i as u64) } diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 8f643c7db720f..16a85b4e8fa09 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -20,7 +20,7 @@ use rustc_span::{source_map::respan, Span}; use rustc_target::abi::{ call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx, }; -use rustc_target::spec::{HasTargetSpec, Target, TlsModel}; +use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, TlsModel, WasmCAbi}; use crate::callee::get_fn; use crate::common::SignType; @@ -110,6 +110,7 @@ pub struct CodegenCx<'gcc, 'tcx> { local_gen_sym_counter: Cell, eh_personality: Cell>>, + #[cfg(feature="master")] pub rust_try_fn: Cell, Function<'gcc>)>>, pub pointee_infos: RefCell, Size), Option>>, @@ -121,6 +122,7 @@ pub struct CodegenCx<'gcc, 'tcx> { /// FIXME(antoyo): fix the rustc API to avoid having this hack. pub structs_as_pointer: RefCell>>, + #[cfg(feature="master")] pub cleanup_blocks: RefCell>>, } @@ -325,9 +327,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { struct_types: Default::default(), local_gen_sym_counter: Cell::new(0), eh_personality: Cell::new(None), + #[cfg(feature="master")] rust_try_fn: Cell::new(None), pointee_infos: Default::default(), structs_as_pointer: Default::default(), + #[cfg(feature="master")] cleanup_blocks: Default::default(), }; // TODO(antoyo): instead of doing this, add SsizeT to libgccjit. @@ -553,6 +557,12 @@ impl<'gcc, 'tcx> HasTargetSpec for CodegenCx<'gcc, 'tcx> { } } +impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> { + fn wasm_c_abi_opt(&self) -> WasmCAbi { + self.tcx.sess.opts.unstable_opts.wasm_c_abi + } +} + impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { type LayoutOfResult = TyAndLayout<'tcx>; diff --git a/compiler/rustc_codegen_gcc/src/coverageinfo.rs b/compiler/rustc_codegen_gcc/src/coverageinfo.rs index 849e9886ef39d..4e44f78f23c26 100644 --- a/compiler/rustc_codegen_gcc/src/coverageinfo.rs +++ b/compiler/rustc_codegen_gcc/src/coverageinfo.rs @@ -1,11 +1,11 @@ use rustc_codegen_ssa::traits::CoverageInfoBuilderMethods; -use rustc_middle::mir::Coverage; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use crate::builder::Builder; impl<'a, 'gcc, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { - fn add_coverage(&mut self, _instance: Instance<'tcx>, _coverage: &Coverage) { + fn add_coverage(&mut self, _instance: Instance<'tcx>, _kind: &CoverageKind) { // TODO(antoyo) } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a6c8b72e851b2..451e5258ebd19 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, }; @@ -133,6 +133,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { func, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + None, ) } sym::likely => self.expect(args[0].immediate(), true), @@ -353,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); @@ -387,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 @@ -401,7 +402,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn abort(&mut self) { let func = self.context.get_builtin_function("abort"); let func: RValue<'gcc> = unsafe { std::mem::transmute(func) }; - self.call(self.type_void(), None, None, func, &[], None); + self.call(self.type_void(), None, None, func, &[], None, None); } fn assume(&mut self, value: Self::Value) { @@ -501,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 { @@ -510,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 @@ -530,7 +531,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { // We instead thus allocate some scratch space... let scratch_size = cast.size(bx); let scratch_align = cast.align(bx); - let llscratch = bx.alloca(cast.gcc_type(bx), scratch_align); + let llscratch = bx.alloca(scratch_size, scratch_align); bx.lifetime_start(llscratch, scratch_size); // ... where we first store the value... @@ -538,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, @@ -570,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, .. } @@ -1103,7 +1109,7 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( dest: RValue<'gcc>, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { - bx.call(bx.type_void(), None, None, try_func, &[data], None); + bx.call(bx.type_void(), None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx.data_layout.i32_align.abi; @@ -1177,21 +1183,21 @@ fn codegen_gnu_try<'gcc>( let zero = bx.cx.context.new_rvalue_zero(bx.int_type); let ptr = bx.cx.context.new_call(None, eh_pointer_builtin, &[zero]); let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, None); bx.ret(bx.const_i32(1)); // NOTE: the blocks must be filled before adding the try/catch, otherwise gcc will not // generate a try/catch. // FIXME(antoyo): add a check in the libgccjit API to prevent this. bx.switch_to_block(current_block); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None); }); let func = unsafe { std::mem::transmute(func) }; // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -1217,7 +1223,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( tcx, ty::Binder::dummy(tcx.mk_fn_sig( iter::once(i8p), - Ty::new_unit(tcx), + tcx.types.unit, false, rustc_hir::Unsafety::Unsafe, Abi::Rust, @@ -1228,7 +1234,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( tcx, ty::Binder::dummy(tcx.mk_fn_sig( [i8p, i8p].iter().cloned(), - Ty::new_unit(tcx), + tcx.types.unit, false, rustc_hir::Unsafety::Unsafe, Abi::Rust, diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index e9af34059a0ec..0c7cffbe7308b 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -18,7 +18,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; use rustc_span::{sym, Span, Symbol}; -use rustc_target::abi::Align; +use rustc_target::abi::{Align, Size}; use crate::builder::Builder; #[cfg(not(feature = "master"))] @@ -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 { @@ -558,7 +558,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let ze = bx.zext(result, bx.type_ix(expected_bytes * 8)); // Convert the integer to a byte array - let ptr = bx.alloca(bx.type_ix(expected_bytes * 8), Align::ONE); + let ptr = bx.alloca(Size::from_bytes(expected_bytes), Align::ONE); bx.store(ze, ptr, Align::ONE); let array_ty = bx.type_array(bx.type_i8(), expected_bytes); let ptr = bx.pointercast(ptr, bx.cx.type_ptr_to(array_ty)); @@ -796,16 +796,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { - match t.kind() { - ty::RawPtr(p) => 1 + ptr_count(p.ty), + match *t.kind() { + ty::RawPtr(p_ty, _) => 1 + ptr_count(p_ty), _ => 0, } } // Non-ptr type fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind() { - ty::RawPtr(p) => non_ptr(p.ty), + match *t.kind() { + ty::RawPtr(p_ty, _) => non_ptr(p_ty), _ => t, } } @@ -814,8 +814,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // to the element type of the first argument let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (pointer_count, underlying_ty) = match element_ty1.kind() { - ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)), + let (pointer_count, underlying_ty) = match *element_ty1.kind() { + ty::RawPtr(p_ty, _) if p_ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)), _ => { require!( false, @@ -910,16 +910,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { - match t.kind() { - ty::RawPtr(p) => 1 + ptr_count(p.ty), + match *t.kind() { + ty::RawPtr(p_ty, _) => 1 + ptr_count(p_ty), _ => 0, } } // Non-ptr type fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind() { - ty::RawPtr(p) => non_ptr(p.ty), + match *t.kind() { + ty::RawPtr(p_ty, _) => non_ptr(p_ty), _ => t, } } @@ -929,8 +929,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); - let (pointer_count, underlying_ty) = match element_ty1.kind() { - ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => { + let (pointer_count, underlying_ty) = match *element_ty1.kind() { + ty::RawPtr(p_ty, mutbl) if p_ty == in_elem && mutbl == hir::Mutability::Mut => { (ptr_count(element_ty1), non_ptr(element_ty1)) } _ => { diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a4ee3015b8d3a..3f2fadce9e4d5 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -19,7 +19,6 @@ #![feature( rustc_private, decl_macro, - associated_type_bounds, never_type, trusted_len, hash_raw_entry diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 3fda59e8b5234..bb5045ec87241 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -28,6 +28,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } +rustc_sanitizers = { path = "../rustc_sanitizers" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index e5f5146fac8fb..123aef0f31076 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -2,12 +2,13 @@ use crate::attributes; use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, Attribute, AttributePlace}; +use crate::llvm_util; use crate::type_::Type; 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; @@ -16,13 +17,15 @@ pub use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; use rustc_middle::ty::Ty; use rustc_session::config; pub use rustc_target::abi::call::*; -use rustc_target::abi::{self, HasDataLayout, Int}; +use rustc_target::abi::{self, HasDataLayout, Int, Size}; pub use rustc_target::spec::abi::Abi; use rustc_target::spec::SanitizerSet; use libc::c_uint; use smallvec::SmallVec; +use std::cmp; + pub trait ArgAttributesExt { fn apply_attrs_to_llfn(&self, idx: AttributePlace, cx: &CodegenCx<'_, '_>, llfn: &Value); fn apply_attrs_to_callsite( @@ -130,42 +133,39 @@ impl LlvmType for Reg { impl LlvmType for CastTarget { fn llvm_type<'ll>(&self, cx: &CodegenCx<'ll, '_>) -> &'ll Type { let rest_ll_unit = self.rest.unit.llvm_type(cx); - let (rest_count, rem_bytes) = if self.rest.unit.size.bytes() == 0 { - (0, 0) + let rest_count = if self.rest.total == Size::ZERO { + 0 } else { - ( - self.rest.total.bytes() / self.rest.unit.size.bytes(), - self.rest.total.bytes() % self.rest.unit.size.bytes(), - ) + assert_ne!( + self.rest.unit.size, + Size::ZERO, + "total size {:?} cannot be divided into units of zero size", + self.rest.total + ); + if self.rest.total.bytes() % self.rest.unit.size.bytes() != 0 { + assert_eq!(self.rest.unit.kind, RegKind::Integer, "only int regs can be split"); + } + self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes()) }; + // Simplify to a single unit or an array if there's no prefix. + // This produces the same layout, but using a simpler type. if self.prefix.iter().all(|x| x.is_none()) { - // Simplify to a single unit when there is no prefix and size <= unit size - if self.rest.total <= self.rest.unit.size { + // We can't do this if is_consecutive is set and the unit would get + // split on the target. Currently, this is only relevant for i128 + // registers. + if rest_count == 1 && (!self.rest.is_consecutive || self.rest.unit != Reg::i128()) { return rest_ll_unit; } - // Simplify to array when all chunks are the same size and type - if rem_bytes == 0 { - return cx.type_array(rest_ll_unit, rest_count); - } - } - - // Create list of fields in the main structure - let mut args: Vec<_> = self - .prefix - .iter() - .flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx))) - .chain((0..rest_count).map(|_| rest_ll_unit)) - .collect(); - - // Append final integer - if rem_bytes != 0 { - // Only integers can be really split further. - assert_eq!(self.rest.unit.kind, RegKind::Integer); - args.push(cx.type_ix(rem_bytes * 8)); + return cx.type_array(rest_ll_unit, rest_count); } + // Generate a struct type with the prefix and the "rest" arguments. + let prefix_args = + self.prefix.iter().flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx))); + let rest_args = (0..rest_count).map(|_| rest_ll_unit); + let args: Vec<_> = prefix_args.chain(rest_args).collect(); cx.type_struct(&args, false) } } @@ -208,54 +208,40 @@ 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: _ } => { bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); } PassMode::Cast { cast, pad_i32: _ } => { - // FIXME(eddyb): Figure out when the simpler Store is safe, clang - // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. - let can_store_through_cast_ptr = false; - if can_store_through_cast_ptr { - bx.store(val, dst.llval, self.layout.align.abi); - } else { - // The actual return type is a struct, but the ABI - // adaptation code has cast it into some scalar type. The - // code that follows is the only reliable way I have - // found to do a transform like i64 -> {i32,i32}. - // Basically we dump the data onto the stack then memcpy it. - // - // Other approaches I tried: - // - Casting rust ret pointer to the foreign type and using Store - // is (a) unsafe if size of foreign type > size of rust type and - // (b) runs afoul of strict aliasing rules, yielding invalid - // assembly under -O (specifically, the store gets removed). - // - Truncating foreign type to correct integral type and then - // bitcasting to the struct type yields invalid cast errors. - - // We instead thus allocate some scratch space... - let scratch_size = cast.size(bx); - let scratch_align = cast.align(bx); - let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align); - bx.lifetime_start(llscratch, scratch_size); - - // ... where we first store the value... - bx.store(val, llscratch, scratch_align); - - // ... and then memcpy it to the intended destination. - bx.memcpy( - dst.llval, - self.layout.align.abi, - llscratch, - scratch_align, - bx.const_usize(self.layout.size.bytes()), - MemFlags::empty(), - ); - - bx.lifetime_end(llscratch, scratch_size); - } + // The ABI mandates that the value is passed as a different struct representation. + // Spill and reload it from the stack to convert from the ABI representation to + // the Rust representation. + let scratch_size = cast.size(bx); + let scratch_align = cast.align(bx); + // Note that the ABI type may be either larger or smaller than the Rust type, + // due to the presence or absence of trailing padding. For example: + // - On some ABIs, the Rust layout { f64, f32, } may omit padding + // when passed by value, making it smaller. + // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes + // when passed by value, making it larger. + let copy_bytes = cmp::min(scratch_size.bytes(), self.layout.size.bytes()); + // Allocate some scratch space... + let llscratch = bx.alloca(scratch_size, scratch_align); + bx.lifetime_start(llscratch, scratch_size); + // ...store the value... + bx.store(val, llscratch, scratch_align); + // ... and then memcpy it to the intended destination. + bx.memcpy( + dst.val.llval, + self.layout.align.abi, + llscratch, + scratch_align, + bx.const_usize(copy_bytes), + MemFlags::empty(), + ); + bx.lifetime_end(llscratch, scratch_size); } _ => { OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst); @@ -280,7 +266,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: _ } @@ -435,6 +426,18 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { cx.type_array(cx.type_i8(), self.ret.layout.size.bytes()), ); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[sret]); + if cx.sess().opts.optimize != config::OptLevel::No + && llvm_util::get_version() >= (18, 0, 0) + { + attributes::apply_to_llfn( + llfn, + llvm::AttributePlace::Argument(i), + &[ + llvm::AttributeKind::Writable.create_attr(cx.llcx), + llvm::AttributeKind::DeadOnUnwind.create_attr(cx.llcx), + ], + ); + } } PassMode::Cast { cast, pad_i32: _ } => { cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 74539d4d49570..e09869cf425c1 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 => { @@ -466,11 +466,11 @@ pub(crate) fn inline_asm_call<'ll>( let call = if !labels.is_empty() { assert!(catch_funclet.is_none()); - bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None) + bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None, None) } else if let Some((catch, funclet)) = catch_funclet { - bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet) + bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet, None) } else { - bx.call(fty, None, None, v, inputs, None) + bx.call(fty, None, None, v, inputs, None, None) }; // Store mark in a metadata node so we can map LLVM errors diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index f9eaa0d94cbbd..870e5ab329619 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -417,7 +417,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry")); } if let Some(align) = codegen_fn_attrs.alignment { - llvm::set_alignment(llfn, align as usize); + llvm::set_alignment(llfn, align); } to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 0619000364b84..d4a3e39cef728 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -415,6 +415,7 @@ impl<'a> LlvmArchiveBuilder<'a> { members.as_ptr() as *const &_, true, kind, + self.sess.target.arch == "arm64ec", ); let ret = if r.into_result().is_err() { let err = llvm::LLVMRustGetLastError(); diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 06a681c24e697..e61af863dc079 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -608,7 +608,7 @@ pub(crate) fn run_pass_manager( "LTOPostLink".as_ptr().cast(), 11, ) { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( module.module_llvm.llmod(), llvm::LLVMModFlagBehavior::Error, c"LTOPostLink".as_ptr().cast(), diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 031bbd6336116..49f9d7ddab65f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -29,7 +29,8 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_errors::{DiagCtxt, FatalError, Level}; use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath}; +use rustc_session::config::{self, Lto, OutputType, Passes}; +use rustc_session::config::{RemapPathScopeComponents, SplitDwarfKind, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; @@ -257,18 +258,17 @@ pub fn target_machine_factory( }; let debuginfo_compression = SmallCStr::new(&debuginfo_compression); - let should_prefer_remapped_for_split_debuginfo_paths = - sess.should_prefer_remapped_for_split_debuginfo_paths(); + let file_name_display_preference = + sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO); Arc::new(move |config: TargetMachineFactoryConfig| { let path_to_cstring_helper = |path: Option| -> CString { let path = path.unwrap_or_default(); - let path = if should_prefer_remapped_for_split_debuginfo_paths { - path_mapping.map_prefix(path).0 - } else { - path.into() - }; - CString::new(path.to_str().unwrap()).unwrap() + let path = path_mapping + .to_real_filename(path) + .to_string_lossy(file_name_display_preference) + .into_owned(); + CString::new(path).unwrap() }; let split_dwarf_file = path_to_cstring_helper(config.split_dwarf_file); @@ -880,6 +880,8 @@ pub(crate) unsafe fn codegen( config.emit_obj != EmitObj::None, dwarf_object_emitted, config.emit_bc, + config.emit_asm, + config.emit_ir, &cgcx.output_filenames, )) } @@ -912,6 +914,7 @@ fn target_is_apple(cgcx: &CodegenContext) -> bool { || cgcx.opts.target_triple.triple().contains("-darwin") || cgcx.opts.target_triple.triple().contains("-tvos") || cgcx.opts.target_triple.triple().contains("-watchos") + || cgcx.opts.target_triple.triple().contains("-visionos") } fn target_is_aix(cgcx: &CodegenContext) -> bool { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 63e59ea13fc35..5c8f358d03a1f 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -17,15 +17,17 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ - FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout, }; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_sanitizers::{cfi, kcfi}; +use rustc_session::config::OptLevel; use rustc_span::Span; -use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions}; use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; use std::borrow::Cow; +use std::ffi::CString; use std::iter; use std::ops::Deref; use std::ptr; @@ -221,6 +223,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { then: &'ll BasicBlock, catch: &'ll BasicBlock, funclet: Option<&Funclet<'ll>>, + instance: Option>, ) -> &'ll Value { debug!("invoke {:?} with args ({:?})", llfn, args); @@ -233,10 +236,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, instance, llfn); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -466,9 +469,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { val } - fn alloca(&mut self, ty: &'ll Type, align: Align) -> &'ll Value { + fn alloca(&mut self, size: Size, align: Align) -> &'ll Value { let mut bx = Builder::with_cx(self.cx); bx.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) }); + let ty = self.cx().type_array(self.cx().type_i8(), size.bytes()); unsafe { let alloca = llvm::LLVMBuildAlloca(bx.llbuilder, ty, UNNAMED); llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); @@ -476,10 +480,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn byte_array_alloca(&mut self, len: &'ll Value, align: Align) -> &'ll Value { + fn dynamic_alloca(&mut self, size: &'ll Value, align: Align) -> &'ll Value { unsafe { let alloca = - llvm::LLVMBuildArrayAlloca(self.llbuilder, self.cx().type_i8(), len, UNNAMED); + llvm::LLVMBuildArrayAlloca(self.llbuilder, self.cx().type_i8(), size, UNNAMED); llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); alloca } @@ -533,7 +537,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); @@ -547,6 +551,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { layout: TyAndLayout<'tcx>, offset: Size, ) { + if bx.cx.sess().opts.optimize == OptLevel::No { + // Don't emit metadata we're not going to use + return; + } + if !scalar.is_uninit_valid() { bx.noundef_metadata(load); } @@ -572,13 +581,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 { @@ -589,7 +599,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); } @@ -601,9 +611,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); @@ -612,11 +622,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 } @@ -663,6 +673,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { return; } + if self.cx.sess().opts.optimize == OptLevel::No { + // Don't emit metadata we're not going to use + return; + } + unsafe { let llty = self.cx.val_ty(load); let v = [ @@ -1136,12 +1151,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { order: rustc_codegen_ssa::common::AtomicOrdering, ) -> &'ll Value { // The only RMW operation that LLVM supports on pointers is compare-exchange. - if self.val_ty(src) == self.type_ptr() - && op != rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicXchg - { + let requires_cast_to_int = self.val_ty(src) == self.type_ptr() + && op != rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicXchg; + if requires_cast_to_int { src = self.ptrtoint(src, self.type_isize()); } - unsafe { + let mut res = unsafe { llvm::LLVMBuildAtomicRMW( self.llbuilder, AtomicRmwBinOp::from_generic(op), @@ -1150,7 +1165,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { AtomicOrdering::from_generic(order), llvm::False, // SingleThreaded ) + }; + if requires_cast_to_int { + res = self.inttoptr(res, self.type_ptr()); } + res } fn atomic_fence( @@ -1231,6 +1250,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { llfn: &'ll Value, args: &[&'ll Value], funclet: Option<&Funclet<'ll>>, + instance: Option>, ) -> &'ll Value { debug!("call {:?} with args ({:?})", llfn, args); @@ -1243,10 +1263,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, instance, llfn); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1468,7 +1488,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); - self.call(ty, None, None, f, args, None) + self.call(ty, None, None, f, args, None, None) } fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) { @@ -1526,7 +1546,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { format!("llvm.{instr}.sat.i{int_width}.f{float_width}") }; let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); - self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None) + self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None, None) } pub(crate) fn landing_pad( @@ -1554,6 +1574,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { default_dest: &'ll BasicBlock, indirect_dest: &[&'ll BasicBlock], funclet: Option<&Funclet<'ll>>, + instance: Option>, ) -> &'ll Value { debug!("invoke {:?} with args ({:?})", llfn, args); @@ -1566,10 +1587,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, instance, llfn); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1601,6 +1622,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { &mut self, fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + instance: Option>, llfn: &'ll Value, ) { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; @@ -1614,15 +1636,19 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - let mut options = TypeIdOptions::empty(); + let mut options = cfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(cfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(cfi::TypeIdOptions::NORMALIZE_INTEGERS); } - let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + let typeid = if let Some(instance) = instance { + cfi::typeid_for_instance(self.tcx, instance, options) + } else { + cfi::typeid_for_fnabi(self.tcx, fn_abi, options) + }; let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); // Test whether the function pointer is associated with the type identifier. @@ -1644,6 +1670,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { &mut self, fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + instance: Option>, llfn: &'ll Value, ) -> Option> { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; @@ -1657,19 +1684,156 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return None; } - let mut options = TypeIdOptions::empty(); + let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + let kcfi_typeid = if let Some(instance) = instance { + kcfi::typeid_for_instance(self.tcx, instance, options) + } else { + kcfi::typeid_for_fnabi(self.tcx, fn_abi, options) + }; + Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)])) } else { None }; kcfi_bundle } + + pub(crate) fn mcdc_parameters( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + bitmap_bytes: &'ll Value, + max_decision_depth: u32, + ) -> Vec<&'ll Value> { + debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes); + + assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); + + let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) }; + let llty = self.cx.type_func( + &[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()], + self.cx.type_void(), + ); + let args = &[fn_name, hash, bitmap_bytes]; + let args = self.check_call("call", llty, llfn, args); + + let mut cond_bitmaps = vec![]; + + unsafe { + let _ = llvm::LLVMRustBuildCall( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + [].as_ptr(), + 0 as c_uint, + ); + // Create condition bitmap named `mcdc.addr`. + for i in 0..=max_decision_depth { + let mut bx = Builder::with_cx(self.cx); + bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn())); + + let name = CString::new(format!("mcdc.addr.{i}")).unwrap(); + let cond_bitmap = { + let alloca = + llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), name.as_ptr()); + llvm::LLVMSetAlignment(alloca, 4); + alloca + }; + bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi); + cond_bitmaps.push(cond_bitmap); + } + } + cond_bitmaps + } + + pub(crate) fn mcdc_tvbitmap_update( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + bitmap_bytes: &'ll Value, + bitmap_index: &'ll Value, + mcdc_temp: &'ll Value, + ) { + debug!( + "mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})", + fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp + ); + assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); + + let llfn = + unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) }; + let llty = self.cx.type_func( + &[ + self.cx.type_ptr(), + self.cx.type_i64(), + self.cx.type_i32(), + self.cx.type_i32(), + self.cx.type_ptr(), + ], + self.cx.type_void(), + ); + let args = &[fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp]; + let args = self.check_call("call", llty, llfn, args); + unsafe { + let _ = llvm::LLVMRustBuildCall( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + [].as_ptr(), + 0 as c_uint, + ); + } + let i32_align = self.tcx().data_layout.i32_align.abi; + self.store(self.const_i32(0), mcdc_temp, i32_align); + } + + pub(crate) fn mcdc_condbitmap_update( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + cond_loc: &'ll Value, + mcdc_temp: &'ll Value, + bool_value: &'ll Value, + ) { + debug!( + "mcdc_condbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})", + fn_name, hash, cond_loc, mcdc_temp, bool_value + ); + assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); + let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(self.cx().llmod) }; + let llty = self.cx.type_func( + &[ + self.cx.type_ptr(), + self.cx.type_i64(), + self.cx.type_i32(), + self.cx.type_ptr(), + self.cx.type_i1(), + ], + self.cx.type_void(), + ); + let args = &[fn_name, hash, cond_loc, mcdc_temp, bool_value]; + self.check_call("call", llty, llfn, args); + unsafe { + let _ = llvm::LLVMRustBuildCall( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + [].as_ptr(), + 0 as c_uint, + ); + } + } } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 25cbd90460f27..ec33ce6292acc 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -160,6 +160,10 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i8(&self, i: i8) -> &'ll Value { + self.const_int(self.type_i8(), i as i64) + } + fn const_u32(&self, i: u32) -> &'ll Value { self.const_uint(self.type_i32(), i as u64) } @@ -251,21 +255,38 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { let (prov, offset) = ptr.into_parts(); let (base_addr, base_addr_space) = match self.tcx.global_alloc(prov.alloc_id()) { GlobalAlloc::Memory(alloc) => { - let init = const_alloc_to_llvm(self, alloc); - let alloc = alloc.inner(); - let value = match alloc.mutability { - Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), - _ => self.static_addr_of(init, alloc.align, None), - }; - if !self.sess().fewer_names() && llvm::get_value_name(value).is_empty() { - let hash = self.tcx.with_stable_hashing_context(|mut hcx| { - let mut hasher = StableHasher::new(); - alloc.hash_stable(&mut hcx, &mut hasher); - hasher.finish::() - }); - llvm::set_value_name(value, format!("alloc_{hash:032x}").as_bytes()); + // For ZSTs directly codegen an aligned pointer. + // This avoids generating a zero-sized constant value and actually needing a + // real address at runtime. + if alloc.inner().len() == 0 { + assert_eq!(offset.bytes(), 0); + let llval = self.const_usize(alloc.inner().align.bytes()); + return if matches!(layout.primitive(), Pointer(_)) { + unsafe { llvm::LLVMConstIntToPtr(llval, llty) } + } else { + self.const_bitcast(llval, llty) + }; + } else { + let init = const_alloc_to_llvm(self, alloc, /*static*/ false); + let alloc = alloc.inner(); + let value = match alloc.mutability { + Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), + _ => self.static_addr_of(init, alloc.align, None), + }; + if !self.sess().fewer_names() && llvm::get_value_name(value).is_empty() + { + let hash = self.tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + alloc.hash_stable(&mut hcx, &mut hasher); + hasher.finish::() + }); + llvm::set_value_name( + value, + format!("alloc_{hash:032x}").as_bytes(), + ); + } + (value, AddressSpace::DATA) } - (value, AddressSpace::DATA) } GlobalAlloc::Function(fn_instance) => ( self.get_fn_addr(fn_instance.polymorphize(self.tcx)), @@ -276,7 +297,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { .tcx .global_alloc(self.tcx.vtable_allocation((ty, trait_ref))) .unwrap_memory(); - let init = const_alloc_to_llvm(self, alloc); + let init = const_alloc_to_llvm(self, alloc, /*static*/ false); let value = self.static_addr_of(init, alloc.inner().align, None); (value, AddressSpace::DATA) } @@ -304,7 +325,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value { - const_alloc_to_llvm(self, alloc) + const_alloc_to_llvm(self, alloc, /*static*/ false) } fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 4afa230e598b7..9e85c2d88f9b4 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -26,8 +26,22 @@ use rustc_target::abi::{ }; use std::ops::Range; -pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value { +pub fn const_alloc_to_llvm<'ll>( + cx: &CodegenCx<'ll, '_>, + alloc: ConstAllocation<'_>, + is_static: bool, +) -> &'ll Value { let alloc = alloc.inner(); + // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or + // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be + // producing empty LLVM allocations as they're just adding noise to binaries and forcing less + // optimal codegen. + // + // Statics have a guaranteed meaningful address so it's less clear that we want to do + // something like this; it's also harder. + if !is_static { + assert!(alloc.len() != 0); + } let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1); let dl = cx.data_layout(); let pointer_size = dl.pointer_size.bytes() as usize; @@ -120,7 +134,7 @@ fn codegen_static_initializer<'ll, 'tcx>( def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; - Ok((const_alloc_to_llvm(cx, alloc), alloc)) + Ok((const_alloc_to_llvm(cx, alloc, /*static*/ true), alloc)) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { @@ -246,7 +260,8 @@ impl<'ll> CodegenCx<'ll, '_> { #[instrument(level = "debug", skip(self, llty))] pub(crate) fn get_static_inner(&self, def_id: DefId, llty: &'ll Type) -> &'ll Value { - if let Some(&g) = self.instances.borrow().get(&Instance::mono(self.tcx, def_id)) { + let instance = Instance::mono(self.tcx, def_id); + if let Some(&g) = self.instances.borrow().get(&instance) { trace!("used cached value"); return g; } @@ -259,7 +274,7 @@ impl<'ll> CodegenCx<'ll, '_> { statics defined in the same CGU, but did not for `{def_id:?}`" ); - let sym = self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name; + let sym = self.tcx.symbol_name(instance).name; let fn_attrs = self.tcx.codegen_fn_attrs(def_id); debug!(?sym, ?fn_attrs); @@ -349,7 +364,7 @@ impl<'ll> CodegenCx<'ll, '_> { } } - self.instances.borrow_mut().insert(Instance::mono(self.tcx, def_id), g); + self.instances.borrow_mut().insert(instance, g); g } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f89c8c9f836bf..d32baa6dc02a6 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -27,9 +27,7 @@ use rustc_session::config::{CrateType, DebugInfo, PAuthKey, PacRet}; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_span::Span; -use rustc_target::abi::{ - call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx, -}; +use rustc_target::abi::{call::FnAbi, HasDataLayout, TargetDataLayout, VariantIdx}; use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel}; use smallvec::SmallVec; @@ -83,7 +81,6 @@ pub struct CodegenCx<'ll, 'tcx> { /// Mapping of scalar types to llvm types. pub scalar_lltypes: RefCell, &'ll Type>>, - pub pointee_infos: RefCell, Size), Option>>, pub isize_ty: &'ll Type, pub coverage_cx: Option>, @@ -126,17 +123,6 @@ pub unsafe fn create_module<'ll>( let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); - if llvm_version < (17, 0, 0) { - if sess.target.arch.starts_with("powerpc") { - // LLVM 17 specifies function pointer alignment for ppc: - // https://reviews.llvm.org/D147016 - target_data_layout = target_data_layout - .replace("-Fn32", "") - .replace("-Fi32", "") - .replace("-Fn64", "") - .replace("-Fi64", ""); - } - } if llvm_version < (18, 0, 0) { if sess.target.arch == "x86" || sess.target.arch == "x86_64" { // LLVM 18 adjusts i128 to be 128-bit aligned on x86 variants. @@ -194,13 +180,13 @@ pub unsafe fn create_module<'ll>( // to ensure intrinsic calls don't use it. if !sess.needs_plt() { let avoid_plt = c"RtLibUseGOT".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); + llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); } // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.) if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() { let canonical_jump_tables = c"CFI Canonical Jump Tables".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, canonical_jump_tables, @@ -211,7 +197,7 @@ pub unsafe fn create_module<'ll>( // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.) if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() { let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, enable_split_lto_unit, @@ -222,7 +208,7 @@ pub unsafe fn create_module<'ll>( // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.) if sess.is_sanitizer_kcfi_enabled() { let kcfi = c"kcfi".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); + llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); } // Control Flow Guard is currently only supported by the MSVC linker on Windows. @@ -231,7 +217,7 @@ pub unsafe fn create_module<'ll>( CFGuard::Disabled => {} CFGuard::NoChecks => { // Set `cfguard=1` module flag to emit metadata only. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"cfguard".as_ptr() as *const _, @@ -240,7 +226,7 @@ pub unsafe fn create_module<'ll>( } CFGuard::Checks => { // Set `cfguard=2` module flag to emit metadata and checks. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"cfguard".as_ptr() as *const _, @@ -252,26 +238,26 @@ pub unsafe fn create_module<'ll>( if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { if sess.target.arch == "aarch64" { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"branch-target-enforcement".as_ptr().cast(), bti.into(), ); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address".as_ptr().cast(), pac_ret.is_some().into(), ); let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address-all".as_ptr().cast(), pac_opts.leaf.into(), ); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address-with-bkey".as_ptr().cast(), @@ -287,7 +273,7 @@ pub unsafe fn create_module<'ll>( // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang). if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, c"cf-protection-branch".as_ptr().cast(), @@ -295,7 +281,7 @@ pub unsafe fn create_module<'ll>( ) } if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, c"cf-protection-return".as_ptr().cast(), @@ -304,7 +290,7 @@ pub unsafe fn create_module<'ll>( } if sess.opts.unstable_opts.virtual_function_elimination { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Error, c"Virtual Function Elim".as_ptr().cast(), @@ -314,7 +300,7 @@ pub unsafe fn create_module<'ll>( // Set module flag to enable Windows EHCont Guard (/guard:ehcont). if sess.opts.unstable_opts.ehcont_guard { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"ehcontguard".as_ptr() as *const _, @@ -326,6 +312,7 @@ pub unsafe fn create_module<'ll>( // // On the wasm targets it will get hooked up to the "producer" sections // `processed-by` information. + #[allow(clippy::option_env_unwrap)] let rustc_producer = format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION")); let name_metadata = llvm::LLVMMDStringInContext( @@ -339,6 +326,22 @@ pub unsafe fn create_module<'ll>( llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), ); + // Emit RISC-V specific target-abi metadata + // to workaround lld as the LTO plugin not + // correctly setting target-abi for the LTO object + // FIXME: https://github.com/llvm/llvm-project/issues/50591 + // If llvm_abiname is empty, emit nothing. + let llvm_abiname = &sess.target.options.llvm_abiname; + if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() { + llvm::LLVMRustAddModuleFlagString( + llmod, + llvm::LLVMModFlagBehavior::Error, + c"target-abi".as_ptr(), + llvm_abiname.as_ptr().cast(), + llvm_abiname.len(), + ); + } + // Add module flags specified via -Z llvm_module_flag for (key, value, behavior) in &sess.opts.unstable_opts.llvm_module_flag { let key = format!("{key}\0"); @@ -354,7 +357,7 @@ pub unsafe fn create_module<'ll>( // We already checked this during option parsing _ => unreachable!(), }; - llvm::LLVMRustAddModuleFlag(llmod, behavior, key.as_ptr().cast(), *value) + llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_ptr().cast(), *value) } llmod @@ -460,7 +463,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { compiler_used_statics: RefCell::new(Vec::new()), type_lowering: Default::default(), scalar_lltypes: Default::default(), - pointee_infos: Default::default(), isize_ty, coverage_cx, dbg_cx, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 2af28146a51ea..12a846a49ec16 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,4 +1,6 @@ -use rustc_middle::mir::coverage::{CodeRegion, CounterId, CovTerm, ExpressionId, MappingKind}; +use rustc_middle::mir::coverage::{ + CodeRegion, ConditionInfo, CounterId, CovTerm, DecisionInfo, ExpressionId, MappingKind, +}; /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] @@ -99,6 +101,86 @@ pub enum RegionKind { /// associated with two counters, each representing the number of times the /// expression evaluates to true or false. BranchRegion = 4, + + /// A DecisionRegion represents a top-level boolean expression and is + /// associated with a variable length bitmap index and condition number. + MCDCDecisionRegion = 5, + + /// A Branch Region can be extended to include IDs to facilitate MC/DC. + MCDCBranchRegion = 6, +} + +pub mod mcdc { + use rustc_middle::mir::coverage::{ConditionInfo, DecisionInfo}; + + /// Must match the layout of `LLVMRustMCDCDecisionParameters`. + #[repr(C)] + #[derive(Clone, Copy, Debug, Default)] + pub struct DecisionParameters { + bitmap_idx: u32, + conditions_num: u16, + } + + // ConditionId in llvm is `unsigned int` at 18 while `int16_t` at [19](https://github.com/llvm/llvm-project/pull/81257) + type LLVMConditionId = i16; + + /// Must match the layout of `LLVMRustMCDCBranchParameters`. + #[repr(C)] + #[derive(Clone, Copy, Debug, Default)] + pub struct BranchParameters { + condition_id: LLVMConditionId, + condition_ids: [LLVMConditionId; 2], + } + + #[repr(C)] + #[derive(Clone, Copy, Debug)] + pub enum ParameterTag { + None = 0, + Decision = 1, + Branch = 2, + } + /// Same layout with `LLVMRustMCDCParameters` + #[repr(C)] + #[derive(Clone, Copy, Debug)] + pub struct Parameters { + tag: ParameterTag, + decision_params: DecisionParameters, + branch_params: BranchParameters, + } + + impl Parameters { + pub fn none() -> Self { + Self { + tag: ParameterTag::None, + decision_params: Default::default(), + branch_params: Default::default(), + } + } + pub fn decision(decision_params: DecisionParameters) -> Self { + Self { tag: ParameterTag::Decision, decision_params, branch_params: Default::default() } + } + pub fn branch(branch_params: BranchParameters) -> Self { + Self { tag: ParameterTag::Branch, decision_params: Default::default(), branch_params } + } + } + + impl From for BranchParameters { + fn from(value: ConditionInfo) -> Self { + Self { + condition_id: value.condition_id.as_u32() as LLVMConditionId, + condition_ids: [ + value.false_next_id.as_u32() as LLVMConditionId, + value.true_next_id.as_u32() as LLVMConditionId, + ], + } + } + } + + impl From for DecisionParameters { + fn from(value: DecisionInfo) -> Self { + Self { bitmap_idx: value.bitmap_idx, conditions_num: value.conditions_num } + } + } } /// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the @@ -122,6 +204,7 @@ pub struct CounterMappingRegion { /// for the false branch of the region. false_counter: Counter, + mcdc_params: mcdc::Parameters, /// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the /// file_id is an index into a function-specific `virtual_file_mapping` array of indexes /// that, in turn, are used to look up the filename for this region. @@ -173,6 +256,26 @@ impl CounterMappingRegion { end_line, end_col, ), + MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => { + Self::mcdc_branch_region( + Counter::from_term(true_term), + Counter::from_term(false_term), + mcdc_params, + local_file_id, + start_line, + start_col, + end_line, + end_col, + ) + } + MappingKind::MCDCDecision(decision_info) => Self::decision_region( + decision_info, + local_file_id, + start_line, + start_col, + end_line, + end_col, + ), } } @@ -187,6 +290,7 @@ impl CounterMappingRegion { Self { counter, false_counter: Counter::ZERO, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id: 0, start_line, @@ -209,6 +313,7 @@ impl CounterMappingRegion { Self { counter, false_counter, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id: 0, start_line, @@ -219,6 +324,54 @@ impl CounterMappingRegion { } } + pub(crate) fn mcdc_branch_region( + counter: Counter, + false_counter: Counter, + condition_info: ConditionInfo, + file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + Self { + counter, + false_counter, + mcdc_params: mcdc::Parameters::branch(condition_info.into()), + file_id, + expanded_file_id: 0, + start_line, + start_col, + end_line, + end_col, + kind: RegionKind::MCDCBranchRegion, + } + } + + pub(crate) fn decision_region( + decision_info: DecisionInfo, + file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + let mcdc_params = mcdc::Parameters::decision(decision_info.into()); + + Self { + counter: Counter::ZERO, + false_counter: Counter::ZERO, + mcdc_params, + file_id, + expanded_file_id: 0, + start_line, + start_col, + end_line, + end_col, + kind: RegionKind::MCDCDecisionRegion, + } + } + // This function might be used in the future; the LLVM API is still evolving, as is coverage // support. #[allow(dead_code)] @@ -233,6 +386,7 @@ impl CounterMappingRegion { Self { counter: Counter::ZERO, false_counter: Counter::ZERO, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id, start_line, @@ -256,6 +410,7 @@ impl CounterMappingRegion { Self { counter: Counter::ZERO, false_counter: Counter::ZERO, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id: 0, start_line, @@ -280,6 +435,7 @@ impl CounterMappingRegion { Self { counter, false_counter: Counter::ZERO, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id: 0, start_line, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index ee7ea34230168..3f3969bbca35c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -6,9 +6,8 @@ use crate::llvm; use itertools::Itertools as _; use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods}; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::mir; @@ -18,11 +17,11 @@ use rustc_span::Symbol; /// Generates and exports the Coverage Map. /// -/// Rust Coverage Map generation supports LLVM Coverage Mapping Format version -/// 6 (zero-based encoded as 5), as defined at -/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format). +/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions +/// 6 and 7 (encoded as 5 and 6 respectively), as described at +/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/18.0-2024-02-13/llvm/docs/CoverageMappingFormat.rst). /// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`) -/// bundled with Rust's fork of LLVM. +/// distributed in the `llvm-tools-preview` rustup component. /// /// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with /// the same version. Clang's implementation of Coverage Map generation was referenced when @@ -32,10 +31,21 @@ use rustc_span::Symbol; pub fn finalize(cx: &CodegenCx<'_, '_>) { let tcx = cx.tcx; - // Ensure the installed version of LLVM supports Coverage Map Version 6 - // (encoded as a zero-based value: 5), which was introduced with LLVM 13. - let version = coverageinfo::mapping_version(); - assert_eq!(version, 5, "The `CoverageMappingVersion` exposed by `llvm-wrapper` is out of sync"); + // Ensure that LLVM is using a version of the coverage mapping format that + // agrees with our Rust-side code. Expected versions (encoded as n-1) are: + // - `CovMapVersion::Version6` (5) used by LLVM 13-17 + // - `CovMapVersion::Version7` (6) used by LLVM 18 + let covmap_version = { + let llvm_covmap_version = coverageinfo::mapping_version(); + let expected_versions = 5..=6; + assert!( + expected_versions.contains(&llvm_covmap_version), + "Coverage mapping version exposed by `llvm-wrapper` is out of sync; \ + expected {expected_versions:?} but was {llvm_covmap_version}" + ); + // This is the version number that we will embed in the covmap section: + llvm_covmap_version + }; debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); @@ -75,7 +85,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { // Generate the coverage map header, which contains the filenames used by // this CGU's coverage mappings, and store it in a well-known global. - let cov_data_val = generate_coverage_map(cx, version, filenames_size, filenames_val); + let cov_data_val = generate_coverage_map(cx, covmap_version, filenames_size, filenames_val); coverageinfo::save_cov_data_to_mod(cx, cov_data_val); let mut unused_function_names = Vec::new(); @@ -174,8 +184,14 @@ impl GlobalFileTable { // Since rustc generates coverage maps with relative paths, the // compilation directory can be combined with the relative paths // to get absolute paths, if needed. + use rustc_session::config::RemapPathScopeComponents; use rustc_session::RemapFileNameExt; - let working_dir: &str = &tcx.sess.opts.working_dir.for_codegen(tcx.sess).to_string_lossy(); + let working_dir: &str = &tcx + .sess + .opts + .working_dir + .for_scope(tcx.sess, RemapPathScopeComponents::MACRO) + .to_string_lossy(); llvm::build_byte_buffer(|buffer| { coverageinfo::write_filenames_section_to_buffer( @@ -335,16 +351,9 @@ fn save_function_record( ); } -/// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for -/// the functions that went through codegen; such as public functions and "used" functions -/// (functions referenced by other "used" or public items). Any other functions considered unused, -/// or "Unreachable", were still parsed and processed through the MIR stage, but were not -/// codegenned. (Note that `-Clink-dead-code` can force some unused code to be codegenned, but -/// that flag is known to cause other errors, when combined with `-C instrument-coverage`; and -/// `-Clink-dead-code` will not generate code for unused generic functions.) -/// -/// We can find the unused functions (including generic functions) by the set difference of all MIR -/// `DefId`s (`tcx` query `mir_keys`) minus the codegenned `DefId`s (`codegenned_and_inlined_items`). +/// Each CGU will normally only emit coverage metadata for the functions that it actually generates. +/// But since we don't want unused functions to disappear from coverage reports, we also scan for +/// functions that were instrumented but are not participating in codegen. /// /// These unused functions don't need to be codegenned, but we do need to add them to the function /// coverage map (in a single designated CGU) so that we still emit coverage mappings for them. @@ -354,75 +363,109 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) { assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu()); let tcx = cx.tcx; + let usage = prepare_usage_sets(tcx); + + let is_unused_fn = |def_id: LocalDefId| -> bool { + let def_id = def_id.to_def_id(); + + // To be eligible for "unused function" mappings, a definition must: + // - Be function-like + // - Not participate directly in codegen (or have lost all its coverage statements) + // - Not have any coverage statements inlined into codegenned functions + tcx.def_kind(def_id).is_fn_like() + && (!usage.all_mono_items.contains(&def_id) + || usage.missing_own_coverage.contains(&def_id)) + && !usage.used_via_inlining.contains(&def_id) + }; - let eligible_def_ids = tcx.mir_keys(()).iter().filter_map(|local_def_id| { - let def_id = local_def_id.to_def_id(); - let kind = tcx.def_kind(def_id); - // `mir_keys` will give us `DefId`s for all kinds of things, not - // just "functions", like consts, statics, etc. Filter those out. - if !matches!(kind, DefKind::Fn | DefKind::AssocFn | DefKind::Closure) { - return None; - } + // Scan for unused functions that were instrumented for coverage. + for def_id in tcx.mir_keys(()).iter().copied().filter(|&def_id| is_unused_fn(def_id)) { + // Get the coverage info from MIR, skipping functions that were never instrumented. + let body = tcx.optimized_mir(def_id); + let Some(function_coverage_info) = body.function_coverage_info.as_deref() else { continue }; // FIXME(79651): Consider trying to filter out dummy instantiations of // unused generic functions from library crates, because they can produce // "unused instantiation" in coverage reports even when they are actually // used by some downstream crate in the same binary. - Some(local_def_id.to_def_id()) - }); - - let codegenned_def_ids = codegenned_and_inlined_items(tcx); - - // For each `DefId` that should have coverage instrumentation but wasn't - // codegenned, add it to the function coverage map as an unused function. - for def_id in eligible_def_ids.filter(|id| !codegenned_def_ids.contains(id)) { - // Skip any function that didn't have coverage data added to it by the - // coverage instrumentor. - let body = tcx.instance_mir(ty::InstanceDef::Item(def_id)); - let Some(function_coverage_info) = body.function_coverage_info.as_deref() else { - continue; - }; - debug!("generating unused fn: {def_id:?}"); - let instance = declare_unused_fn(tcx, def_id); - add_unused_function_coverage(cx, instance, function_coverage_info); + add_unused_function_coverage(cx, def_id, function_coverage_info); } } -/// All items participating in code generation together with (instrumented) -/// items inlined into them. -fn codegenned_and_inlined_items(tcx: TyCtxt<'_>) -> DefIdSet { - let (items, cgus) = tcx.collect_and_partition_mono_items(()); - let mut visited = DefIdSet::default(); - let mut result = items.clone(); - - for cgu in cgus { - for item in cgu.items().keys() { - if let mir::mono::MonoItem::Fn(ref instance) = item { - let did = instance.def_id(); - if !visited.insert(did) { - continue; - } - let body = tcx.instance_mir(instance.def); - for block in body.basic_blocks.iter() { - for statement in &block.statements { - let mir::StatementKind::Coverage(_) = statement.kind else { continue }; - let scope = statement.source_info.scope; - if let Some(inlined) = scope.inlined_instance(&body.source_scopes) { - result.insert(inlined.def_id()); - } - } - } +struct UsageSets<'tcx> { + all_mono_items: &'tcx DefIdSet, + used_via_inlining: FxHashSet, + missing_own_coverage: FxHashSet, +} + +/// Prepare sets of definitions that are relevant to deciding whether something +/// is an "unused function" for coverage purposes. +fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { + let (all_mono_items, cgus) = tcx.collect_and_partition_mono_items(()); + + // Obtain a MIR body for each function participating in codegen, via an + // arbitrary instance. + let mut def_ids_seen = FxHashSet::default(); + let def_and_mir_for_all_mono_fns = cgus + .iter() + .flat_map(|cgu| cgu.items().keys()) + .filter_map(|item| match item { + mir::mono::MonoItem::Fn(instance) => Some(instance), + mir::mono::MonoItem::Static(_) | mir::mono::MonoItem::GlobalAsm(_) => None, + }) + // We only need one arbitrary instance per definition. + .filter(move |instance| def_ids_seen.insert(instance.def_id())) + .map(|instance| { + // We don't care about the instance, just its underlying MIR. + let body = tcx.instance_mir(instance.def); + (instance.def_id(), body) + }); + + // Functions whose coverage statments were found inlined into other functions. + let mut used_via_inlining = FxHashSet::default(); + // Functions that were instrumented, but had all of their coverage statements + // removed by later MIR transforms (e.g. UnreachablePropagation). + let mut missing_own_coverage = FxHashSet::default(); + + for (def_id, body) in def_and_mir_for_all_mono_fns { + let mut saw_own_coverage = false; + + // Inspect every coverage statement in the function's MIR. + for stmt in body + .basic_blocks + .iter() + .flat_map(|block| &block.statements) + .filter(|stmt| matches!(stmt.kind, mir::StatementKind::Coverage(_))) + { + if let Some(inlined) = stmt.source_info.scope.inlined_instance(&body.source_scopes) { + // This coverage statement was inlined from another function. + used_via_inlining.insert(inlined.def_id()); + } else { + // Non-inlined coverage statements belong to the enclosing function. + saw_own_coverage = true; } } + + if !saw_own_coverage && body.function_coverage_info.is_some() { + missing_own_coverage.insert(def_id); + } } - result + UsageSets { all_mono_items, used_via_inlining, missing_own_coverage } } -fn declare_unused_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::Instance<'tcx> { - ty::Instance::new( +fn add_unused_function_coverage<'tcx>( + cx: &CodegenCx<'_, 'tcx>, + def_id: LocalDefId, + function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo, +) { + let tcx = cx.tcx; + let def_id = def_id.to_def_id(); + + // Make a dummy instance that fills in all generics with placeholders. + let instance = ty::Instance::new( def_id, ty::GenericArgs::for_item(tcx, def_id, |param, _| { if let ty::GenericParamDefKind::Lifetime = param.kind { @@ -431,14 +474,8 @@ fn declare_unused_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::Instance<'tc tcx.mk_param_from_def(param) } }), - ) -} + ); -fn add_unused_function_coverage<'tcx>( - cx: &CodegenCx<'_, 'tcx>, - instance: ty::Instance<'tcx>, - function_coverage_info: &'tcx mir::coverage::FunctionCoverageInfo, -) { // An unused function's mappings will automatically be rewritten to map to // zero, because none of its counters/expressions are marked as seen. let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 133084b7c12ae..679c6e1a2ff83 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -13,10 +13,10 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_llvm::RustString; use rustc_middle::bug; -use rustc_middle::mir::coverage::CoverageKind; -use rustc_middle::mir::Coverage; +use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo}; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Instance; +use rustc_target::abi::Align; use std::cell::RefCell; @@ -24,14 +24,13 @@ pub(crate) mod ffi; pub(crate) mod map_data; pub mod mapgen; -const VAR_ALIGN_BYTES: usize = 8; - /// A context object for maintaining all state needed by the coverageinfo module. pub struct CrateCoverageContext<'ll, 'tcx> { /// Coverage data for each instrumented function identified by DefId. pub(crate) function_coverage_map: RefCell, FunctionCoverageCollector<'tcx>>>, pub(crate) pgo_func_name_var_map: RefCell, &'ll llvm::Value>>, + pub(crate) mcdc_condition_bitmap_map: RefCell, Vec<&'ll llvm::Value>>>, } impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { @@ -39,6 +38,7 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { Self { function_coverage_map: Default::default(), pgo_func_name_var_map: Default::default(), + mcdc_condition_bitmap_map: Default::default(), } } @@ -47,6 +47,23 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { ) -> FxIndexMap, FunctionCoverageCollector<'tcx>> { self.function_coverage_map.replace(FxIndexMap::default()) } + + /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap. + /// In order to handle nested decisions, several condition bitmaps can be + /// allocated for a function body. + /// These values are named `mcdc.addr.{i}` and are a 32-bit integers. + /// They respectively hold the condition bitmaps for decisions with a depth of `i`. + fn try_get_mcdc_condition_bitmap( + &self, + instance: &Instance<'tcx>, + decision_depth: u16, + ) -> Option<&'ll llvm::Value> { + self.mcdc_condition_bitmap_map + .borrow() + .get(instance) + .and_then(|bitmap_map| bitmap_map.get(decision_depth as usize)) + .copied() // Dereference Option<&&Value> to Option<&Value> + } } // These methods used to be part of trait `CoverageInfoMethods`, which no longer @@ -75,7 +92,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { #[instrument(level = "debug", skip(self))] - fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) { + fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) { // Our caller should have already taken care of inlining subtleties, // so we can assume that counter/expression IDs in this coverage // statement are meaningful for the given instance. @@ -85,14 +102,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let bx = self; - match coverage.kind { - // Marker statements have no effect during codegen, - // so return early and don't create `func_coverage`. - CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => return, - // Match exhaustively to ensure that newly-added kinds are classified correctly. - CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {} - } - let Some(function_coverage_info) = bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref() else { @@ -100,16 +109,19 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; }; + if function_coverage_info.mcdc_bitmap_bytes > 0 { + ensure_mcdc_parameters(bx, instance, function_coverage_info); + } + let Some(coverage_context) = bx.coverage_context() else { return }; let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); let func_coverage = coverage_map .entry(instance) .or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info)); - let Coverage { kind } = coverage; match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( - "unexpected marker statement {kind:?} should have caused an early return" + "marker statement {kind:?} should have been removed by CleanupPostBorrowck" ), CoverageKind::CounterIncrement { id } => { func_coverage.mark_counter_id_seen(id); @@ -142,10 +154,67 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { CoverageKind::ExpressionUsed { id } => { func_coverage.mark_expression_id_seen(id); } + CoverageKind::CondBitmapUpdate { id, value, decision_depth } => { + drop(coverage_map); + assert_ne!( + id.as_u32(), + 0, + "ConditionId of evaluated conditions should never be zero" + ); + let cond_bitmap = coverage_context + .try_get_mcdc_condition_bitmap(&instance, decision_depth) + .expect("mcdc cond bitmap should have been allocated for updating"); + let cond_loc = bx.const_i32(id.as_u32() as i32 - 1); + let bool_value = bx.const_bool(value); + let fn_name = bx.get_pgo_func_name_var(instance); + let hash = bx.const_u64(function_coverage_info.function_source_hash); + bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value); + } + CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => { + drop(coverage_map); + let cond_bitmap = coverage_context + .try_get_mcdc_condition_bitmap(&instance, decision_depth) + .expect("mcdc cond bitmap should have been allocated for merging into the global bitmap"); + let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes; + assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range"); + assert!( + bitmap_bytes <= function_coverage_info.mcdc_bitmap_bytes, + "bitmap length disagreement: query says {bitmap_bytes} but function info only has {}", + function_coverage_info.mcdc_bitmap_bytes + ); + + let fn_name = bx.get_pgo_func_name_var(instance); + let hash = bx.const_u64(function_coverage_info.function_source_hash); + let bitmap_bytes = bx.const_u32(bitmap_bytes); + let bitmap_index = bx.const_u32(bitmap_idx); + bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_bytes, bitmap_index, cond_bitmap); + } } } } +fn ensure_mcdc_parameters<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + instance: Instance<'tcx>, + function_coverage_info: &FunctionCoverageInfo, +) { + let Some(cx) = bx.coverage_context() else { return }; + if cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance) { + return; + } + + let fn_name = bx.get_pgo_func_name_var(instance); + let hash = bx.const_u64(function_coverage_info.function_source_hash); + let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes); + let max_decision_depth = function_coverage_info.mcdc_max_decision_depth; + let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes, max_decision_depth as u32); + bx.coverage_context() + .expect("already checked above") + .mcdc_condition_bitmap_map + .borrow_mut() + .insert(instance, cond_bitmap); +} + /// Calls llvm::createPGOFuncNameVar() with the given function instance's /// mangled function name. The LLVM API returns an llvm::GlobalVariable /// containing the function name, with the specific variable name and linkage @@ -235,7 +304,8 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( llvm::set_global_constant(llglobal, true); llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::set_section(llglobal, &covmap_section_name); - llvm::set_alignment(llglobal, VAR_ALIGN_BYTES); + // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. + llvm::set_alignment(llglobal, Align::EIGHT); cx.add_used_global(llglobal); } @@ -265,7 +335,8 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); llvm::set_visibility(llglobal, llvm::Visibility::Hidden); llvm::set_section(llglobal, covfun_section_name); - llvm::set_alignment(llglobal, VAR_ALIGN_BYTES); + // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. + llvm::set_alignment(llglobal, Align::EIGHT); llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); cx.add_used_global(llglobal); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 5782b156335f3..e5fecddec528d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -452,7 +452,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id), ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id), ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id), - ty::RawPtr(ty::TypeAndMut { ty: pointee_type, .. }) | ty::Ref(_, pointee_type, _) => { + ty::RawPtr(pointee_type, _) | ty::Ref(_, pointee_type, _) => { build_pointer_or_reference_di_node(cx, t, pointee_type, unique_type_id) } // Some `Box` are newtyped pointers, make debuginfo aware of that. @@ -554,13 +554,16 @@ pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> ) -> &'ll DIFile { debug!(?source_file.name); - use rustc_session::RemapFileNameExt; + let filename_display_preference = + cx.sess().filename_display_preference(RemapPathScopeComponents::DEBUGINFO); + + use rustc_session::config::RemapPathScopeComponents; let (directory, file_name) = match &source_file.name { FileName::Real(filename) => { let working_directory = &cx.sess().opts.working_dir; debug!(?working_directory); - if cx.sess().should_prefer_remapped_for_codegen() { + if filename_display_preference == FileNameDisplayPreference::Remapped { let filename = cx .sess() .source_map() @@ -623,7 +626,7 @@ pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> } other => { debug!(?other); - ("".into(), other.for_codegen(cx.sess()).to_string_lossy().into_owned()) + ("".into(), other.display(filename_display_preference).to_string()) } }; @@ -832,9 +835,11 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( codegen_unit_name: &str, debug_context: &CodegenUnitDebugContext<'ll, 'tcx>, ) -> &'ll DIDescriptor { + use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; let mut name_in_debuginfo = tcx .sess .local_crate_source_file() + .map(|src| src.for_scope(&tcx.sess, RemapPathScopeComponents::DEBUGINFO).to_path_buf()) .unwrap_or_else(|| PathBuf::from(tcx.crate_name(LOCAL_CRATE).as_str())); // To avoid breaking split DWARF, we need to ensure that each codegen unit @@ -862,30 +867,29 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. let producer = format!("clang LLVM ({rustc_producer})"); - use rustc_session::RemapFileNameExt; let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); - let work_dir = tcx.sess.opts.working_dir.for_codegen(tcx.sess).to_string_lossy(); + let work_dir = tcx + .sess + .opts + .working_dir + .for_scope(tcx.sess, RemapPathScopeComponents::DEBUGINFO) + .to_string_lossy(); let output_filenames = tcx.output_filenames(()); - let split_name = if tcx.sess.target_can_use_split_dwarf() { - output_filenames - .split_dwarf_path( - tcx.sess.split_debuginfo(), - tcx.sess.opts.unstable_opts.split_dwarf_kind, - Some(codegen_unit_name), - ) - // We get a path relative to the working directory from split_dwarf_path - .map(|f| { - if tcx.sess.should_prefer_remapped_for_split_debuginfo_paths() { - tcx.sess.source_map().path_mapping().map_prefix(f).0 - } else { - f.into() - } - }) + let split_name = if tcx.sess.target_can_use_split_dwarf() + && let Some(f) = output_filenames.split_dwarf_path( + tcx.sess.split_debuginfo(), + tcx.sess.opts.unstable_opts.split_dwarf_kind, + Some(codegen_unit_name), + ) { + // We get a path relative to the working directory from split_dwarf_path + Some(tcx.sess.source_map().path_mapping().to_real_filename(f)) } else { None - } - .unwrap_or_default(); - let split_name = split_name.to_str().unwrap(); + }; + let split_name = split_name + .as_ref() + .map(|f| f.for_scope(tcx.sess, RemapPathScopeComponents::DEBUGINFO).to_string_lossy()) + .unwrap_or_default(); let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo); let dwarf_version = diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 4792b0798dfb8..4edef14422e5f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -683,7 +683,8 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( _ => unreachable!(), }; - let coroutine_layout = cx.tcx.optimized_mir(coroutine_def_id).coroutine_layout().unwrap(); + let coroutine_layout = + cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.kind_ty()).unwrap(); let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 3dbe820b8ff9b..115d5187eafa8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -135,7 +135,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { let coroutine_type = unique_type_id.expect_ty(); - let &ty::Coroutine(coroutine_def_id, _) = coroutine_type.kind() else { + let &ty::Coroutine(coroutine_def_id, coroutine_args) = coroutine_type.kind() else { bug!("build_coroutine_di_node() called with non-coroutine type: `{:?}`", coroutine_type) }; @@ -158,8 +158,10 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), |cx, coroutine_type_di_node| { - let coroutine_layout = - cx.tcx.optimized_mir(coroutine_def_id).coroutine_layout().unwrap(); + let coroutine_layout = cx + .tcx + .coroutine_layout(coroutine_def_id, coroutine_args.as_coroutine().kind_ty()) + .unwrap(); let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = coroutine_type_and_layout.variants diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 1aec65cf94900..e521d5e259caa 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -5,6 +5,7 @@ use rustc_data_structures::{ fx::FxHashMap, stable_hasher::{HashStable, StableHasher}, }; +use rustc_macros::HashStable; use rustc_middle::{ bug, ty::{ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}, @@ -23,6 +24,8 @@ use crate::{ use super::{unknown_file_metadata, SmallVec, UNKNOWN_LINE_NUMBER}; mod private { + use rustc_macros::HashStable; + // This type cannot be constructed outside of this module because // it has a private field. We make use of this in order to prevent // `UniqueTypeId` from being constructed directly, without asserting diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index d3a851b40c0a2..4fdaa59e0e559 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -110,7 +110,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { .unstable_opts .dwarf_version .unwrap_or(sess.target.default_dwarf_version); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"Dwarf Version".as_ptr().cast(), @@ -118,7 +118,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { ); } else { // Indicate that we want CodeView debug information on MSVC - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"CodeView".as_ptr().cast(), @@ -127,7 +127,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { } // Prevent bitcode readers from deleting the debug info. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"Debug Info Version".as_ptr().cast(), diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 78c0725a63784..7117c4a0ed910 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -18,12 +18,11 @@ use crate::llvm; use crate::llvm::AttributePlace::Function; use crate::type_::Type; use crate::value::Value; +use itertools::Itertools; use rustc_codegen_ssa::traits::TypeMembershipMethods; +use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; -use rustc_symbol_mangling::typeid::{ - kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, - TypeIdOptions, -}; +use rustc_sanitizers::{cfi, kcfi}; use smallvec::SmallVec; /// Declare a function. @@ -141,51 +140,51 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if self.tcx.sess.is_sanitizer_cfi_enabled() { if let Some(instance) = instance { - let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::empty()); - self.set_type_metadata(llfn, typeid); - let typeid = - typeid_for_instance(self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS); - self.add_type_metadata(llfn, typeid); - let typeid = - typeid_for_instance(self.tcx, &instance, TypeIdOptions::NORMALIZE_INTEGERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_instance( - self.tcx, - &instance, - TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, - ); - self.add_type_metadata(llfn, typeid); + let mut typeids = FxIndexSet::default(); + for options in [ + cfi::TypeIdOptions::GENERALIZE_POINTERS, + cfi::TypeIdOptions::NORMALIZE_INTEGERS, + cfi::TypeIdOptions::USE_CONCRETE_SELF, + ] + .into_iter() + .powerset() + .map(cfi::TypeIdOptions::from_iter) + { + let typeid = cfi::typeid_for_instance(self.tcx, instance, options); + if typeids.insert(typeid.clone()) { + self.add_type_metadata(llfn, typeid); + } + } } else { - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); - self.set_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi( - self.tcx, - fn_abi, - TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, - ); - self.add_type_metadata(llfn, typeid); + for options in [ + cfi::TypeIdOptions::GENERALIZE_POINTERS, + cfi::TypeIdOptions::NORMALIZE_INTEGERS, + ] + .into_iter() + .powerset() + .map(cfi::TypeIdOptions::from_iter) + { + let typeid = cfi::typeid_for_fnabi(self.tcx, fn_abi, options); + self.add_type_metadata(llfn, typeid); + } } } if self.tcx.sess.is_sanitizer_kcfi_enabled() { // LLVM KCFI does not support multiple !kcfi_type attachments - let mut options = TypeIdOptions::empty(); + let mut options = kcfi::TypeIdOptions::empty(); if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() { - options.insert(TypeIdOptions::GENERALIZE_POINTERS); + options.insert(kcfi::TypeIdOptions::GENERALIZE_POINTERS); } if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() { - options.insert(TypeIdOptions::NORMALIZE_INTEGERS); + options.insert(kcfi::TypeIdOptions::NORMALIZE_INTEGERS); } if let Some(instance) = instance { - let kcfi_typeid = kcfi_typeid_for_instance(self.tcx, &instance, options); + let kcfi_typeid = kcfi::typeid_for_instance(self.tcx, instance, options); self.set_kcfi_type_metadata(llfn, kcfi_typeid); } else { - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + let kcfi_typeid = kcfi::typeid_for_fnabi(self.tcx, fn_abi, options); self.set_kcfi_type_metadata(llfn, kcfi_typeid); } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 467e02d55e332..56550dbfa4b8b 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf}; use rustc_middle::ty::{self, GenericArgsRef, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::{sym, Span, Symbol}; -use rustc_target::abi::{self, Align, HasDataLayout, Primitive}; +use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size}; use rustc_target::spec::{HasTargetSpec, PanicStrategy}; use std::cmp::Ordering; @@ -181,6 +181,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { simple_fn, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + Some(instance), ) } sym::likely => { @@ -263,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(()); } @@ -314,25 +315,32 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { Some((width, signed)) => match name { sym::ctlz | sym::cttz => { let y = self.const_bool(false); - self.call_intrinsic( + let ret = self.call_intrinsic( &format!("llvm.{name}.i{width}"), &[args[0].immediate(), y], - ) + ); + + self.intcast(ret, llret_ty, false) } sym::ctlz_nonzero => { let y = self.const_bool(true); let llvm_name = &format!("llvm.ctlz.i{width}"); - self.call_intrinsic(llvm_name, &[args[0].immediate(), y]) + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) } sym::cttz_nonzero => { let y = self.const_bool(true); let llvm_name = &format!("llvm.cttz.i{width}"); - self.call_intrinsic(llvm_name, &[args[0].immediate(), y]) + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::ctpop => { + let ret = self.call_intrinsic( + &format!("llvm.ctpop.i{width}"), + &[args[0].immediate()], + ); + self.intcast(ret, llret_ty, false) } - sym::ctpop => self.call_intrinsic( - &format!("llvm.ctpop.i{width}"), - &[args[0].immediate()], - ), sym::bswap => { if width == 8 { args[0].immediate() // byte swap a u8/i8 is just a no-op @@ -354,6 +362,10 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { // rotate = funnel shift with first two args the same let llvm_name = &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); + + // llvm expects shift to be the same type as the values, but rust always uses `u32` + let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); + self.call_intrinsic(llvm_name, &[val, val, raw_shift]) } sym::saturating_add | sym::saturating_sub => { @@ -427,7 +439,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 @@ -481,7 +493,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 @@ -539,7 +551,7 @@ fn catch_unwind_intrinsic<'ll>( ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.call(try_func_ty, None, None, try_func, &[data], None); + bx.call(try_func_ty, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx().data_layout.i32_align.abi; @@ -637,10 +649,11 @@ fn codegen_msvc_try<'ll>( // } // // More information can be found in libstd's seh.rs implementation. + let ptr_size = bx.tcx().data_layout.pointer_size; let ptr_align = bx.tcx().data_layout.pointer_align.abi; - let slot = bx.alloca(bx.type_ptr(), ptr_align); + let slot = bx.alloca(ptr_size, ptr_align); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, None); bx.switch_to_block(normal); bx.ret(bx.const_i32(0)); @@ -684,7 +697,7 @@ fn codegen_msvc_try<'ll>( let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]); let ptr = bx.load(bx.type_ptr(), slot, ptr_align); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None); bx.catch_ret(&funclet, caught); // The flag value of 64 indicates a "catch-all". @@ -692,7 +705,7 @@ fn codegen_msvc_try<'ll>( let flags = bx.const_i32(64); let null = bx.const_null(bx.type_ptr()); let funclet = bx.catch_pad(cs, &[null, flags, null]); - bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet), None); bx.catch_ret(&funclet, caught); bx.switch_to_block(caught); @@ -701,7 +714,7 @@ fn codegen_msvc_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -750,7 +763,7 @@ fn codegen_wasm_try<'ll>( // } // let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, None); bx.switch_to_block(normal); bx.ret(bx.const_i32(0)); @@ -766,7 +779,7 @@ fn codegen_wasm_try<'ll>( let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None); bx.catch_ret(&funclet, caught); bx.switch_to_block(caught); @@ -775,7 +788,7 @@ fn codegen_wasm_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -818,7 +831,7 @@ fn codegen_gnu_try<'ll>( let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None); bx.switch_to_block(then); bx.ret(bx.const_i32(0)); @@ -836,13 +849,13 @@ fn codegen_gnu_try<'ll>( bx.add_clause(vals, tydesc); let ptr = bx.extract_value(vals, 0); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, None); bx.ret(bx.const_i32(1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -882,7 +895,7 @@ fn codegen_emcc_try<'ll>( let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None); bx.switch_to_block(then); bx.ret(bx.const_i32(0)); @@ -908,25 +921,24 @@ fn codegen_emcc_try<'ll>( // We need to pass two values to catch_func (ptr and is_rust_panic), so // create an alloca and pass a pointer to that. + let ptr_size = bx.tcx().data_layout.pointer_size; let ptr_align = bx.tcx().data_layout.pointer_align.abi; let i8_align = bx.tcx().data_layout.i8_align.abi; - let catch_data_type = bx.type_struct(&[bx.type_ptr(), bx.type_bool()], false); - let catch_data = bx.alloca(catch_data_type, ptr_align); - let catch_data_0 = - bx.inbounds_gep(catch_data_type, catch_data, &[bx.const_usize(0), bx.const_usize(0)]); - bx.store(ptr, catch_data_0, ptr_align); - let catch_data_1 = - bx.inbounds_gep(catch_data_type, catch_data, &[bx.const_usize(0), bx.const_usize(1)]); + // Required in order for there to be no padding between the fields. + assert!(i8_align <= ptr_align); + let catch_data = bx.alloca(2 * ptr_size, ptr_align); + bx.store(ptr, catch_data, ptr_align); + let catch_data_1 = bx.inbounds_ptradd(catch_data, bx.const_usize(ptr_size.bytes())); bx.store(is_rust_panic, catch_data_1, i8_align); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None); + bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None, None); bx.ret(bx.const_i32(1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -972,7 +984,7 @@ fn get_rust_try_fn<'ll, 'tcx>( tcx, ty::Binder::dummy(tcx.mk_fn_sig( [i8p], - Ty::new_unit(tcx), + tcx.types.unit, false, hir::Unsafety::Unsafe, Abi::Rust, @@ -983,7 +995,7 @@ fn get_rust_try_fn<'ll, 'tcx>( tcx, ty::Binder::dummy(tcx.mk_fn_sig( [i8p, i8p], - Ty::new_unit(tcx), + tcx.types.unit, false, hir::Unsafety::Unsafe, Abi::Rust, @@ -1064,7 +1076,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, @@ -1129,7 +1141,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_shuffle_generic { let idx = fn_args[2] .expect_const() - .eval(tcx, ty::ParamEnv::reveal_all(), Some(span)) + .eval(tcx, ty::ParamEnv::reveal_all(), span) .unwrap() .unwrap_branch(); let n = idx.len() as u64; @@ -1362,7 +1374,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8)); // Convert the integer to a byte array - let ptr = bx.alloca(bx.type_ix(expected_bytes * 8), Align::ONE); + let ptr = bx.alloca(Size::from_bytes(expected_bytes), Align::ONE); bx.store(ze, ptr, Align::ONE); let array_ty = bx.type_array(bx.type_i8(), expected_bytes); return Ok(bx.load(array_ty, ptr, Align::ONE)); @@ -1439,6 +1451,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + None, ); Ok(c) } @@ -1483,7 +1496,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( v.normalize(bx.target_spec().pointer_width).bit_width().unwrap() ), ty::Float(v) => format!("v{}f{}", vec_len, v.bit_width()), - ty::RawPtr(_) => format!("v{}p0", vec_len), + ty::RawPtr(_, _) => format!("v{}p0", vec_len), _ => unreachable!(), } } @@ -1493,7 +1506,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ty::Int(v) => cx.type_int_from_ty(v), ty::Uint(v) => cx.type_uint_from_ty(v), ty::Float(v) => cx.type_float_from_ty(v), - ty::RawPtr(_) => cx.type_ptr(), + ty::RawPtr(_, _) => cx.type_ptr(), _ => unreachable!(), }; cx.type_vector(elem_ty, vec_len) @@ -1548,8 +1561,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require!( matches!( - element_ty1.kind(), - ty::RawPtr(p) if p.ty == in_elem && p.ty.kind() == element_ty0.kind() + *element_ty1.kind(), + ty::RawPtr(p_ty, _) if p_ty == in_elem && p_ty.kind() == element_ty0.kind() ), InvalidMonomorphization::ExpectedElementType { span, @@ -1607,6 +1620,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[1].immediate(), alignment, mask, args[0].immediate()], None, + None, ); return Ok(v); } @@ -1654,8 +1668,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require!( matches!( - pointer_ty.kind(), - ty::RawPtr(p) if p.ty == values_elem && p.ty.kind() == values_elem.kind() + *pointer_ty.kind(), + ty::RawPtr(p_ty, _) if p_ty == values_elem && p_ty.kind() == values_elem.kind() ), InvalidMonomorphization::ExpectedElementType { span, @@ -1706,6 +1720,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[1].immediate(), alignment, mask, args[2].immediate()], None, + None, ); return Ok(v); } @@ -1746,8 +1761,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // The second argument must be a mutable pointer type matching the element type require!( matches!( - pointer_ty.kind(), - ty::RawPtr(p) if p.ty == values_elem && p.ty.kind() == values_elem.kind() && p.mutbl.is_mut() + *pointer_ty.kind(), + ty::RawPtr(p_ty, p_mutbl) if p_ty == values_elem && p_ty.kind() == values_elem.kind() && p_mutbl.is_mut() ), InvalidMonomorphization::ExpectedElementType { span, @@ -1799,6 +1814,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[2].immediate(), args[1].immediate(), alignment, mask], None, + None, ); return Ok(v); } @@ -1843,9 +1859,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require!( matches!( - element_ty1.kind(), - ty::RawPtr(p) - if p.ty == in_elem && p.mutbl.is_mut() && p.ty.kind() == element_ty0.kind() + *element_ty1.kind(), + ty::RawPtr(p_ty, p_mutbl) + if p_ty == in_elem && p_mutbl.is_mut() && p_ty.kind() == element_ty0.kind() ), InvalidMonomorphization::ExpectedElementType { span, @@ -1904,6 +1920,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[0].immediate(), args[1].immediate(), alignment, mask], None, + None, ); return Ok(v); } @@ -2074,8 +2091,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); match in_elem.kind() { - ty::RawPtr(p) => { - let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| { + ty::RawPtr(p_ty, _) => { + let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); require!( @@ -2088,8 +2105,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } } match out_elem.kind() { - ty::RawPtr(p) => { - let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| { + ty::RawPtr(p_ty, _) => { + let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); require!( @@ -2105,7 +2122,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return Ok(args[0].immediate()); } - if name == sym::simd_expose_addr { + if name == sym::simd_expose_provenance { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); require!( in_len == out_len, @@ -2120,7 +2137,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); match in_elem.kind() { - ty::RawPtr(_) => {} + ty::RawPtr(_, _) => {} _ => { return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem }) } @@ -2133,7 +2150,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return Ok(bx.ptrtoint(args[0].immediate(), llret_ty)); } - if name == sym::simd_from_exposed_addr { + if name == sym::simd_with_exposed_provenance { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); require!( in_len == out_len, @@ -2152,7 +2169,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( _ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: in_elem }), } match out_elem.kind() { - ty::RawPtr(_) => {} + ty::RawPtr(_, _) => {} _ => { return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem }) } @@ -2352,11 +2369,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[0].immediate(), bx.const_int(bx.type_i1(), 0)], None, + None, )) } else { let fn_ty = bx.type_func(&[vec_ty], vec_ty); let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None)) + Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None)) }; } @@ -2409,7 +2427,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty); let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None); + let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None, None); return Ok(v); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index c84461e53eb1b..1cecf682e5db6 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -15,8 +15,6 @@ #![feature(let_chains)] #![feature(impl_trait_in_assoc_type)] -#[macro_use] -extern crate rustc_macros; #[macro_use] extern crate tracing; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d34c289887e12..7a34e21628dc6 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -200,6 +200,8 @@ pub enum AttributeKind { AllocAlign = 39, SanitizeSafeStack = 40, FnRetThunkExtern = 41, + Writable = 42, + DeadOnUnwind = 43, } /// LLVMIntPredicate @@ -1519,7 +1521,7 @@ extern "C" { #[link(name = "llvm-wrapper", kind = "static")] extern "C" { - pub fn LLVMRustInstallFatalErrorHandler(); + pub fn LLVMRustInstallErrorHandlers(); pub fn LLVMRustDisableSystemDialogsOnCrash(); // Create and destroy contexts. @@ -1631,6 +1633,10 @@ extern "C" { // Miscellaneous instructions pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value; + pub fn LLVMRustGetInstrProfMCDCParametersIntrinsic(M: &Module) -> &Value; + pub fn LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(M: &Module) -> &Value; + pub fn LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(M: &Module) -> &Value; + pub fn LLVMRustBuildCall<'a>( B: &Builder<'a>, Ty: &'a Type, @@ -1801,12 +1807,21 @@ extern "C" { /// /// In order for Rust-C LTO to work, module flags must be compatible with Clang. What /// "compatible" means depends on the merge behaviors involved. - pub fn LLVMRustAddModuleFlag( + pub fn LLVMRustAddModuleFlagU32( M: &Module, merge_behavior: LLVMModFlagBehavior, name: *const c_char, value: u32, ); + + pub fn LLVMRustAddModuleFlagString( + M: &Module, + merge_behavior: LLVMModFlagBehavior, + name: *const c_char, + value: *const c_char, + value_len: size_t, + ); + pub fn LLVMRustHasModuleFlag(M: &Module, name: *const c_char, len: size_t) -> bool; pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>; @@ -2294,6 +2309,7 @@ extern "C" { Members: *const &RustArchiveMember<'_>, WriteSymbtab: bool, Kind: ArchiveKind, + isEC: bool, ) -> LLVMRustResult; pub fn LLVMRustArchiveMemberNew<'a>( Filename: *const c_char, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 4f5cc575da6e5..6ab1eea959761 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -11,6 +11,7 @@ pub use self::RealPredicate::*; use libc::c_uint; use rustc_data_structures::small_c_str::SmallCStr; use rustc_llvm::RustString; +use rustc_target::abi::Align; use std::cell::RefCell; use std::ffi::{CStr, CString}; use std::str::FromStr; @@ -229,9 +230,9 @@ pub fn set_visibility(llglobal: &Value, visibility: Visibility) { } } -pub fn set_alignment(llglobal: &Value, bytes: usize) { +pub fn set_alignment(llglobal: &Value, align: Align) { unsafe { - ffi::LLVMSetAlignment(llglobal, bytes as c_uint); + ffi::LLVMSetAlignment(llglobal, align.bytes() as c_uint); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index e32c38644aafb..5552b38102511 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -49,7 +49,7 @@ unsafe fn configure_llvm(sess: &Session) { let mut llvm_c_strs = Vec::with_capacity(n_args + 1); let mut llvm_args = Vec::with_capacity(n_args + 1); - llvm::LLVMRustInstallFatalErrorHandler(); + llvm::LLVMRustInstallErrorHandlers(); // On Windows, an LLVM assertion will open an Abort/Retry/Ignore dialog // box for the purpose of launching a debugger. However, on CI this will // cause it to hang until it times out, which can take several hours. @@ -270,9 +270,10 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> { "sve2-bitperm", TargetFeatureFoldStrength::EnableOnly("neon"), ), - // The unaligned-scalar-mem feature was renamed to fast-unaligned-access. - ("riscv32" | "riscv64", "fast-unaligned-access") if get_version().0 <= 17 => { - LLVMFeature::new("unaligned-scalar-mem") + // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called + // `fast-unaligned-access`. In LLVM 19, it was split back out. + ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => { + LLVMFeature::new("fast-unaligned-access") } // For LLVM 18, enable the evex512 target feature if a avx512 target feature is enabled. ("x86", s) if get_version().0 >= 18 && s.starts_with("avx512") => { diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index af1bbda4d08a5..a00f09dc40da6 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -281,9 +281,6 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn reg_backend_type(&self, ty: &Reg) -> &'ll Type { ty.llvm_type(self) } - fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option { - layout.scalar_copy_llvm_type(self) - } } impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index d10a083765b89..40ed6baa61072 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -5,7 +5,6 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; -use rustc_target::abi::HasDataLayout; use rustc_target::abi::{Abi, Align, FieldsShape}; use rustc_target::abi::{Int, Pointer, F128, F16, F32, F64}; use rustc_target::abi::{Scalar, Size, Variants}; @@ -166,7 +165,6 @@ pub trait LayoutLlvmExt<'tcx> { index: usize, immediate: bool, ) -> &'a Type; - fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type>; } impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { @@ -308,44 +306,4 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { self.scalar_llvm_type_at(cx, scalar) } - - fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type> { - debug_assert!(self.is_sized()); - - // FIXME: this is a fairly arbitrary choice, but 128 bits on WASM - // (matching the 128-bit SIMD types proposal) and 256 bits on x64 - // (like AVX2 registers) seems at least like a tolerable starting point. - let threshold = cx.data_layout().pointer_size * 4; - if self.layout.size() > threshold { - return None; - } - - // Vectors, even for non-power-of-two sizes, have the same layout as - // arrays but don't count as aggregate types - // While LLVM theoretically supports non-power-of-two sizes, and they - // often work fine, sometimes x86-isel deals with them horribly - // (see #115212) so for now only use power-of-two ones. - if let FieldsShape::Array { count, .. } = self.layout.fields() - && count.is_power_of_two() - && let element = self.field(cx, 0) - && element.ty.is_integral() - { - // `cx.type_ix(bits)` is tempting here, but while that works great - // for things that *stay* as memory-to-memory copies, it also ends - // up suppressing vectorization as it introduces shifts when it - // extracts all the individual values. - - let ety = element.llvm_type(cx); - if *count == 1 { - // Emitting `<1 x T>` would be silly; just use the scalar. - return Some(ety); - } else { - return Some(cx.type_vector(ety, *count)); - } - } - - // FIXME: The above only handled integer arrays; surely more things - // would also be possible. Be careful about provenance, though! - None - } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 7851b9e8e038c..f347a7fb0bb10 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -ar_archive_writer = "0.1.5" +ar_archive_writer = "0.2.0" bitflags = "2.4.1" cc = "1.0.90" itertools = "0.12" @@ -25,6 +25,7 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } +rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 5ba66d1be4329..d159fe58d3eef 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,6 +16,9 @@ codegen_ssa_cgu_not_recorded = codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. +codegen_ssa_compiler_builtins_cannot_call = + `compiler_builtins` cannot call functions through upstream monomorphizations; encountered invalid call from `{$caller}` to `{$callee}` + codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$error} diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 22b58c13949e2..c99118f5156d1 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -13,7 +13,7 @@ use object::read::macho::FatArch; use tempfile::Builder as TempFileBuilder; use std::error::Error; -use std::fs::File; +use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -231,7 +231,11 @@ impl<'a> ArArchiveBuilder<'a> { "gnu" => ArchiveKind::Gnu, "bsd" => ArchiveKind::Bsd, "darwin" => ArchiveKind::Darwin, - "coff" => ArchiveKind::Coff, + "coff" => { + // FIXME: ar_archive_writer doesn't support COFF archives yet. + // https://github.com/rust-lang/ar_archive_writer/issues/9 + ArchiveKind::Gnu + } "aix_big" => ArchiveKind::AixBig, kind => { self.sess.dcx().emit_fatal(UnknownArchiveKind { kind }); @@ -276,19 +280,21 @@ impl<'a> ArArchiveBuilder<'a> { // This prevents programs (including rustc) from attempting to read a partial archive. // It also enables writing an archive with the same filename as a dependency on Windows as // required by a test. - let mut archive_tmpfile = TempFileBuilder::new() + // The tempfile crate currently uses 0o600 as mode for the temporary files and directories + // it creates. We need it to be the default mode for back compat reasons however. (See + // #107495) To handle this we are telling tempfile to create a temporary directory instead + // and then inside this directory create a file using File::create. + let archive_tmpdir = TempFileBuilder::new() .suffix(".temp-archive") - .tempfile_in(output.parent().unwrap_or_else(|| Path::new(""))) - .map_err(|err| io_error_context("couldn't create a temp file", err))?; - - write_archive_to_stream( - archive_tmpfile.as_file_mut(), - &entries, - true, - archive_kind, - true, - false, - )?; + .tempdir_in(output.parent().unwrap_or_else(|| Path::new(""))) + .map_err(|err| { + io_error_context("couldn't create a directory for the temp file", err) + })?; + let archive_tmpfile_path = archive_tmpdir.path().join("tmp.a"); + let mut archive_tmpfile = File::create_new(&archive_tmpfile_path) + .map_err(|err| io_error_context("couldn't create the temp file", err))?; + + write_archive_to_stream(&mut archive_tmpfile, &entries, archive_kind, false)?; let any_entries = !entries.is_empty(); drop(entries); @@ -296,9 +302,11 @@ impl<'a> ArArchiveBuilder<'a> { // output archive to the same location as an input archive on Windows. drop(self.src_archives); - archive_tmpfile - .persist(output) - .map_err(|err| io_error_context("failed to rename archive file", err.error))?; + fs::rename(archive_tmpfile_path, output) + .map_err(|err| io_error_context("failed to rename archive file", err))?; + archive_tmpdir + .close() + .map_err(|err| io_error_context("failed to remove temporary directory", err))?; Ok(any_entries) } diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 9b0ba34135c11..3a89be89951b8 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -100,12 +100,6 @@ impl Command { Program::Lld(ref p, flavor) => { let mut c = process::Command::new(p); c.arg("-flavor").arg(flavor.as_str()); - if let LldFlavor::Wasm = flavor { - // LLVM expects host-specific formatting for @file - // arguments, but we always generate posix formatted files - // at this time. Indicate as such. - c.arg("--rsp-quoting=posix"); - } c } }; diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f5e8d5fc92a91..1f691d14c5320 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -8,9 +8,11 @@ use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; +use rustc_middle::bug; 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 +24,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 +44,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 +53,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>); - -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 +63,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 +302,6 @@ fn link_rlib<'a>( flavor: RlibFlavor, tmpdir: &MaybeTempDir, ) -> Result, 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 +375,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 +449,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, +fn collate_raw_dylibs<'a>( + sess: &Session, + used_libraries: impl IntoIterator, ) -> Result)>, ErrorGuaranteed> { // Use index maps to preserve original order of imports and libraries. let mut dylib_table = FxIndexMap::>::default(); @@ -513,8 +498,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 +611,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 +715,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, @@ -785,7 +766,7 @@ fn link_natively<'a>( let mut i = 0; loop { i += 1; - prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, tmpdir)); + prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, flavor, tmpdir)); let Ok(ref output) = prog else { break; }; @@ -1099,8 +1080,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 +1318,9 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { sess: &Session, linker: Option, flavor: Option, + 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 +1368,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 +1408,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 +1416,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 +1450,6 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) { } } -fn archive_search_paths(sess: &Session) -> Vec { - sess.target_filesearch(PathKind::Native).search_path_dirs() -} - #[derive(PartialEq)] enum RlibFlavor { Normal, @@ -1576,6 +1577,7 @@ fn exec_linker( sess: &Session, cmd: &Command, out_filename: &Path, + flavor: LinkerFlavor, tmpdir: &Path, ) -> io::Result { // When attempting to spawn the linker we run a risk of blowing out the @@ -1584,9 +1586,9 @@ fn exec_linker( // // Here we attempt to handle errors from the OS saying "your list of // arguments is too big" by reinvoking the linker again with an `@`-file - // that contains all the arguments. The theory is that this is then - // accepted on all linkers and the linker will read all its options out of - // there instead of looking at the command line. + // that contains all the arguments (aka 'response' files). + // The theory is that this is then accepted on all linkers and the linker + // will read all its options out of there instead of looking at the command line. if !cmd.very_likely_to_exceed_some_spawn_limit() { match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() { Ok(child) => { @@ -1606,8 +1608,12 @@ fn exec_linker( let mut args = String::new(); for arg in cmd2.take_args() { args.push_str( - &Escape { arg: arg.to_str().unwrap(), is_like_msvc: sess.target.is_like_msvc } - .to_string(), + &Escape { + arg: arg.to_str().unwrap(), + // LLD also uses MSVC-like parsing for @-files by default when running on windows hosts + is_like_msvc: sess.target.is_like_msvc || (cfg!(windows) && flavor.uses_lld()), + } + .to_string(), ); args.push('\n'); } @@ -2054,7 +2060,7 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: /// Add options making relocation sections in the produced ELF files read-only /// and suppressing lazy binding. fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) { - match sess.opts.unstable_opts.relro_level.unwrap_or(sess.target.relro_level) { + match sess.opts.cg.relro_level.unwrap_or(sess.target.relro_level) { RelroLevel::Full => cmd.full_relro(), RelroLevel::Partial => cmd.partial_relro(), RelroLevel::Off => cmd.no_relro(), @@ -2084,14 +2090,14 @@ fn add_rpath_args( .map(|(path, _)| &**path) }) .collect::>(); - let mut rpath_config = RPathConfig { + let rpath_config = RPathConfig { libs: &*libs, out_filename: out_filename.to_path_buf(), has_rpath: sess.target.has_rpath, is_like_osx: sess.target.is_like_osx, linker_is_gnu: sess.target.linker_flavor.is_gnu(), }; - cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); + cmd.args(&rpath::get_rpath_flags(&rpath_config)); } } @@ -2103,10 +2109,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, @@ -2495,7 +2501,6 @@ fn add_native_libs_from_crate( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, - search_paths: &SearchPaths, bundled_libs: &FxIndexSet, cnum: CrateNum, link_static: bool, @@ -2558,7 +2563,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); } } } @@ -2572,7 +2577,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 { @@ -2619,7 +2624,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; @@ -2629,7 +2633,6 @@ fn add_local_native_libraries( archive_builder_builder, codegen_results, tmpdir, - &search_paths, &Default::default(), LOCAL_CRATE, link_static, @@ -2638,9 +2641,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, @@ -2661,7 +2664,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. @@ -2718,7 +2720,6 @@ fn add_upstream_rust_crates<'a>( archive_builder_builder, codegen_results, tmpdir, - &search_paths, &bundled_libs, cnum, link_static, @@ -2736,7 +2737,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 @@ -2758,7 +2758,6 @@ fn add_upstream_native_libraries( archive_builder_builder, codegen_results, tmpdir, - &search_paths, &Default::default(), cnum, link_static, @@ -2777,7 +2776,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()) }; @@ -2810,9 +2809,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, @@ -2941,7 +2940,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { let os = &sess.target.os; let llvm_target = &sess.target.llvm_target; if sess.target.vendor != "apple" - || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "macos") + || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "visionos" | "macos") || !matches!(flavor, LinkerFlavor::Darwin(..)) { return; @@ -2966,6 +2965,8 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { ("arm64_32", "watchos") => "watchos", ("aarch64", "watchos") if llvm_target.ends_with("-simulator") => "watchsimulator", ("aarch64", "watchos") => "watchos", + ("aarch64", "visionos") if llvm_target.ends_with("-simulator") => "xrsimulator", + ("aarch64", "visionos") => "xros", ("arm", "watchos") => "watchos", (_, "macos") => "macosx", _ => { @@ -2983,13 +2984,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> { @@ -3022,6 +3039,11 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result {} "watchsimulator" if sdkroot.contains("WatchOS.platform") || sdkroot.contains("MacOSX.platform") => {} + "xros" + if sdkroot.contains("XRSimulator.platform") + || sdkroot.contains("MacOSX.platform") => {} + "xrsimulator" + if sdkroot.contains("XROS.platform") || sdkroot.contains("MacOSX.platform") => {} // Ignore `SDKROOT` if it's not a valid path. _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} _ => return Ok(sdkroot), diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index f5640ea26bc8c..f85056f8ad410 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; @@ -13,6 +12,7 @@ use std::{env, mem, str}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; +use rustc_middle::bug; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind}; @@ -172,13 +172,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 +476,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 +485,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 +812,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}")); @@ -922,52 +903,45 @@ impl<'a> Linker for MsvcLinker<'a> { } } - fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { - match strip { - Strip::None => { - // This will cause the Microsoft linker to generate a PDB file - // from the CodeView line tables in the object files. - self.cmd.arg("/DEBUG"); - - // Default to emitting only the file name of the PDB file into - // the binary instead of the full path. Emitting the full path - // may leak private information (such as user names). - // See https://github.com/rust-lang/rust/issues/87825. - // - // This default behavior can be overridden by explicitly passing - // `-Clink-arg=/PDBALTPATH:...` to rustc. - self.cmd.arg("/PDBALTPATH:%_PDB%"); - - // This will cause the Microsoft linker to embed .natvis info into the PDB file - let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc"); - if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { - for entry in natvis_dir { - match entry { - Ok(entry) => { - let path = entry.path(); - if path.extension() == Some("natvis".as_ref()) { - let mut arg = OsString::from("/NATVIS:"); - arg.push(path); - self.cmd.arg(arg); - } - } - Err(error) => { - self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error }); - } + fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { + // This will cause the Microsoft linker to generate a PDB file + // from the CodeView line tables in the object files. + self.cmd.arg("/DEBUG"); + + // Default to emitting only the file name of the PDB file into + // the binary instead of the full path. Emitting the full path + // may leak private information (such as user names). + // See https://github.com/rust-lang/rust/issues/87825. + // + // This default behavior can be overridden by explicitly passing + // `-Clink-arg=/PDBALTPATH:...` to rustc. + self.cmd.arg("/PDBALTPATH:%_PDB%"); + + // This will cause the Microsoft linker to embed .natvis info into the PDB file + let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc"); + if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { + for entry in natvis_dir { + match entry { + Ok(entry) => { + let path = entry.path(); + if path.extension() == Some("natvis".as_ref()) { + let mut arg = OsString::from("/NATVIS:"); + arg.push(path); + self.cmd.arg(arg); } } - } - - // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file - for path in natvis_debugger_visualizers { - let mut arg = OsString::from("/NATVIS:"); - arg.push(path); - self.cmd.arg(arg); + Err(error) => { + self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error }); + } } } - Strip::Debuginfo | Strip::Symbols => { - self.cmd.arg("/DEBUG:NONE"); - } + } + + // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file + for path in natvis_debugger_visualizers { + let mut arg = OsString::from("/NATVIS:"); + arg.push(path); + self.cmd.arg(arg); } } @@ -1064,13 +1038,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 +1211,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 +1358,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 +1536,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 +1741,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 +1823,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 +1914,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/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index ab1bc0b6cd2eb..264a98844ad67 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -16,6 +16,7 @@ use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice}; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::fs::METADATA_FILENAME; use rustc_metadata::EncodedMetadata; +use rustc_middle::bug; use rustc_session::Session; use rustc_span::sym; use rustc_target::abi::Endian; diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 60346228625fa..ebbf49af18487 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -12,7 +12,7 @@ pub struct RPathConfig<'a> { pub linker_is_gnu: bool, } -pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec { +pub fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec { // No rpath on windows if !config.has_rpath { return Vec::new(); @@ -52,7 +52,7 @@ fn rpaths_to_flags(rpaths: Vec) -> Vec { ret } -fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec { +fn get_rpaths(config: &RPathConfig<'_>) -> Vec { debug!("output: {:?}", config.out_filename.display()); debug!("libs:"); for libpath in config.libs { @@ -73,11 +73,11 @@ fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec { minimize_rpaths(&rpaths) } -fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec { +fn get_rpaths_relative_to_output(config: &RPathConfig<'_>) -> Vec { config.libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() } -fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> OsString { +fn get_rpath_relative_to_output(config: &RPathConfig<'_>, lib: &Path) -> OsString { // Mac doesn't appear to support $ORIGIN let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" }; diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 87b6f0e914c35..0e335bf00cf05 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -6,6 +6,7 @@ use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE} use rustc_data_structures::unord::UnordMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{ metadata_symbol_name, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, @@ -81,10 +82,6 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap {} @@ -367,6 +364,24 @@ fn exported_symbols_provider_local( }, )); } + MonoItem::Fn(Instance { + def: InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty)), + args, + }) => { + // A little sanity-check + debug_assert_eq!( + args.non_erasable_generics(tcx, def_id).skip(1).next(), + Some(GenericArgKind::Type(ty)) + ); + symbols.push(( + ExportedSymbol::AsyncDropGlueCtorShim(ty), + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); + } _ => { // Any other symbols don't qualify for sharing } @@ -389,6 +404,7 @@ fn upstream_monomorphizations_provider( let mut instances: DefIdMap> = Default::default(); let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn(); + let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn(); for &cnum in cnums.iter() { for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() { @@ -403,6 +419,18 @@ fn upstream_monomorphizations_provider( continue; } } + ExportedSymbol::AsyncDropGlueCtorShim(ty) => { + if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id { + ( + async_drop_in_place_fn_def_id, + tcx.mk_args(&[tcx.lifetimes.re_erased.into(), ty.into()]), + ) + } else { + // `drop_in_place` in place does not exist, don't try + // to use it. + continue; + } + } ExportedSymbol::NonGeneric(..) | ExportedSymbol::ThreadLocalShim(..) | ExportedSymbol::NoDefId(..) => { @@ -538,6 +566,13 @@ pub fn symbol_name_for_instance_in_crate<'tcx>( Instance::resolve_drop_in_place(tcx, ty), instantiating_crate, ), + ExportedSymbol::AsyncDropGlueCtorShim(ty) => { + rustc_symbol_mangling::symbol_name_for_instance_in_crate( + tcx, + Instance::resolve_async_drop_in_place(tcx, ty), + instantiating_crate, + ) + } ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(), } } @@ -586,6 +621,9 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>( // DropGlue always use the Rust calling convention and thus follow the target's default // symbol decoration scheme. ExportedSymbol::DropGlue(..) => None, + // AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the + // target's default symbol decoration scheme. + ExportedSymbol::AsyncDropGlueCtorShim(..) => None, // NoDefId always follow the target's default symbol decoration scheme. ExportedSymbol::NoDefId(..) => None, // ThreadLocalShim always follow the target's default symbol decoration scheme. diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 4eda4c2f08c69..53ba0da7d0490 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -26,6 +26,7 @@ use rustc_incremental::{ }; use rustc_metadata::fs::copy_to_stdout; use rustc_metadata::EncodedMetadata; +use rustc_middle::bug; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::exported_symbols::SymbolExportInfo; use rustc_middle::ty::TyCtxt; @@ -528,12 +529,20 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) { let mut files = Vec::new(); if let Some(object_file_path) = &module.object { - files.push(("o", object_file_path.as_path())); + files.push((OutputType::Object.extension(), object_file_path.as_path())); } if let Some(dwarf_object_file_path) = &module.dwarf_object { files.push(("dwo", dwarf_object_file_path.as_path())); } - + if let Some(path) = &module.assembly { + files.push((OutputType::Assembly.extension(), path.as_path())); + } + if let Some(path) = &module.llvm_ir { + files.push((OutputType::LlvmAssembly.extension(), path.as_path())); + } + if let Some(path) = &module.bytecode { + files.push((OutputType::Bitcode.extension(), path.as_path())); + } if let Some((id, product)) = copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, files.as_slice()) { @@ -592,7 +601,7 @@ fn produce_final_output_artifacts( .unwrap() .to_owned(); - if crate_output.outputs.contains_key(&output_type) { + if crate_output.outputs.contains_explicit_name(&output_type) { // 2) Multiple codegen units, with `--emit foo=some_name`. We have // no good solution for this case, so warn the user. sess.dcx().emit_warn(errors::IgnoringEmitPath { extension }); @@ -899,8 +908,6 @@ fn execute_copy_from_cache_work_item( module: CachedModuleCodegen, module_config: &ModuleConfig, ) -> WorkItemResult { - assert!(module_config.emit_obj != EmitObj::None); - let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { @@ -920,12 +927,6 @@ fn execute_copy_from_cache_work_item( } }; - let object = load_from_incr_comp_dir( - cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)), - module.source.saved_files.get("o").unwrap_or_else(|| { - cgcx.create_dcx().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) - }), - ); let dwarf_object = module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| { let dwarf_obj_out = cgcx @@ -937,12 +938,33 @@ fn execute_copy_from_cache_work_item( load_from_incr_comp_dir(dwarf_obj_out, saved_dwarf_object_file) }); + let load_from_incr_cache = |perform, output_type: OutputType| { + if perform { + let saved_file = module.source.saved_files.get(output_type.extension())?; + let output_path = cgcx.output_filenames.temp_path(output_type, Some(&module.name)); + load_from_incr_comp_dir(output_path, &saved_file) + } else { + None + } + }; + + let should_emit_obj = module_config.emit_obj != EmitObj::None; + let assembly = load_from_incr_cache(module_config.emit_asm, OutputType::Assembly); + let llvm_ir = load_from_incr_cache(module_config.emit_ir, OutputType::LlvmAssembly); + let bytecode = load_from_incr_cache(module_config.emit_bc, OutputType::Bitcode); + let object = load_from_incr_cache(should_emit_obj, OutputType::Object); + if should_emit_obj && object.is_none() { + cgcx.create_dcx().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) + } + WorkItemResult::Finished(CompiledModule { name: module.name, kind: ModuleKind::Regular, object, dwarf_object, - bytecode: None, + bytecode, + assembly, + llvm_ir, }) } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index c316d19e04119..358c24bfb823c 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -5,14 +5,14 @@ use crate::back::write::{ compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen, }; -use crate::common::{IntPredicate, RealPredicate, TypeKind}; +use crate::common::{self, IntPredicate, RealPredicate, TypeKind}; use crate::errors; use crate::meth; use crate::mir; use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; -use crate::{CachedModuleCodegen, CompiledModule, CrateInfo, MemFlags, ModuleCodegen, ModuleKind}; +use crate::{CachedModuleCodegen, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind}; use rustc_ast::expand::allocator::{global_fn_name, AllocatorKind, ALLOCATOR_METHODS}; use rustc_attr as attr; @@ -24,6 +24,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_metadata::EncodedMetadata; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; use rustc_middle::middle::exported_symbols; @@ -33,11 +34,11 @@ use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; -use rustc_session::config::{self, CrateType, EntryFnType, OutputType}; +use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Symbol; -use rustc_target::abi::{Align, FIRST_VARIANT}; +use rustc_target::abi::FIRST_VARIANT; use std::cmp; use std::collections::BTreeSet; @@ -193,8 +194,8 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> (Bx::Value, Bx::Value) { debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty); match (src_ty.kind(), dst_ty.kind()) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) - | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { + (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) + | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => { assert_eq!(bx.cx().type_is_sized(a), old_info.is_none()); (src, unsized_info(bx, a, b, old_info)) } @@ -282,15 +283,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } if src_f.layout.ty == dst_f.layout.ty { - memcpy_ty( - bx, - dst_f.llval, - dst_f.align, - src_f.llval, - src_f.align, - src_f.layout, - MemFlags::empty(), - ); + bx.typed_place_copy(dst_f, src_f); } else { coerce_unsized_into(bx, src_f, dst_f); } @@ -300,14 +293,35 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +/// Returns `rhs` sufficiently masked, truncated, and/or extended so that +/// it can be used to shift `lhs`. +/// +/// Shifts in MIR are all allowed to have mismatched LHS & RHS types. +/// The shift methods in `BuilderMethods`, however, are fully homogeneous +/// (both parameters and the return type are all the same type). +/// +/// If `is_unchecked` is false, this masks the RHS to ensure it stays in-bounds, +/// as the `BuilderMethods` shifts are UB for out-of-bounds shift amounts. +/// For 32- and 64-bit types, this matches the semantics +/// of Java. (See related discussion on #1877 and #10183.) +/// +/// If `is_unchecked` is true, this does no masking, and adds sufficient `assume` +/// calls or operation flags to preserve as much freedom to optimize as possible. +pub fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, lhs: Bx::Value, - rhs: Bx::Value, + mut rhs: Bx::Value, + is_unchecked: bool, ) -> Bx::Value { // Shifts may have any size int on the rhs let mut rhs_llty = bx.cx().val_ty(rhs); let mut lhs_llty = bx.cx().val_ty(lhs); + + let mask = common::shift_mask_val(bx, lhs_llty, rhs_llty, false); + if !is_unchecked { + rhs = bx.and(rhs, mask); + } + if bx.cx().type_kind(rhs_llty) == TypeKind::Vector { rhs_llty = bx.cx().element_type(rhs_llty) } @@ -317,6 +331,12 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let rhs_sz = bx.cx().int_width(rhs_llty); let lhs_sz = bx.cx().int_width(lhs_llty); if lhs_sz < rhs_sz { + if is_unchecked && bx.sess().opts.optimize != OptLevel::No { + // FIXME: Use `trunc nuw` once that's available + let inrange = bx.icmp(IntPredicate::IntULE, rhs, mask); + bx.assume(inrange); + } + bx.trunc(rhs, lhs_llty) } else if lhs_sz > rhs_sz { // We zero-extend even if the RHS is signed. So e.g. `(x: i32) << -1i8` will zero-extend the @@ -355,30 +375,6 @@ pub fn wants_new_eh_instructions(sess: &Session) -> bool { wants_wasm_eh(sess) || wants_msvc_seh(sess) } -pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - dst: Bx::Value, - dst_align: Align, - src: Bx::Value, - src_align: Align, - layout: TyAndLayout<'tcx>, - flags: MemFlags, -) { - let size = layout.size.bytes(); - if size == 0 { - return; - } - - if flags == MemFlags::empty() - && let Some(bty) = bx.cx().scalar_copy_backend_type(layout) - { - let temp = bx.load(bty, src, src_align); - bx.store(temp, dst, dst_align); - } else { - bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags); - } -} - pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, instance: Instance<'tcx>, @@ -462,27 +458,34 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let ptr_ty = cx.type_ptr(); let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx); - let (start_fn, start_ty, args) = if let EntryFnType::Main { sigpipe } = entry_type { + let (start_fn, start_ty, args, instance) = if let EntryFnType::Main { sigpipe } = entry_type + { let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None); - let start_fn = cx.get_fn_addr(ty::Instance::expect_resolve( + let start_instance = ty::Instance::expect_resolve( cx.tcx(), ty::ParamEnv::reveal_all(), start_def_id, cx.tcx().mk_args(&[main_ret_ty.into()]), - )); + ); + let start_fn = cx.get_fn_addr(start_instance); let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); let start_ty = cx.type_func(&[cx.val_ty(rust_main), isize_ty, ptr_ty, i8_ty], isize_ty); - (start_fn, start_ty, vec![rust_main, arg_argc, arg_argv, arg_sigpipe]) + ( + start_fn, + start_ty, + vec![rust_main, arg_argc, arg_argv, arg_sigpipe], + Some(start_instance), + ) } else { debug!("using user-defined start fn"); let start_ty = cx.type_func(&[isize_ty, ptr_ty], isize_ty); - (rust_main, start_ty, vec![arg_argc, arg_argv]) + (rust_main, start_ty, vec![arg_argc, arg_argv], None) }; - let result = bx.call(start_ty, None, None, start_fn, &args, None); + let result = bx.call(start_ty, None, None, start_fn, &args, None, instance); if cx.sess().target.os.contains("uefi") { bx.ret(result); } else { @@ -506,7 +509,7 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let ptr_size = bx.tcx().data_layout.pointer_size; let ptr_align = bx.tcx().data_layout.pointer_align.abi; let arg_argc = bx.const_int(cx.type_isize(), 2); - let arg_argv = bx.alloca(cx.type_array(cx.type_ptr(), 2), ptr_align); + let arg_argv = bx.alloca(2 * ptr_size, ptr_align); bx.store(param_handle, arg_argv, ptr_align); let arg_argv_el1 = bx.inbounds_ptradd(arg_argv, bx.const_usize(ptr_size.bytes())); bx.store(param_system_table, arg_argv_el1, ptr_align); @@ -622,6 +625,8 @@ pub fn codegen_crate( object: Some(file_name), dwarf_object: None, bytecode: None, + assembly: None, + llvm_ir: None, } }) }); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 9c9e134f0337e..c28b0d644e675 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -435,7 +435,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::repr => { codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list() && let [item] = items.as_slice() - && let Some((sym::align, literal)) = item.name_value_literal() + && let Some((sym::align, literal)) = item.singleton_lit_list() { rustc_attr::parse_alignment(&literal.kind) .map_err(|msg| { diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 44a2434238dad..e4a36b3f59187 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -2,10 +2,11 @@ use rustc_hir::LangItem; use rustc_middle::mir; -use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; +use rustc_middle::ty::Instance; +use rustc_middle::ty::{self, layout::TyAndLayout, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_span::Span; -use crate::base; use crate::traits::*; #[derive(Copy, Clone)] @@ -120,49 +121,11 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &Bx, span: Option, li: LangItem, -) -> (Bx::FnAbiOfResult, Bx::Value) { +) -> (Bx::FnAbiOfResult, Bx::Value, Instance<'tcx>) { let tcx = bx.tcx(); let def_id = tcx.require_lang_item(li, span); let instance = ty::Instance::mono(tcx, def_id); - (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance)) -} - -// To avoid UB from LLVM, these two functions mask RHS with an -// appropriate mask unconditionally (i.e., the fallback behavior for -// all shifts). For 32- and 64-bit types, this matches the semantics -// of Java. (See related discussion on #1877 and #10183.) - -pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - lhs: Bx::Value, - rhs: Bx::Value, -) -> Bx::Value { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - bx.shl(lhs, rhs) -} - -pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - lhs_t: Ty<'tcx>, - lhs: Bx::Value, - rhs: Bx::Value, -) -> Bx::Value { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - let is_signed = lhs_t.is_signed(); - if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) } -} - -fn shift_mask_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - bx: &mut Bx, - rhs: Bx::Value, -) -> Bx::Value { - let rhs_llty = bx.val_ty(rhs); - let shift_val = shift_mask_val(bx, rhs_llty, rhs_llty, false); - bx.and(rhs, shift_val) + (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) } pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 5bd7442822a21..e9c7606dc5a82 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -16,9 +16,11 @@ use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData}; use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Mutability}; +use rustc_middle::bug; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; +use rustc_span::DUMMY_SP; use rustc_target::abi::Integer; use smallvec::SmallVec; @@ -137,7 +139,7 @@ fn push_debuginfo_type_name<'tcx>( output.push(')'); } } - ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { + ty::RawPtr(inner_type, mutbl) => { if cpp_like_debuginfo { match mutbl { Mutability::Not => output.push_str("ptr_const$<"), @@ -201,6 +203,16 @@ fn push_debuginfo_type_name<'tcx>( } } } + ty::Pat(inner_type, pat) => { + if cpp_like_debuginfo { + output.push_str("pat$<"); + push_debuginfo_type_name(tcx, inner_type, true, output, visited); + // FIXME(wg-debugging): implement CPP like printing for patterns. + write!(output, ",{:?}>", pat).unwrap(); + } else { + write!(output, "{:?}", t).unwrap(); + } + } ty::Slice(inner_type) => { if cpp_like_debuginfo { output.push_str("slice2$<"); @@ -704,7 +716,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S // avoiding collisions and will make the emitted type names shorter. let hash_short = tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); - let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), None).unwrap(); + let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP).unwrap(); hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher)); hasher.finish::() }); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 3572ee301c818..ed6a0c2441061 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -431,7 +431,6 @@ pub struct ProcessingDymutilFailed { #[derive(Diagnostic)] #[diag(codegen_ssa_unable_to_run_dsymutil)] -#[note] pub struct UnableToRunDsymutil { pub error: Error, } @@ -1030,3 +1029,10 @@ pub struct FailedToGetLayout<'tcx> { pub struct ErrorCreatingRemarkDir { pub error: std::io::Error, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_compiler_builtins_cannot_call)] +pub struct CompilerBuiltinsCannotCall { + pub caller: String, + pub callee: String, +} diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 92f0be541c0b0..4eb24d71009aa 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -4,7 +4,6 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(let_chains)] @@ -16,12 +15,8 @@ //! The backend-agnostic functions of this crate use functions defined in various traits that //! have to be implemented by each backend. -#[macro_use] -extern crate rustc_macros; #[macro_use] extern crate tracing; -#[macro_use] -extern crate rustc_middle; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; @@ -29,6 +24,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::CrateNum; +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; @@ -79,13 +75,26 @@ impl ModuleCodegen { emit_obj: bool, emit_dwarf_obj: bool, emit_bc: bool, + emit_asm: bool, + emit_ir: bool, outputs: &OutputFilenames, ) -> CompiledModule { let object = emit_obj.then(|| outputs.temp_path(OutputType::Object, Some(&self.name))); let dwarf_object = emit_dwarf_obj.then(|| outputs.temp_path_dwo(Some(&self.name))); let bytecode = emit_bc.then(|| outputs.temp_path(OutputType::Bitcode, Some(&self.name))); + let assembly = emit_asm.then(|| outputs.temp_path(OutputType::Assembly, Some(&self.name))); + let llvm_ir = + emit_ir.then(|| outputs.temp_path(OutputType::LlvmAssembly, Some(&self.name))); - CompiledModule { name: self.name.clone(), kind: self.kind, object, dwarf_object, bytecode } + CompiledModule { + name: self.name.clone(), + kind: self.kind, + object, + dwarf_object, + bytecode, + assembly, + llvm_ir, + } } } @@ -96,6 +105,8 @@ pub struct CompiledModule { pub object: Option, pub dwarf_object: Option, pub bytecode: Option, + pub assembly: Option, // --emit=asm + pub llvm_ir: Option, // --emit=llvm-ir, llvm-bc is in bytecode } pub struct CachedModuleCodegen { diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 4f7dc9968a13c..23036e9bea022 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -1,5 +1,6 @@ use crate::traits::*; +use rustc_middle::bug; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::config::Lto; use rustc_symbol_mangling::typeid_for_trait_ref; diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index c1de9b76fe734..09ae7cf64109b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -10,6 +10,7 @@ use rustc_middle::mir::traversal; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::{bug, span_bug}; pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx: &FunctionCx<'a, 'tcx, Bx>, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 9bb2a52826585..be5458523d1ca 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,10 +1,11 @@ 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; use crate::common::{self, IntPredicate}; +use crate::errors::CompilerBuiltinsCannotCall; use crate::meth; use crate::traits::*; use crate::MemFlags; @@ -16,6 +17,8 @@ use rustc_middle::mir::{self, AssertKind, BasicBlock, SwitchTargets, UnwindTermi use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty}; +use rustc_middle::{bug, span_bug}; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::config::OptLevel; use rustc_span::{source_map::Spanned, sym, Span}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg}; @@ -157,8 +160,28 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, mut unwind: mir::UnwindAction, copied_constant_arguments: &[PlaceRef<'tcx, ::Value>], + instance: Option>, mergeable_succ: bool, ) -> MergingSucc { + let tcx = bx.tcx(); + if let Some(instance) = instance { + if is_call_from_compiler_builtins_to_upstream_monomorphization(tcx, instance) { + if destination.is_some() { + let caller = with_no_trimmed_paths!(tcx.def_path_str(fx.instance.def_id())); + let callee = with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())); + tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee }); + } else { + info!( + "compiler_builtins call to diverging function {:?} replaced with abort", + instance.def_id() + ); + bx.abort(); + bx.unreachable(); + return MergingSucc::False; + } + } + } + // If there is a cleanup block and the function we're calling can unwind, then // do an invoke, otherwise do a call. let fn_ty = bx.fn_decl_backend_type(fn_abi); @@ -210,6 +233,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { ret_llbb, unwind_block, self.funclet(fx), + instance, ); if fx.mir[self.bb].is_cleanup { bx.apply_attrs_to_cleanup_callsite(invokeret); @@ -219,20 +243,21 @@ 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); } MergingSucc::False } else { - let llret = bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx)); + let llret = + bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx), instance); if fx.mir[self.bb].is_cleanup { bx.apply_attrs_to_cleanup_callsite(llret); } 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) @@ -407,7 +432,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"), } @@ -431,8 +456,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) } @@ -442,21 +467,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"), }; @@ -488,14 +515,14 @@ 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) = + let (drop_fn, fn_abi, drop_instance) = match ty.kind() { // FIXME(eddyb) perhaps move some of this logic into // `Instance::resolve_drop_in_place`? @@ -527,6 +554,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) .get_fn(bx, vtable, ty, fn_abi), fn_abi, + virtual_drop, ) } ty::Dynamic(_, _, ty::DynStar) => { @@ -569,9 +597,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) .get_fn(bx, meta.immediate(), ty, fn_abi), fn_abi, + virtual_drop, ) } - _ => (bx.get_fn_addr(drop_fn), bx.fn_abi_of_instance(drop_fn, ty::List::empty())), + _ => ( + bx.get_fn_addr(drop_fn), + bx.fn_abi_of_instance(drop_fn, ty::List::empty()), + drop_fn, + ), }; helper.do_call( self, @@ -582,6 +615,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some((ReturnDest::Nothing, target)), unwind, &[], + Some(drop_instance), mergeable_succ, ) } @@ -651,17 +685,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (LangItem::PanicMisalignedPointerDereference, vec![required, found, location]) } _ => { - let msg = bx.const_str(msg.description()); - // It's `pub fn panic(expr: &str)`, with the wide reference being passed - // as two arguments, and `#[track_caller]` adds an implicit third argument. - (LangItem::Panic, vec![msg.0, msg.1, location]) + // It's `pub fn panic_...()` and `#[track_caller]` adds an implicit argument. + (msg.panic_function(), vec![location]) } }; - let (fn_abi, llfn) = common::build_langcall(bx, Some(span), lang_item); + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), lang_item); // Codegen the actual panic invoke/call. - let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], false); + let merging_succ = + helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], Some(instance), false); assert_eq!(merging_succ, MergingSucc::False); MergingSucc::False } @@ -677,7 +710,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(bx, terminator.source_info); // Obtain the panic entry point. - let (fn_abi, llfn) = common::build_langcall(bx, Some(span), reason.lang_item()); + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), reason.lang_item()); // Codegen the actual panic invoke/call. let merging_succ = helper.do_call( @@ -689,6 +722,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None, mir::UnwindAction::Unreachable, &[], + Some(instance), false, ); assert_eq!(merging_succ, MergingSucc::False); @@ -738,7 +772,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let msg = bx.const_str(&msg_str); // Obtain the panic entry point. - let (fn_abi, llfn) = + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind); // Codegen the actual panic invoke/call. @@ -751,6 +785,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)), unwind, &[], + Some(instance), mergeable_succ, ) } else { @@ -798,9 +833,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::FnPtr(_) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), }; + let def = instance.map(|i| i.def); - if let Some(ty::InstanceDef::DropGlue(_, None)) = def { + if let Some( + ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None), + ) = def + { // Empty drop glue; a no-op. let target = target.unwrap(); return helper.funclet_br(self, bx, target, mergeable_succ); @@ -885,7 +924,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") } @@ -918,7 +957,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 { @@ -999,7 +1038,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, @@ -1025,16 +1064,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; } _ => { @@ -1046,12 +1085,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); } _ => {} @@ -1106,6 +1145,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { destination, unwind, &copied_constant_arguments, + instance, mergeable_succ, ) } @@ -1394,7 +1434,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; @@ -1416,41 +1456,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, _, 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); - base::memcpy_ty( - bx, - scratch.llval, - scratch.align, - llval, - align, - op.layout, - MemFlags::empty(), - ); - (scratch.llval, scratch.align, true) + let op_place = PlaceRef { val: op_place_val, layout: op.layout }; + bx.typed_place_copy(scratch, op_place); + (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, .. } => { @@ -1463,7 +1496,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:?}"), }, @@ -1471,9 +1504,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if by_ref && !arg.is_indirect() { // Have to load the argument, maybe while casting it. - if let PassMode::Cast { cast: ty, .. } = &arg.mode { - let llty = bx.cast_backend_type(ty); - llval = bx.load(llty, llval, align.min(arg.layout.align.abi)); + if let PassMode::Cast { cast, pad_i32: _ } = &arg.mode { + // The ABI mandates that the value is passed as a different struct representation. + // Spill and reload it from the stack to convert from the Rust representation to + // the ABI representation. + let scratch_size = cast.size(bx); + let scratch_align = cast.align(bx); + // Note that the ABI type may be either larger or smaller than the Rust type, + // due to the presence or absence of trailing padding. For example: + // - On some ABIs, the Rust layout { f64, f32, } may omit padding + // when passed by value, making it smaller. + // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes + // when passed by value, making it larger. + let copy_bytes = cmp::min(scratch_size.bytes(), arg.layout.size.bytes()); + // Allocate some scratch space... + let llscratch = bx.alloca(scratch_size, scratch_align); + bx.lifetime_start(llscratch, scratch_size); + // ...memcpy the value... + bx.memcpy( + llscratch, + scratch_align, + llval, + align, + bx.const_usize(copy_bytes), + MemFlags::empty(), + ); + // ...and then load it with the ABI type. + let cast_ty = bx.cast_backend_type(cast); + llval = bx.load(cast_ty, llscratch, scratch_align); + bx.lifetime_end(llscratch, scratch_size); } else { // We can't use `PlaceRef::load` here because the argument // may have a type we don't treat as immediate, but the ABI @@ -1504,15 +1563,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() { @@ -1664,11 +1724,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span)); - let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, reason.lang_item()); - let fn_ty = bx.fn_decl_backend_type(fn_abi); + let (fn_abi, fn_ptr, instance) = common::build_langcall(&bx, None, reason.lang_item()); + if is_call_from_compiler_builtins_to_upstream_monomorphization(bx.tcx(), instance) { + bx.abort(); + } else { + let fn_ty = bx.fn_decl_backend_type(fn_abi); - let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); - bx.apply_attrs_to_cleanup_callsite(llret); + let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref(), None); + bx.apply_attrs_to_cleanup_callsite(llret); + } bx.unreachable(); @@ -1688,7 +1752,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn try_llbb(&mut self, bb: mir::BasicBlock) -> Option { match self.cached_llbbs[bb] { CachedLlbb::None => { - // FIXME(eddyb) only name the block if `fewer_names` is `false`. let llbb = Bx::append_block(self.cx, self.llfn, &format!("{bb:?}")); self.cached_llbbs[bb] = CachedLlbb::Some(llbb); Some(llbb) @@ -1726,7 +1789,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 @@ -1747,7 +1810,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). @@ -1756,7 +1819,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) @@ -1802,12 +1865,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } enum ReturnDest<'tcx, V> { - // Do nothing; the return value is indirect or ignored. + /// Do nothing; the return value is indirect or ignored. Nothing, - // Store the return value to the pointer. + /// Store the return value to the pointer. Store(PlaceRef<'tcx, V>), - // Store an indirect return value to an operand local place. + /// Store an indirect return value to an operand local place. IndirectOperand(PlaceRef<'tcx, V>, mir::Local), - // Store a direct return value to an operand local place. + /// Store a direct return value to an operand local place. DirectOperand(mir::Local), } diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index ff899c50b3de6..dba5fbefd8a79 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -5,6 +5,7 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; +use rustc_middle::{bug, span_bug}; use rustc_target::abi::Abi; use super::FunctionCx; @@ -24,7 +25,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // `MirUsedCollector` visited all required_consts before codegen began, so if we got here // there can be no more constants that fail to evaluate. self.monomorphize(constant.const_) - .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span)) + .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), constant.span) .expect("erroneous constant missed by mono item collection") } @@ -56,11 +57,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { other => span_bug!(constant.span, "{other:#?}"), }; let uv = self.monomorphize(uv); - self.cx.tcx().const_eval_resolve_for_typeck( - ty::ParamEnv::reveal_all(), - uv, - Some(constant.span), - ) + self.cx.tcx().const_eval_resolve_for_typeck(ty::ParamEnv::reveal_all(), uv, constant.span) } /// process constant containing SIMD shuffle indices diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index ee70465966d0c..721872772287b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -1,12 +1,12 @@ use crate::traits::*; -use rustc_middle::mir::Coverage; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::SourceScope; use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn codegen_coverage(&self, bx: &mut Bx, coverage: &Coverage, scope: SourceScope) { + pub fn codegen_coverage(&self, bx: &mut Bx, kind: &CoverageKind, scope: SourceScope) { // Determine the instance that coverage data was originally generated for. let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) { self.monomorphize(inlined) @@ -15,6 +15,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; // Handle the coverage info in a backend-specific way. - bx.add_coverage(instance, coverage); + bx.add_coverage(instance, kind); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 48f3f4f25228a..50bf1ef61f3a4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -1,20 +1,20 @@ use crate::traits::*; use rustc_data_structures::fx::FxHashMap; use rustc_index::IndexVec; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; use rustc_middle::ty; -use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Instance; use rustc_middle::ty::Ty; use rustc_session::config::DebugInfo; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{BytePos, Span}; +use rustc_span::{hygiene, 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; @@ -220,26 +220,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, source_info: mir::SourceInfo, ) -> Option<(Bx::DIScope, Option, Span)> { - let span = self.adjust_span_for_debugging(source_info.span); let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]; + let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span); Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span)) } - /// In order to have a good line stepping behavior in debugger, we overwrite debug - /// locations of macro expansions with that of the outermost expansion site (when the macro is - /// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). - fn adjust_span_for_debugging(&self, span: Span) -> Span { - // Bail out if debug info emission is not enabled. - if self.debug_context.is_none() { - return span; - } - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - // Use span of the outermost expansion site, while keeping the original lexical scope. - self.cx.tcx().collapsed_debuginfo(span, self.mir.span) - } - fn spill_operand_to_stack( operand: OperandRef<'tcx, Bx::Value>, name: Option, @@ -252,7 +237,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 +316,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) => { @@ -414,22 +399,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { calculate_debuginfo_offset(bx, var.projection, base); // Create a variable which will be a pointer to the actual value - let ptr_ty = Ty::new_ptr( - bx.tcx(), - ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty }, - ); + 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, @@ -438,7 +420,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, @@ -556,7 +538,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 1d1826d984474..2e008460798f8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,7 +1,6 @@ use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; use super::FunctionCx; -use crate::common::IntPredicate; use crate::errors; use crate::errors::InvalidMonomorphization; use crate::meth; @@ -10,6 +9,8 @@ use crate::traits::*; use crate::MemFlags; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; +use rustc_session::config::OptLevel; use rustc_span::{sym, Span}; use rustc_target::abi::{ call::{FnAbi, PassMode}, @@ -76,6 +77,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let name = bx.tcx().item_name(def_id); let name_str = name.as_str(); + // If we're swapping something that's *not* an `OperandValue::Ref`, + // then we can do it directly and avoid the alloca. + // Otherwise, we'll let the fallback MIR body take care of it. + if let sym::typed_swap = name { + let pointee_ty = fn_args.type_at(0); + let pointee_layout = bx.layout_of(pointee_ty); + if !bx.is_backend_ref(pointee_layout) + // But if we're not going to optimize, trying to use the fallback + // body just makes things worse, so don't bother. + || bx.sess().opts.optimize == OptLevel::No + // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary + // reinterpretation of values as (chunkable) byte arrays, and the loop in the + // block optimization in `ptr::swap_nonoverlapping` is hard to rewrite back + // into the (unoptimized) direct swapping implementation, so we disable it. + || bx.sess().target.arch == "spirv" + { + let x_place = PlaceRef::new_sized(args[0].immediate(), pointee_layout); + let y_place = PlaceRef::new_sized(args[1].immediate(), pointee_layout); + bx.typed_place_swap(x_place, y_place); + return Ok(()); + } + } + let llret_ty = bx.backend_type(bx.layout_of(ret_ty)); let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); @@ -130,7 +154,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::variant_count => { let value = bx .tcx() - .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) + .const_eval_instance(ty::ParamEnv::reveal_all(), instance, span) .unwrap(); OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx) } @@ -364,9 +388,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); } @@ -456,12 +480,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return Ok(()); } - sym::ptr_guaranteed_cmp => { - let a = args[0].immediate(); - let b = args[1].immediate(); - bx.icmp(IntPredicate::IntEQ, a, b) - } - sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { let ty = fn_args.type_at(0); let pointee_size = bx.layout_of(ty).size; @@ -494,7 +512,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 387a5366b209b..0064c16f5d9f6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -2,11 +2,13 @@ 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; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; +use rustc_middle::{bug, span_bug}; use rustc_target::abi::call::{FnAbi, PassMode}; use std::iter; @@ -257,20 +259,19 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Apply debuginfo to the newly allocated locals. fx.debug_introduce_locals(&mut start_bx); - let reachable_blocks = mir.reachable_blocks_in_mono(cx.tcx(), instance); - // The builders will be created separately for each basic block at `codegen_block`. // So drop the builder of `start_llbb` to avoid having two at the same time. drop(start_bx); + let reachable_blocks = traversal::mono_reachable_as_bitset(mir, cx.tcx(), instance); + // Codegen the body of each block using reverse postorder for (bb, _) in traversal::reverse_postorder(mir) { if reachable_blocks.contains(bb) { fx.codegen_block(bb); } else { - // This may have references to things we didn't monomorphize, so we - // don't actually codegen the body. We still create the block so - // terminators in other blocks can reference it without worry. + // 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.codegen_block_as_unreachable(bb); } } @@ -290,6 +291,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() @@ -337,7 +344,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 932926976b58d..38f77f2e646fc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -1,11 +1,11 @@ -use super::place::PlaceRef; +use super::place::{PlaceRef, PlaceValue}; use super::{FunctionCx, LocalRef}; -use crate::base; use crate::size_of_val; use crate::traits::*; use crate::MemFlags; +use rustc_middle::bug; use rustc_middle::mir::interpret::{alloc_range, Pointer, Scalar}; use rustc_middle::mir::{self, ConstValue}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; @@ -24,11 +24,14 @@ pub enum OperandValue { /// 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, Align), + Ref(PlaceValue), /// A single LLVM immediate value. /// /// An `OperandValue` *must* be this variant for any type for which @@ -222,7 +225,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. @@ -324,7 +328,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let llfield_ty = bx.cx().backend_type(field); // Can't bitcast an aggregate, so round trip through memory. - let llptr = bx.alloca(llfield_ty, field.align.abi); + let llptr = bx.alloca(field.size, field.align.abi); bx.store(*llval, llptr, field.align.abi); *llval = bx.load(llfield_ty, llptr, field.align.abi); } @@ -362,7 +366,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { 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)) } } @@ -398,7 +402,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { self.store_with_flags(bx, dest, MemFlags::NONTEMPORAL); } - fn store_with_flags>( + pub(crate) fn store_with_flags>( self, bx: &mut Bx, dest: PlaceRef<'tcx, V>, @@ -410,23 +414,17 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { // 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(r, None, source_align) => { + OperandValue::Ref(val) => { assert!(dest.layout.is_sized(), "cannot directly store unsized values"); - if flags.contains(MemFlags::NONTEMPORAL) { - // HACK(nox): This is inefficient but there is no nontemporal memcpy. - let ty = bx.backend_type(dest.layout); - let val = bx.load(ty, r, source_align); - bx.store_with_flags(val, dest.llval, dest.align, flags); - return; + if val.llextra.is_some() { + bug!("cannot directly store unsized values"); } - base::memcpy_ty(bx, dest.llval, dest.align, r, source_align, dest.layout, flags) - } - OperandValue::Ref(_, 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::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 { @@ -435,12 +433,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { 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); } } @@ -460,7 +458,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { .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)") }; @@ -472,7 +471,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { let align_minus_1 = bx.sub(align, one); let size_extra = bx.add(size, align_minus_1); let min_align = Align::ONE; - let alloca = bx.byte_array_alloca(size_extra, min_align); + let alloca = bx.dynamic_alloca(size_extra, min_align); let address = bx.ptrtoint(alloca, bx.type_isize()); let neg_address = bx.neg(address); let offset = bx.and(neg_address, align_minus_1); diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 1ec6c351e2537..870a105c61d6f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -5,6 +5,7 @@ use crate::common::IntPredicate; use crate::size_of_val; use crate::traits::*; +use rustc_middle::bug; use rustc_middle::mir; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; @@ -12,25 +13,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 { /// 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, - /// The monomorphized type of this place, including variant information. - pub layout: TyAndLayout<'tcx>, - /// The alignment we know for this place. pub align: Align, } +impl PlaceValue { + /// Constructor for the ordinary case of `Sized` types. + /// + /// Sets `llextra` to `None`. + pub fn new_sized(llval: V, align: Align) -> PlaceValue { + 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, + + /// 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 +63,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 @@ -57,7 +82,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { align: Align, ) -> Self { assert!(layout.is_sized(), "tried to statically allocate unsized place"); - let tmp = bx.alloca(bx.cx().backend_type(layout), align); + let tmp = bx.alloca(layout.size, align); Self::new_sized_aligned(tmp, layout, align) } @@ -78,7 +103,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 +122,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 +173,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // The type `Foo>` 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 +195,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 +344,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 +388,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 +422,11 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } pub fn storage_live>(&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>(&self, bx: &mut Bx) { - bx.lifetime_end(self.llval, self.layout.size); + bx.lifetime_end(self.val.llval, self.layout.size); } } @@ -461,8 +494,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 8159f76b421b3..4e7d251a2e928 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -3,15 +3,16 @@ use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; use crate::base; -use crate::common::{self, IntPredicate}; +use crate::common::IntPredicate; use crate::traits::*; use crate::MemFlags; -use rustc_middle::mir; -use rustc_middle::mir::Operand; +use rustc_hir as hir; +use rustc_middle::mir::{self, AggregateKind, Operand}; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{self, FIRST_VARIANT}; @@ -67,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"); } @@ -94,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; } } @@ -181,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), + ); } } } @@ -216,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 => { @@ -374,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 { @@ -404,7 +408,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let cast = bx.cx().layout_of(self.monomorphize(mir_cast_ty)); let val = match *kind { - mir::CastKind::PointerExposeAddress => { + mir::CastKind::PointerExposeProvenance => { assert!(bx.cx().is_backend_immediate(cast)); let llptr = operand.immediate(); let llcast_ty = bx.cx().immediate_backend_type(cast); @@ -486,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"), @@ -508,7 +512,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Since int2ptr can have arbitrary integer types as input (so we have to do // sign extension and all that), it is currently best handled in the same code // path as the other integer-to-X casts. - | mir::CastKind::PointerFromExposedAddress => { + | mir::CastKind::PointerWithExposedProvenance => { assert!(bx.cx().is_backend_immediate(cast)); let ll_t_out = bx.cx().immediate_backend_type(cast); if operand.layout.abi.is_uninhabited() { @@ -572,20 +576,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Ref(_, bk, place) => { let mk_ref = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { - Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() }, - ) + Ty::new_ref(tcx, tcx.lifetimes.re_erased, ty, bk.to_mutbl_lossy()) }; self.codegen_place_to_pointer(bx, place, mk_ref) } mir::Rvalue::CopyForDeref(place) => self.codegen_operand(bx, &Operand::Copy(place)), mir::Rvalue::AddressOf(mutability, place) => { - let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { - Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl: mutability }) - }; + let mk_ptr = + move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| Ty::new_ptr(tcx, ty, mutability); self.codegen_place_to_pointer(bx, place, mk_ptr) } @@ -685,9 +684,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes(); bx.cx().const_usize(val) } - mir::NullOp::UbCheck(_) => { - // In codegen, we want to check for language UB and library UB - let val = bx.tcx().sess.opts.debug_assertions; + mir::NullOp::UbChecks => { + let val = bx.tcx().sess.ub_checks(); bx.cx().const_bool(val) } }; @@ -715,13 +713,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { None }; - bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None) + bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, Some(instance)) } else { bx.get_static(def_id) }; OperandRef { val: OperandValue::Immediate(static_), layout } } mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand), + mir::Rvalue::Aggregate(box mir::AggregateKind::RawPtr(..), ref fields) => { + let ty = rvalue.ty(self.mir, self.cx.tcx()); + let layout = self.cx.layout_of(self.monomorphize(ty)); + let [data, meta] = &*fields.raw else { + bug!("RawPtr fields: {fields:?}"); + }; + let data = self.codegen_operand(bx, data); + let meta = self.codegen_operand(bx, meta); + match (data.val, meta.val) { + (p @ OperandValue::Immediate(_), OperandValue::ZeroSized) => { + OperandRef { val: p, layout } + } + (OperandValue::Immediate(p), OperandValue::Immediate(m)) => { + OperandRef { val: OperandValue::Pair(p, m), layout } + } + _ => bug!("RawPtr operands {data:?} {meta:?}"), + } + } mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => { // According to `rvalue_creates_operand`, only ZST // aggregate rvalues are allowed to be operands. @@ -770,9 +786,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)) } } @@ -866,14 +882,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.inbounds_gep(llty, lhs, &[rhs]) } } - mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs), - mir::BinOp::ShlUnchecked => { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); + mir::BinOp::Shl | mir::BinOp::ShlUnchecked => { + let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShlUnchecked); bx.shl(lhs, rhs) } - mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs), - mir::BinOp::ShrUnchecked => { - let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs); + mir::BinOp::Shr | mir::BinOp::ShrUnchecked => { + let rhs = base::build_shift_expr_rhs(bx, lhs, rhs, op == mir::BinOp::ShrUnchecked); if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) } } mir::BinOp::Ne @@ -888,6 +902,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs) } } + mir::BinOp::Cmp => { + use std::cmp::Ordering; + debug_assert!(!is_float); + let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed); + if bx.cx().tcx().sess.opts.optimize == OptLevel::No { + // FIXME: This actually generates tighter assembly, and is a classic trick + // + // However, as of 2023-11 it optimizes worse in things like derived + // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it + // better (see ), it'll + // be worth trying it in optimized builds as well. + let is_gt = bx.icmp(pred(hir::BinOpKind::Gt), lhs, rhs); + let gtext = bx.zext(is_gt, bx.type_i8()); + let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let ltext = bx.zext(is_lt, bx.type_i8()); + bx.unchecked_ssub(gtext, ltext) + } else { + // These operations are those expected by `tests/codegen/integer-cmp.rs`, + // from . + let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs); + let ge = bx.select( + is_ne, + bx.cx().const_i8(Ordering::Greater as i8), + bx.cx().const_i8(Ordering::Equal as i8), + ); + bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) + } + } } } @@ -1007,6 +1050,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::ThreadLocalRef(_) | mir::Rvalue::Use(..) => // (*) true, + // This always produces a `ty::RawPtr`, so will be Immediate or Pair + mir::Rvalue::Aggregate(box AggregateKind::RawPtr(..), ..) => true, mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => { let ty = rvalue.ty(self.mir, self.cx.tcx()); diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index ac7dfbb261dec..a0429022587ce 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,5 +1,5 @@ -use rustc_middle::mir; -use rustc_middle::mir::NonDivergingIntrinsic; +use rustc_middle::mir::{self, NonDivergingIntrinsic}; +use rustc_middle::span_bug; use rustc_session::config::OptLevel; use super::FunctionCx; @@ -64,8 +64,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cg_indirect_place.storage_dead(bx); } } - mir::StatementKind::Coverage(box ref coverage) => { - self.codegen_coverage(bx, coverage, statement.source_info.scope); + mir::StatementKind::Coverage(ref kind) => { + self.codegen_coverage(bx, kind, statement.source_info.scope); } mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => { if !matches!(bx.tcx().sess.opts.optimize, OptLevel::No | OptLevel::Less) { diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 7b7cdae0ed61a..40921c2932fa9 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -2,8 +2,10 @@ use crate::base; use crate::common; use crate::traits::*; use rustc_hir as hir; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{Linkage, Visibility}; +use rustc_middle::span_bug; use rustc_middle::ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::Instance; @@ -40,23 +42,34 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { .iter() .map(|(op, op_sp)| match *op { hir::InlineAsmOperand::Const { ref anon_const } => { - let const_value = cx - .tcx() - .const_eval_poly(anon_const.def_id.to_def_id()) - .unwrap_or_else(|_| { - span_bug!(*op_sp, "asm const cannot be resolved") - }); - let ty = cx - .tcx() - .typeck_body(anon_const.body) - .node_type(anon_const.hir_id); - let string = common::asm_const_to_str( - cx.tcx(), - *op_sp, - const_value, - cx.layout_of(ty), - ); - GlobalAsmOperandRef::Const { string } + match cx.tcx().const_eval_poly(anon_const.def_id.to_def_id()) { + Ok(const_value) => { + let ty = cx + .tcx() + .typeck_body(anon_const.body) + .node_type(anon_const.hir_id); + let string = common::asm_const_to_str( + cx.tcx(), + *op_sp, + const_value, + cx.layout_of(ty), + ); + GlobalAsmOperandRef::Const { string } + } + Err(ErrorHandled::Reported { .. }) => { + // An error has already been reported and + // compilation is guaranteed to fail if execution + // hits this path. So an empty string instead of + // a stringified constant value will suffice. + GlobalAsmOperandRef::Const { string: String::new() } + } + Err(ErrorHandled::TooGeneric(_)) => { + span_bug!( + *op_sp, + "asm const cannot be resolved; too generic" + ) + } + } } hir::InlineAsmOperand::SymFn { ref anon_const } => { let ty = cx diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 087836ca37de0..032699f1fa15a 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -5,6 +5,7 @@ use crate::common::IntPredicate; use crate::meth; use crate::traits::*; use rustc_hir::LangItem; +use rustc_middle::bug; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::WrappingRange; @@ -62,14 +63,23 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let msg = bx.const_str(&msg_str); // Obtain the panic entry point. - let (fn_abi, llfn) = common::build_langcall(bx, None, LangItem::PanicNounwind); + let (fn_abi, llfn, _instance) = + common::build_langcall(bx, None, LangItem::PanicNounwind); // Generate the call. // Cannot use `do_call` since we don't have a MIR terminator so we can't create a `TerminationCodegenHelper`. // (But we are in good company, this code is duplicated plenty of times.) let fn_ty = bx.fn_decl_backend_type(fn_abi); - bx.call(fn_ty, /* fn_attrs */ None, Some(fn_abi), llfn, &[msg.0, msg.1], None); + bx.call( + fn_ty, + /* fn_attrs */ None, + Some(fn_abi), + llfn, + &[msg.0, msg.1], + None, + None, + ); // This function does not return so we can now return whatever we want. let size = bx.const_usize(layout.size.bytes()); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 241b0a15f7872..bcddfe9fb9cb0 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -8,6 +8,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index e6d42c596d2af..e8b9490d4010a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -21,7 +21,6 @@ use rustc_session::{ }; use rustc_span::symbol::Symbol; use rustc_target::abi::call::FnAbi; -use rustc_target::spec::Target; use std::fmt; @@ -70,12 +69,6 @@ pub trait CodegenBackend { fn print_passes(&self) {} fn print_version(&self) {} - /// If this plugin provides additional builtin targets, provide the one enabled by the options here. - /// Be careful: this is called *before* init() is called. - fn target_override(&self, _opts: &config::Options) -> Option { - None - } - /// The metadata loader used to load rlib and dylib metadata. /// /// Alternative codegen backends may want to use different rlib or dylib formats than the diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 36f37e3791bc5..51b22bfaf2558 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -1,22 +1,24 @@ use super::abi::AbiBuilderMethods; use super::asm::AsmBuilderMethods; +use super::consts::ConstMethods; use super::coverageinfo::CoverageInfoBuilderMethods; use super::debuginfo::DebugInfoBuilderMethods; use super::intrinsic::IntrinsicCallMethods; use super::misc::MiscMethods; -use super::type_::{ArgAbiMethods, BaseTypeMethods}; +use super::type_::{ArgAbiMethods, BaseTypeMethods, LayoutTypeMethods}; use super::{HasCodegen, StaticBuilderMethods}; use crate::common::{ AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; -use crate::mir::operand::OperandRef; -use crate::mir::place::PlaceRef; +use crate::mir::operand::{OperandRef, OperandValue}; +use crate::mir::place::{PlaceRef, PlaceValue}; use crate::MemFlags; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Instance, Ty}; +use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Abi, Align, Scalar, Size, WrappingRange}; @@ -80,6 +82,7 @@ pub trait BuilderMethods<'a, 'tcx>: then: Self::BasicBlock, catch: Self::BasicBlock, funclet: Option<&Self::Funclet>, + instance: Option>, ) -> Self::Value; fn unreachable(&mut self); @@ -141,8 +144,8 @@ pub trait BuilderMethods<'a, 'tcx>: } fn to_immediate_scalar(&mut self, val: Self::Value, scalar: Scalar) -> Self::Value; - fn alloca(&mut self, ty: Self::Type, align: Align) -> Self::Value; - fn byte_array_alloca(&mut self, len: Self::Value, align: Align) -> Self::Value; + fn alloca(&mut self, size: Size, align: Align) -> Self::Value; + fn dynamic_alloca(&mut self, size: Self::Value, align: Align) -> Self::Value; fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value; @@ -153,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 { + 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>; @@ -168,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 { + debug_assert_eq!(place.llextra, None); + self.store(val, place.llval, place.align) + } fn store_with_flags( &mut self, val: Self::Value, @@ -267,6 +278,69 @@ pub trait BuilderMethods<'a, 'tcx>: flags: MemFlags, ); + /// *Typed* copy for non-overlapping places. + /// + /// Has a default implementation in terms of `memcpy`, but specific backends + /// can override to do something smarter if possible. + /// + /// (For example, typed load-stores with alias metadata.) + fn typed_place_copy( + &mut self, + dst: PlaceRef<'tcx, Self::Value>, + src: PlaceRef<'tcx, Self::Value>, + ) { + self.typed_place_copy_with_flags(dst, src, MemFlags::empty()); + } + + fn typed_place_copy_with_flags( + &mut self, + dst: PlaceRef<'tcx, Self::Value>, + src: PlaceRef<'tcx, Self::Value>, + flags: MemFlags, + ) { + 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_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` + // isn't useful, so just load-store the value for smaller code. + let temp = self.load_operand(src); + 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.val.llval, dst.val.align, src.val.llval, src.val.align, bytes, flags); + } + } + + /// *Typed* swap for non-overlapping places. + /// + /// Avoids `alloca`s for Immediates and ScalarPairs. + /// + /// FIXME: Maybe do something smarter for Ref types too? + /// For now, the `typed_swap` intrinsic just doesn't call this for those + /// cases (in non-debug), preferring the fallback body instead. + fn typed_place_swap( + &mut self, + left: PlaceRef<'tcx, Self::Value>, + right: PlaceRef<'tcx, Self::Value>, + ) { + let mut temp = self.load_operand(left); + if let OperandValue::Ref(..) = temp.val { + // The SSA value isn't stand-alone, so we need to copy it elsewhere + let alloca = PlaceRef::alloca(self, left.layout); + self.typed_place_copy(alloca, left); + temp = self.load_operand(alloca); + } + self.typed_place_copy(left, right); + temp.val.store(self, right); + } + fn select( &mut self, cond: Self::Value, @@ -339,6 +413,7 @@ pub trait BuilderMethods<'a, 'tcx>: llfn: Self::Value, args: &[Self::Value], funclet: Option<&Self::Funclet>, + instance: Option>, ) -> Self::Value; fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 4dff9c7684f18..8cb17a5b37a89 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -19,6 +19,7 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn const_bool(&self, val: bool) -> Self::Value; fn const_i16(&self, i: i16) -> Self::Value; fn const_i32(&self, i: i32) -> Self::Value; + fn const_i8(&self, i: i8) -> Self::Value; fn const_u32(&self, i: u32) -> Self::Value; fn const_u64(&self, i: u64) -> Self::Value; fn const_u128(&self, i: u128) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index 7e8de0ddc5bf4..d1d813bd38922 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -1,5 +1,5 @@ use super::BackendTypes; -use rustc_middle::mir::Coverage; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { @@ -7,5 +7,5 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { /// /// This can potentially be a no-op in backends that don't support /// coverage instrumentation. - fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage); + fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind); } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 72cce43a3fac2..ccb35629a829d 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -3,6 +3,7 @@ use super::Backend; use super::HasCodegen; use crate::common::TypeKind; use crate::mir::place::PlaceRef; +use rustc_middle::bug; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::call::{ArgAbi, CastTarget, FnAbi, Reg}; @@ -120,26 +121,18 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { immediate: bool, ) -> Self::Type; - /// A type that can be used in a [`super::BuilderMethods::load`] + - /// [`super::BuilderMethods::store`] pair to implement a *typed* copy, - /// such as a MIR `*_0 = *_1`. + /// A type that produces an [`OperandValue::Ref`] when loaded. /// - /// It's always legal to return `None` here, as the provided impl does, - /// in which case callers should use [`super::BuilderMethods::memcpy`] - /// instead of the `load`+`store` pair. + /// AKA one that's not a ZST, not `is_backend_immediate`, and + /// not `is_backend_scalar_pair`. For such a type, a + /// [`load_operand`] doesn't actually `load` anything. /// - /// This can be helpful for things like arrays, where the LLVM backend type - /// `[3 x i16]` optimizes to three separate loads and stores, but it can - /// instead be copied via an `i48` that stays as the single `load`+`store`. - /// (As of 2023-05 LLVM cannot necessarily optimize away a `memcpy` in these - /// cases, due to `poison` handling, but in codegen we have more information - /// about the type invariants, so can emit something better instead.) - /// - /// This *should* return `None` for particularly-large types, where leaving - /// the `memcpy` may well be important to avoid code size explosion. - fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option { - let _ = layout; - None + /// [`OperandValue::Ref`]: crate::mir::operand::OperandValue::Ref + /// [`load_operand`]: super::BuilderMethods::load_operand + fn is_backend_ref(&self, layout: TyAndLayout<'tcx>) -> bool { + !(layout.is_zst() + || self.is_backend_immediate(layout) + || self.is_backend_scalar_pair(layout)) } } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 0046190d20cc7..b79d7441acac7 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -82,12 +82,6 @@ const_eval_double_storage_live = const_eval_dyn_call_not_a_method = `dyn` call trying to call something that is not a method -const_eval_dyn_call_vtable_mismatch = - `dyn` call on a pointer whose vtable does not match its type - -const_eval_dyn_star_call_vtable_mismatch = - `dyn*` call on a pointer whose vtable does not match its type - const_eval_error = {$error_kind -> [static] could not evaluate static initializer [const] evaluation of constant value failed @@ -146,6 +140,8 @@ const_eval_intern_kind = {$kind -> *[other] {""} } +const_eval_interrupted = compilation was interrupted + const_eval_invalid_align_details = invalid align passed to `{$name}`: {$align} is {$err_kind -> [not_power_of_two] not a power of 2 @@ -190,6 +186,8 @@ const_eval_invalid_uninit_bytes_unknown = const_eval_invalid_vtable_pointer = using {$pointer} as vtable pointer but it does not point to a vtable +const_eval_invalid_vtable_trait = + using vtable for trait `{$vtable_trait}` but trait `{$expected_trait}` was expected const_eval_live_drop = destructor of `{$dropped_ty}` cannot be evaluated at compile-time @@ -220,6 +218,7 @@ const_eval_mut_deref = const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind} +const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead const_eval_non_const_fmt_macro_call = cannot call non-const formatting macro in {const_eval_const_context}s @@ -398,9 +397,6 @@ const_eval_unterminated_c_string = const_eval_unwind_past_top = unwinding past the topmost frame of the stack -const_eval_upcast_mismatch = - upcast on a pointer whose vtable does not match its type - ## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`. ## (We'd love to sort this differently to make that more clear but tidy won't let us...) const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant @@ -447,6 +443,7 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer +const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$ref_trait}`, but encountered `{$vtable_trait}` const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` const_eval_validation_null_box = {$front_matter}: encountered a null box diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs new file mode 100644 index 0000000000000..7b6828c6e1828 --- /dev/null +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -0,0 +1,196 @@ +use crate::interpret::{self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic}; +use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult}; +use rustc_middle::mir::*; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_span::def_id::DefId; + +/// Macro for machine-specific `InterpError` without allocation. +/// (These will never be shown to the user, but they help diagnose ICEs.) +pub macro throw_machine_stop_str($($tt:tt)*) {{ + // We make a new local type for it. The type itself does not carry any information, + // but its vtable (for the `MachineStopType` trait) does. + #[derive(Debug)] + struct Zst; + // Printing this type shows the desired string. + impl std::fmt::Display for Zst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $($tt)*) + } + } + + impl rustc_middle::mir::interpret::MachineStopType for Zst { + fn diagnostic_message(&self) -> rustc_errors::DiagMessage { + self.to_string().into() + } + + fn add_args( + self: Box, + _: &mut dyn FnMut(rustc_errors::DiagArgName, rustc_errors::DiagArgValue), + ) {} + } + throw_machine_stop!(Zst) +}} + +pub struct DummyMachine; + +impl HasStaticRootDefId for DummyMachine { + fn static_def_id(&self) -> Option { + None + } +} + +impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { + interpret::compile_time_machine!(<'mir, 'tcx>); + type MemoryKind = !; + const PANIC_ON_ALLOC_FAIL: bool = true; + + // We want to just eval random consts in the program, so `eval_mir_const` can fail. + const ALL_CONSTS_ARE_PRECHECKED: bool = false; + + #[inline(always)] + fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + false // no reason to enforce alignment + } + + fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { + false + } + + fn before_access_global( + _tcx: TyCtxtAt<'tcx>, + _machine: &Self, + _alloc_id: AllocId, + alloc: ConstAllocation<'tcx>, + _static_def_id: Option, + is_write: bool, + ) -> InterpResult<'tcx> { + if is_write { + throw_machine_stop_str!("can't write to global"); + } + + // If the static allocation is mutable, then we can't const prop it as its content + // might be different at runtime. + if alloc.inner().mutability.is_mut() { + throw_machine_stop_str!("can't access mutable globals in ConstProp"); + } + + Ok(()) + } + + fn find_mir_or_eval_fn( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _instance: ty::Instance<'tcx>, + _abi: rustc_target::spec::abi::Abi, + _args: &[interpret::FnArg<'tcx, Self::Provenance>], + _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _target: Option, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { + unimplemented!() + } + + fn panic_nounwind( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _msg: &str, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn call_intrinsic( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _instance: ty::Instance<'tcx>, + _args: &[interpret::OpTy<'tcx, Self::Provenance>], + _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _target: Option, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn assert_panic( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _msg: &rustc_middle::mir::AssertMessage<'tcx>, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn binary_ptr_op( + ecx: &InterpCx<'mir, 'tcx, Self>, + bin_op: BinOp, + left: &interpret::ImmTy<'tcx, Self::Provenance>, + right: &interpret::ImmTy<'tcx, Self::Provenance>, + ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { + use rustc_middle::mir::BinOp::*; + Ok(match bin_op { + Eq | Ne | Lt | Le | Gt | Ge => { + // Types can differ, e.g. fn ptrs with different `for`. + assert_eq!(left.layout.abi, right.layout.abi); + let size = ecx.pointer_size(); + // Just compare the bits. ScalarPairs are compared lexicographically. + // We thus always compare pairs and simply fill scalars up with 0. + // If the pointer has provenance, `to_bits` will return `Err` and we bail out. + let left = match **left { + Immediate::Scalar(l) => (l.to_bits(size)?, 0), + Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let right = match **right { + Immediate::Scalar(r) => (r.to_bits(size)?, 0), + Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let res = match bin_op { + Eq => left == right, + Ne => left != right, + Lt => left < right, + Le => left <= right, + Gt => left > right, + Ge => left >= right, + _ => bug!(), + }; + (ImmTy::from_bool(res, *ecx.tcx), false) + } + + // Some more operations are possible with atomics. + // The return value always has the provenance of the *left* operand. + Add | Sub | BitOr | BitAnd | BitXor => { + throw_machine_stop_str!("pointer arithmetic is not handled") + } + + _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op), + }) + } + + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: interpret::Pointer, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn init_frame_extra( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _frame: interpret::Frame<'mir, 'tcx, Self::Provenance>, + ) -> interpret::InterpResult< + 'tcx, + interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, + > { + unimplemented!() + } + + fn stack<'a>( + _ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { + // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants. + &[] + } + + fn stack_mut<'a>( + _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec> { + unimplemented!() + } +} diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5a1c7cc4209ad..40afd9f162f4e 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -1,3 +1,5 @@ +use std::sync::atomic::Ordering::Relaxed; + use either::{Left, Right}; use rustc_hir::def::DefKind; @@ -8,20 +10,22 @@ use rustc_middle::traits::Reveal; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_target::abi::{self, Abi}; use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter}; use crate::const_eval::CheckAlignment; -use crate::errors; use crate::errors::ConstEvalError; -use crate::interpret::eval_nullary_intrinsic; +use crate::errors::{self, DanglingPtrInFinal}; use crate::interpret::{ create_static_alloc, intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, }; +use crate::interpret::{eval_nullary_intrinsic, InternResult}; +use crate::CTRL_C_RECEIVED; // Returns a pointer to where the result lives #[instrument(level = "trace", skip(ecx, body))] @@ -79,14 +83,42 @@ fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>( ecx.storage_live_for_always_live_locals()?; // The main interpreter loop. - while ecx.step()? {} + while ecx.step()? { + if CTRL_C_RECEIVED.load(Relaxed) { + throw_exhaust!(Interrupted); + } + } // Intern the result - intern_const_alloc_recursive(ecx, intern_kind, &ret)?; + let intern_result = intern_const_alloc_recursive(ecx, intern_kind, &ret); // Since evaluation had no errors, validate the resulting constant. const_validate_mplace(&ecx, &ret, cid)?; + // Only report this after validation, as validaiton produces much better diagnostics. + // FIXME: ensure validation always reports this and stop making interning care about it. + + match intern_result { + Ok(()) => {} + Err(InternResult::FoundDanglingPointer) => { + return Err(ecx + .tcx + .dcx() + .emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }) + .into()); + } + Err(InternResult::FoundBadMutablePointer) => { + // only report mutable pointers if there were no dangling pointers + let err_diag = errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }; + ecx.tcx.emit_node_span_lint( + lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE, + ecx.best_lint_scope(), + err_diag.span, + err_diag, + ) + } + } + Ok(R::make_result(ret, ecx)) } @@ -402,7 +434,7 @@ fn const_validate_mplace<'mir, 'tcx>( } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) - // Instead of just reporting the `InterpError` via the usual machinery, we give a more targetted + // Instead of just reporting the `InterpError` via the usual machinery, we give a more targeted // error about the validation failure. .map_err(|error| report_validation_error(&ecx, error, alloc_id))?; inner = true; diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index d0d6adbfad069..8efc67bcb0c4d 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -2,17 +2,20 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::InterpErrorInfo; -use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::query::{Key, TyCtxtAt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_target::abi::VariantIdx; -use crate::interpret::format_interp_error; +use crate::interpret::{format_interp_error, InterpCx}; +mod dummy_machine; mod error; mod eval_queries; mod fn_queries; mod machine; mod valtrees; +pub use dummy_machine::*; pub use error::*; pub use eval_queries::*; pub use fn_queries::*; @@ -75,3 +78,21 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( Some(mir::DestructuredConstant { variant, fields }) } + +/// Computes the tag (if any) for a given type and variant. +#[instrument(skip(tcx), level = "debug")] +pub fn tag_for_variant_provider<'tcx>( + tcx: TyCtxt<'tcx>, + (ty, variant_index): (Ty<'tcx>, VariantIdx), +) -> Option { + assert!(ty.is_enum()); + + let ecx = InterpCx::new( + tcx, + ty.default_span(tcx), + ty::ParamEnv::reveal_all(), + crate::const_eval::DummyMachine, + ); + + ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag) +} diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index d3428d27d52fd..dcfce4e35e05d 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; @@ -98,7 +99,17 @@ fn const_to_valtree_inner<'tcx>( Ok(ty::ValTree::Leaf(val.assert_int())) } - ty::RawPtr(_) => { + ty::Pat(base, ..) => { + let mut place = place.clone(); + // The valtree of the base type is the same as the valtree of the pattern type. + // Since the returned valtree does not contain the type or layout, we can just + // switch to the base type. + place.layout = ecx.layout_of(*base).unwrap(); + ensure_sufficient_stack(|| const_to_valtree_inner(ecx, &place, num_nodes)) + }, + + + ty::RawPtr(_, _) => { // Not all raw pointers are allowed, as we cannot properly test them for // equality at compile-time (see `ptr_guaranteed_cmp`). // However we allow those that are just integers in disguise. @@ -273,12 +284,12 @@ pub fn valtree_to_const_value<'tcx>( let (param_env, ty) = param_env_ty.into_parts(); - match ty.kind() { + match *ty.kind() { ty::FnDef(..) => { assert!(valtree.unwrap_branch().is_empty()); mir::ConstValue::ZeroSized } - ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_) => { + ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_, _) => { match valtree { ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)), ty::ValTree::Branch(_) => bug!( @@ -286,10 +297,11 @@ pub fn valtree_to_const_value<'tcx>( ), } } + ty::Pat(ty, _) => valtree_to_const_value(tcx, param_env.and(ty), valtree), ty::Ref(_, inner_ty, _) => { let mut ecx = mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No); - let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty); + let imm = valtree_to_ref(&mut ecx, valtree, inner_ty); let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap()); op_to_const(&ecx, &imm.into(), /* for diagnostics */ false) } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index cc32640408b7e..90d4f1168e4fd 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -25,6 +25,13 @@ pub(crate) struct DanglingPtrInFinal { pub kind: InternKind, } +#[derive(Diagnostic)] +#[diag(const_eval_nested_static_in_thread_local)] +pub(crate) struct NestedStaticInThreadLocal { + #[primary_span] + pub span: Span, +} + #[derive(LintDiagnostic)] #[diag(const_eval_mutable_ptr_in_final)] pub(crate) struct MutablePtrInFinal { @@ -491,6 +498,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidTag(_) => const_eval_invalid_tag, InvalidFunctionPointer(_) => const_eval_invalid_function_pointer, InvalidVTablePointer(_) => const_eval_invalid_vtable_pointer, + InvalidVTableTrait { .. } => const_eval_invalid_vtable_trait, InvalidStr(_) => const_eval_invalid_str, InvalidUninitBytes(None) => const_eval_invalid_uninit_bytes_unknown, InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes, @@ -530,6 +538,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { | DeadLocal | UninhabitedEnumVariantWritten(_) | UninhabitedEnumVariantRead(_) => {} + BoundsCheckFailed { len, index } => { diag.arg("len", len); diag.arg("index", index); @@ -537,6 +546,13 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => { diag.arg("pointer", ptr); } + InvalidVTableTrait { expected_trait, vtable_trait } => { + diag.arg("expected_trait", expected_trait.to_string()); + diag.arg( + "vtable_trait", + vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("")), + ); + } PointerUseAfterFree(alloc_id, msg) => { diag.arg("alloc_id", alloc_id) .arg("bad_pointer_message", bad_pointer_message(msg, dcx)); @@ -627,6 +643,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant, Uninit { .. } => const_eval_validation_uninit, InvalidVTablePtr { .. } => const_eval_validation_invalid_vtable_ptr, + InvalidMetaWrongTrait { .. } => const_eval_validation_invalid_vtable_trait, InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => { const_eval_validation_invalid_box_slice_meta } @@ -766,6 +783,13 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { DanglingPtrNoProvenance { pointer, .. } => { err.arg("pointer", pointer); } + InvalidMetaWrongTrait { expected_trait: ref_trait, vtable_trait } => { + err.arg("ref_trait", ref_trait.to_string()); + err.arg( + "vtable_trait", + vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("")), + ); + } NullPtr { .. } | PtrToStatic { .. } | ConstRefToMutable @@ -884,6 +908,7 @@ impl ReportErrorExt for ResourceExhaustionInfo { ResourceExhaustionInfo::StackFrameLimitReached => const_eval_stack_frame_limit_reached, ResourceExhaustionInfo::MemoryExhausted => const_eval_memory_exhausted, ResourceExhaustionInfo::AddressSpaceFull => const_eval_address_space_full, + ResourceExhaustionInfo::Interrupted => const_eval_interrupted, } } fn add_args(self, _: &mut Diag<'_, G>) {} diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 2cebea9d145bb..76e59ea90559f 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -6,7 +6,7 @@ use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::mir::CastKind; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut}; +use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_target::abi::Integer; use rustc_type_ir::TyKind::*; @@ -34,15 +34,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.unsize_into(src, cast_layout, dest)?; } - CastKind::PointerExposeAddress => { + CastKind::PointerExposeProvenance => { let src = self.read_immediate(src)?; - let res = self.pointer_expose_address_cast(&src, cast_layout)?; + let res = self.pointer_expose_provenance_cast(&src, cast_layout)?; self.write_immediate(*res, dest)?; } - CastKind::PointerFromExposedAddress => { + CastKind::PointerWithExposedProvenance => { let src = self.read_immediate(src)?; - let res = self.pointer_from_exposed_address_cast(&src, cast_layout)?; + let res = self.pointer_with_exposed_provenance_cast(&src, cast_layout)?; self.write_immediate(*res, dest)?; } @@ -225,12 +225,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - pub fn pointer_expose_address_cast( + pub fn pointer_expose_provenance_cast( &mut self, src: &ImmTy<'tcx, M::Provenance>, cast_to: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { - assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_)); + assert_matches!(src.layout.ty.kind(), ty::RawPtr(_, _) | ty::FnPtr(_)); assert!(cast_to.ty.is_integral()); let scalar = src.to_scalar(); @@ -242,13 +242,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(ImmTy::from_scalar(self.cast_from_int_like(scalar, src.layout, cast_to.ty)?, cast_to)) } - pub fn pointer_from_exposed_address_cast( + pub fn pointer_with_exposed_provenance_cast( &self, src: &ImmTy<'tcx, M::Provenance>, cast_to: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { assert!(src.layout.ty.is_integral()); - assert_matches!(cast_to.ty.kind(), ty::RawPtr(_)); + assert_matches!(cast_to.ty.kind(), ty::RawPtr(_, _)); // First cast to usize. let scalar = src.to_scalar(); @@ -393,6 +393,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = self.read_immediate(src)?; if data_a.principal() == data_b.principal() { // A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables. + // (But currently mismatching vtables violate the validity invariant so UB is triggered anyway.) return self.write_immediate(*val, dest); } let (old_data, old_vptr) = val.to_scalar_pair(); @@ -400,7 +401,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let old_vptr = old_vptr.to_pointer(self)?; let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?; if old_trait != data_a.principal() { - throw_ub_custom!(fluent::const_eval_upcast_mismatch); + throw_ub!(InvalidVTableTrait { + expected_trait: data_a, + vtable_trait: old_trait, + }); } let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) @@ -435,10 +439,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx> { trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty); match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { - (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) - | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { - self.unsize_into_ptr(src, dest, *s, *c) - } + (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _)) + | (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, *s, *c), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); // implies same number of fields diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 6d4f6d0cb3c52..caacc6f57d3c2 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -2,7 +2,7 @@ use rustc_middle::mir; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, ScalarInt, Ty}; use rustc_target::abi::{self, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; @@ -28,78 +28,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { throw_ub!(UninhabitedEnumVariantWritten(variant_index)) } - match dest.layout().variants { - abi::Variants::Single { index } => { - assert_eq!(index, variant_index); - } - abi::Variants::Multiple { - tag_encoding: TagEncoding::Direct, - tag: tag_layout, - tag_field, - .. - } => { + match self.tag_for_variant(dest.layout().ty, variant_index)? { + Some((tag, tag_field)) => { // No need to validate that the discriminant here because the - // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - - let discr_val = dest - .layout() - .ty - .discriminant_for_variant(*self.tcx, variant_index) - .unwrap() - .val; - - // raw discriminants for enums are isize or bigger during - // their computation, but the in-memory tag is the smallest possible - // representation - let size = tag_layout.size(self); - let tag_val = size.truncate(discr_val); - + // `TyAndLayout::for_variant()` call earlier already checks the + // variant is valid. let tag_dest = self.project_field(dest, tag_field)?; - self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?; + self.write_scalar(tag, &tag_dest) } - abi::Variants::Multiple { - tag_encoding: - TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, - tag: tag_layout, - tag_field, - .. - } => { - // No need to validate that the discriminant here because the - // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - - if variant_index != untagged_variant { - let variants_start = niche_variants.start().as_u32(); - let variant_index_relative = variant_index - .as_u32() - .checked_sub(variants_start) - .expect("overflow computing relative variant idx"); - // We need to use machine arithmetic when taking into account `niche_start`: - // tag_val = variant_index_relative + niche_start_val - let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?; - let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); - let variant_index_relative_val = - ImmTy::from_uint(variant_index_relative, tag_layout); - let tag_val = self.wrapping_binary_op( - mir::BinOp::Add, - &variant_index_relative_val, - &niche_start_val, - )?; - // Write result. - let niche_dest = self.project_field(dest, tag_field)?; - self.write_immediate(*tag_val, &niche_dest)?; - } else { - // The untagged variant is implicitly encoded simply by having a value that is - // outside the niche variants. But what if the data stored here does not - // actually encode this variant? That would be bad! So let's double-check... - let actual_variant = self.read_discriminant(&dest.to_op(self)?)?; - if actual_variant != variant_index { - throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); - } + None => { + // No need to write the tag here, because an untagged variant is + // implicitly encoded. For `Niche`-optimized enums, this works by + // simply by having a value that is outside the niche variants. + // But what if the data stored here does not actually encode + // this variant? That would be bad! So let's double-check... + let actual_variant = self.read_discriminant(&dest.to_op(self)?)?; + if actual_variant != variant_index { + throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); } + Ok(()) } } - - Ok(()) } /// Read discriminant, return the runtime value as well as the variant index. @@ -277,4 +226,78 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; Ok(ImmTy::from_scalar(discr_value, discr_layout)) } + + /// Computes how to write the tag of a given variant of enum `ty`: + /// - `None` means that nothing needs to be done as the variant is encoded implicitly + /// - `Some((val, field_idx))` means that the given integer value needs to be stored at the + /// given field index. + pub(crate) fn tag_for_variant( + &self, + ty: Ty<'tcx>, + variant_index: VariantIdx, + ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> { + match self.layout_of(ty)?.variants { + abi::Variants::Single { index } => { + assert_eq!(index, variant_index); + Ok(None) + } + + abi::Variants::Multiple { + tag_encoding: TagEncoding::Direct, + tag: tag_layout, + tag_field, + .. + } => { + // raw discriminants for enums are isize or bigger during + // their computation, but the in-memory tag is the smallest possible + // representation + let discr = self.discriminant_for_variant(ty, variant_index)?; + let discr_size = discr.layout.size; + let discr_val = discr.to_scalar().to_bits(discr_size)?; + let tag_size = tag_layout.size(self); + let tag_val = tag_size.truncate(discr_val); + let tag = ScalarInt::try_from_uint(tag_val, tag_size).unwrap(); + Ok(Some((tag, tag_field))) + } + + abi::Variants::Multiple { + tag_encoding: TagEncoding::Niche { untagged_variant, .. }, + .. + } if untagged_variant == variant_index => { + // The untagged variant is implicitly encoded simply by having a + // value that is outside the niche variants. + Ok(None) + } + + abi::Variants::Multiple { + tag_encoding: + TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, + tag: tag_layout, + tag_field, + .. + } => { + assert!(variant_index != untagged_variant); + let variants_start = niche_variants.start().as_u32(); + let variant_index_relative = variant_index + .as_u32() + .checked_sub(variants_start) + .expect("overflow computing relative variant idx"); + // We need to use machine arithmetic when taking into account `niche_start`: + // tag_val = variant_index_relative + niche_start_val + let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?; + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); + let variant_index_relative_val = + ImmTy::from_uint(variant_index_relative, tag_layout); + let tag = self + .wrapping_binary_op( + mir::BinOp::Add, + &variant_index_relative_val, + &niche_start_val, + )? + .to_scalar() + .assert_int(); + Ok(Some((tag, tag_field))) + } + } + } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 09e9cc4b35d31..126d64329f815 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -822,15 +822,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.stack_mut().push(frame); // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). - if M::POST_MONO_CHECKS { - for &const_ in &body.required_consts { - let c = self - .instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; - c.eval(*self.tcx, self.param_env, Some(const_.span)).map_err(|err| { - err.emit_note(*self.tcx); - err - })?; - } + for &const_ in &body.required_consts { + let c = + self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; + c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| { + err.emit_note(*self.tcx); + err + })?; } // done @@ -1034,8 +1032,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx> { trace!("{:?} is now live", local); - // We avoid `ty.is_trivially_sized` since that (a) cannot assume WF, so it recurses through - // all fields of a tuple, and (b) does something expensive for ADTs. + // We avoid `ty.is_trivially_sized` since that does something expensive for ADTs. fn is_very_trivially_sized(ty: Ty<'_>) -> bool { match ty.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) @@ -1054,12 +1051,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never - | ty::Error(_) => true, + | ty::Error(_) + | ty::Dynamic(_, _, ty::DynStar) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => false, + ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)), + ty::Pat(ty, ..) => is_very_trivially_sized(*ty), + // We don't want to do any queries, so there is not much we can do with ADTs. ty::Adt(..) => false, @@ -1174,13 +1174,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn eval_mir_constant( &self, val: &mir::Const<'tcx>, - span: Option, + span: Span, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| { let const_val = val.eval(*ecx.tcx, ecx.param_env, span).map_err(|err| { - // FIXME: somehow this is reachable even when POST_MONO_CHECKS is on. - // Are we not always populating `required_consts`? + if M::ALL_CONSTS_ARE_PRECHECKED && !matches!(err, ErrorHandled::TooGeneric(..)) { + // Looks like the const is not captued by `required_consts`, that's bad. + bug!("interpret const eval failure of {val:?} which is not in required_consts"); + } err.emit_note(*ecx.tcx); err })?; diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 17bb59aae8f17..d4168273f29fb 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -16,18 +16,17 @@ use hir::def::DefKind; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::TyAndLayout; -use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::sym; use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy}; use crate::const_eval; -use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal}; +use crate::errors::NestedStaticInThreadLocal; pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine< 'mir, @@ -106,11 +105,21 @@ fn intern_as_new_static<'tcx>( DefKind::Static { mutability: alloc.0.mutability, nested: true }, ); tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); - feed.codegen_fn_attrs(tcx.codegen_fn_attrs(static_id).clone()); + + if tcx.is_thread_local_static(static_id.into()) { + tcx.dcx().emit_err(NestedStaticInThreadLocal { span: tcx.def_span(static_id) }); + } + + // These do not inherit the codegen attrs of the parent static allocation, since + // it doesn't make sense for them to inherit their `#[no_mangle]` and `#[link_name = ..]` + // and the like. + feed.codegen_fn_attrs(CodegenFnAttrs::new()); + feed.eval_static_initializer(Ok(alloc)); feed.generics_of(tcx.generics_of(static_id).clone()); feed.def_ident_span(tcx.def_ident_span(static_id)); feed.explicit_predicates_of(tcx.explicit_predicates_of(static_id)); + feed.feed_hir(); } /// How a constant value should be interned. @@ -123,6 +132,12 @@ pub enum InternKind { Promoted, } +#[derive(Debug)] +pub enum InternResult { + FoundBadMutablePointer, + FoundDanglingPointer, +} + /// Intern `ret` and everything it references. /// /// This *cannot raise an interpreter error*. Doing so is left to validation, which @@ -138,7 +153,7 @@ pub fn intern_const_alloc_recursive< ecx: &mut InterpCx<'mir, 'tcx, M>, intern_kind: InternKind, ret: &MPlaceTy<'tcx>, -) -> Result<(), ErrorGuaranteed> { +) -> Result<(), InternResult> { // We are interning recursively, and for mutability we are distinguishing the "root" allocation // that we are starting in, and all other allocations that we are encountering recursively. let (base_mutability, inner_mutability, is_static) = match intern_kind { @@ -190,7 +205,7 @@ pub fn intern_const_alloc_recursive< // Whether we encountered a bad mutable pointer. // We want to first report "dangling" and then "mutable", so we need to delay reporting these // errors. - let mut found_bad_mutable_pointer = false; + let mut result = Ok(()); // Keep interning as long as there are things to intern. // We show errors if there are dangling pointers, or mutable pointers in immutable contexts @@ -240,7 +255,10 @@ pub fn intern_const_alloc_recursive< // on the promotion analysis not screwing up to ensure that it is sound to intern // promoteds as immutable. trace!("found bad mutable pointer"); - found_bad_mutable_pointer = true; + // Prefer dangling pointer errors over mutable pointer errors + if result.is_ok() { + result = Err(InternResult::FoundBadMutablePointer); + } } if ecx.tcx.try_get_global_alloc(alloc_id).is_some() { // Already interned. @@ -258,21 +276,15 @@ pub fn intern_const_alloc_recursive< // pointers before deciding which allocations can be made immutable; but for now we are // okay with losing some potential for immutability here. This can anyway only affect // `static mut`. - todo.extend(intern_shallow(ecx, alloc_id, inner_mutability).map_err(|()| { - ecx.tcx.dcx().emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind }) - })?); - } - if found_bad_mutable_pointer { - let err_diag = MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind }; - ecx.tcx.emit_node_span_lint( - lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE, - ecx.best_lint_scope(), - err_diag.span, - err_diag, - ) + match intern_shallow(ecx, alloc_id, inner_mutability) { + Ok(nested) => todo.extend(nested), + Err(()) => { + ecx.tcx.dcx().delayed_bug("found dangling pointer during const interning"); + result = Err(InternResult::FoundDanglingPointer); + } + } } - - Ok(()) + result } /// Intern `ret`. This function assumes that `ret` references no other allocation. @@ -291,7 +303,9 @@ pub fn intern_const_alloc_for_constprop< return Ok(()); } // Move allocation to `tcx`. - for _ in intern_shallow(ecx, alloc_id, Mutability::Not).map_err(|()| err_ub!(DeadLocal))? { + if let Some(_) = + (intern_shallow(ecx, alloc_id, Mutability::Not).map_err(|()| err_ub!(DeadLocal))?).next() + { // We are not doing recursive interning, so we don't currently support provenance. // (If this assertion ever triggers, we should just implement a // proper recursive interning loop -- or just call `intern_const_alloc_recursive`. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index b68bfb4211d1e..4d37c3c22cd7c 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -21,8 +21,8 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::Size; use super::{ - util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, MPlaceTy, Machine, OpTy, - Pointer, + memory::MemoryKind, util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, + MPlaceTy, Machine, OpTy, Pointer, }; use crate::fluent_generated as fluent; @@ -69,6 +69,10 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => { throw_inval!(TooGeneric) } + ty::Pat(_, pat) => match **pat { + ty::PatternKind::Range { .. } => ConstValue::from_target_usize(0u64, &tcx), + // Future pattern kinds may have more variants + }, ty::Bound(_, _) => bug!("bound ty during ctfe"), ty::Bool | ty::Char @@ -79,7 +83,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -153,9 +157,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::type_name => Ty::new_static_str(self.tcx.tcx), _ => bug!(), }; - let val = self.ctfe_query(|tcx| { - tcx.const_eval_global_id(self.param_env, gid, Some(tcx.span)) - })?; + let val = + self.ctfe_query(|tcx| tcx.const_eval_global_id(self.param_env, gid, tcx.span))?; let val = self.const_val_to_op(val, ty, Some(dest.layout))?; self.copy_op(&val, dest)?; } @@ -170,7 +173,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ty = instance_args.type_at(0); let layout = self.layout_of(ty)?; let val = self.read_scalar(&args[0])?; - let out_val = self.numeric_intrinsic(intrinsic_name, val, layout)?; + + let out_val = self.numeric_intrinsic(intrinsic_name, val, layout, dest.layout)?; self.write_scalar(out_val, dest)?; } sym::saturating_add | sym::saturating_sub => { @@ -197,12 +201,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::rotate_left | sym::rotate_right => { // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW)) // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) - let layout = self.layout_of(instance_args.type_at(0))?; + let layout_val = self.layout_of(instance_args.type_at(0))?; let val = self.read_scalar(&args[0])?; - let val_bits = val.to_bits(layout.size)?; + let val_bits = val.to_bits(layout_val.size)?; + + let layout_raw_shift = self.layout_of(self.tcx.types.u32)?; let raw_shift = self.read_scalar(&args[1])?; - let raw_shift_bits = raw_shift.to_bits(layout.size)?; - let width_bits = u128::from(layout.size.bits()); + let raw_shift_bits = raw_shift.to_bits(layout_raw_shift.size)?; + + let width_bits = u128::from(layout_val.size.bits()); let shift_bits = raw_shift_bits % width_bits; let inv_shift_bits = (width_bits - shift_bits) % width_bits; let result_bits = if intrinsic_name == sym::rotate_left { @@ -210,8 +217,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else { (val_bits >> shift_bits) | (val_bits << inv_shift_bits) }; - let truncated_bits = self.truncate(result_bits, layout); - let result = Scalar::from_uint(truncated_bits, layout.size); + let truncated_bits = self.truncate(result_bits, layout_val); + let result = Scalar::from_uint(truncated_bits, layout_val.size); self.write_scalar(result, dest)?; } sym::copy => { @@ -415,6 +422,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = self.raw_eq_intrinsic(&args[0], &args[1])?; self.write_scalar(result, dest)?; } + sym::typed_swap => { + self.typed_swap_intrinsic(&args[0], &args[1])?; + } sym::vtable_size => { let ptr = self.read_pointer(&args[0])?; @@ -466,6 +476,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { name: Symbol, val: Scalar, layout: TyAndLayout<'tcx>, + ret_layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, Scalar> { assert!(layout.ty.is_integral(), "invalid type for numeric intrinsic: {}", layout.ty); let bits = val.to_bits(layout.size)?; @@ -477,11 +488,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::ctlz | sym::ctlz_nonzero => u128::from(bits.leading_zeros()) - extra, sym::cttz | sym::cttz_nonzero => u128::from((bits << extra).trailing_zeros()) - extra, - sym::bswap => (bits << extra).swap_bytes(), - sym::bitreverse => (bits << extra).reverse_bits(), + sym::bswap => { + assert_eq!(layout, ret_layout); + (bits << extra).swap_bytes() + } + sym::bitreverse => { + assert_eq!(layout, ret_layout); + (bits << extra).reverse_bits() + } _ => bug!("not a numeric intrinsic: {}", name), }; - Ok(Scalar::from_uint(bits_out, layout.size)) + Ok(Scalar::from_uint(bits_out, ret_layout.size)) } pub fn exact_div( @@ -608,6 +625,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.mem_copy(src, dst, size, nonoverlapping) } + /// Does a *typed* swap of `*left` and `*right`. + fn typed_swap_intrinsic( + &mut self, + left: &OpTy<'tcx, >::Provenance>, + right: &OpTy<'tcx, >::Provenance>, + ) -> InterpResult<'tcx> { + let left = self.deref_pointer(left)?; + let right = self.deref_pointer(right)?; + debug_assert_eq!(left.layout, right.layout); + let kind = MemoryKind::Stack; + let temp = self.allocate(left.layout, kind)?; + self.copy_op(&left, &temp)?; + self.copy_op(&right, &left)?; + self.copy_op(&temp, &right)?; + self.deallocate_ptr(temp.ptr(), None, kind)?; + Ok(()) + } + pub(crate) fn write_bytes_intrinsic( &mut self, dst: &OpTy<'tcx, >::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index afc4a80c283f4..8bc569bed54c5 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -140,8 +140,9 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Should the machine panic on allocation failures? const PANIC_ON_ALLOC_FAIL: bool; - /// Should post-monomorphization checks be run when a stack frame is pushed? - const POST_MONO_CHECKS: bool = true; + /// Determines whether `eval_mir_constant` can never fail because all required consts have + /// already been checked before. + const ALL_CONSTS_ARE_PRECHECKED: bool = true; /// Whether memory accesses should be alignment-checked. fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; @@ -288,28 +289,19 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { } /// Return the `AllocId` for the given thread-local static in the current thread. - fn thread_local_static_base_pointer( + fn thread_local_static_pointer( _ecx: &mut InterpCx<'mir, 'tcx, Self>, def_id: DefId, ) -> InterpResult<'tcx, Pointer> { throw_unsup!(ThreadLocalStatic(def_id)) } - /// Return the root pointer for the given `extern static`. - fn extern_static_base_pointer( + /// Return the `AllocId` for the given `extern static`. + fn extern_static_pointer( ecx: &InterpCx<'mir, 'tcx, Self>, def_id: DefId, ) -> InterpResult<'tcx, Pointer>; - /// Return a "base" pointer for the given allocation: the one that is used for direct - /// accesses to this static/const/fn allocation, or the one returned from the heap allocator. - /// - /// Not called on `extern` or thread-local statics (those use the methods above). - fn adjust_alloc_base_pointer( - ecx: &InterpCx<'mir, 'tcx, Self>, - ptr: Pointer, - ) -> InterpResult<'tcx, Pointer>; - /// "Int-to-pointer cast" fn ptr_from_addr_cast( ecx: &InterpCx<'mir, 'tcx, Self>, @@ -336,6 +328,8 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Called to adjust allocations to the Provenance and AllocExtra of this machine. /// + /// If `alloc` contains pointers, then they are all pointing to globals. + /// /// The way we construct allocations is to always first construct it without extra and then add /// the extra. This keeps uniform code paths for handling both allocations created by CTFE for /// globals, and allocations created by Miri during evaluation. @@ -347,8 +341,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// allocation (because a copy had to be done to adjust things), machine memory will /// cache the result. (This relies on `AllocMap::get_or` being able to add the /// owned allocation to the map even when the map is shared.) - /// - /// This must only fail if `alloc` contains provenance. fn adjust_allocation<'b>( ecx: &InterpCx<'mir, 'tcx, Self>, id: AllocId, @@ -356,6 +348,19 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { kind: Option>, ) -> InterpResult<'tcx, Cow<'b, Allocation>>; + /// Return a "root" pointer for the given allocation: the one that is used for direct + /// accesses to this static/const/fn allocation, or the one returned from the heap allocator. + /// + /// Not called on `extern` or thread-local statics (those use the methods above). + /// + /// `kind` is the kind of the allocation the pointer points to; it can be `None` when + /// it's a global and `GLOBAL_KIND` is `None`. + fn adjust_alloc_root_pointer( + ecx: &InterpCx<'mir, 'tcx, Self>, + ptr: Pointer, + kind: Option>, + ) -> InterpResult<'tcx, Pointer>; + /// Evaluate the inline assembly. /// /// This should take care of jumping to the next block (one of `targets`) when asm goto @@ -427,6 +432,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { _prov: (AllocId, Self::ProvenanceExtra), _size: Size, _align: Align, + _kind: MemoryKind, ) -> InterpResult<'tcx> { Ok(()) } @@ -525,7 +531,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { fn eval_mir_constant( ecx: &InterpCx<'mir, 'tcx, Self>, val: mir::Const<'tcx>, - span: Option, + span: Span, layout: Option>, eval: F, ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>> @@ -533,7 +539,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { F: Fn( &InterpCx<'mir, 'tcx, Self>, mir::Const<'tcx>, - Option, + Span, Option>, ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>, { @@ -593,7 +599,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { Ok(alloc) } - fn extern_static_base_pointer( + fn extern_static_pointer( ecx: &InterpCx<$mir, $tcx, Self>, def_id: DefId, ) -> InterpResult<$tcx, Pointer> { @@ -602,9 +608,10 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { } #[inline(always)] - fn adjust_alloc_base_pointer( + fn adjust_alloc_root_pointer( _ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer, + _kind: Option>, ) -> InterpResult<$tcx, Pointer> { Ok(ptr) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 86aad2e1642d6..594e3b3212f2e 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -165,7 +165,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// /// This function can fail only if `ptr` points to an `extern static`. #[inline] - pub fn global_base_pointer( + pub fn global_root_pointer( &self, ptr: Pointer, ) -> InterpResult<'tcx, Pointer> { @@ -178,12 +178,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bug!("global memory cannot point to thread-local static") } Some(GlobalAlloc::Static(def_id)) if self.tcx.is_foreign_item(def_id) => { - return M::extern_static_base_pointer(self, def_id); + return M::extern_static_pointer(self, def_id); + } + None => { + assert!( + self.memory.extra_fn_ptr_map.contains_key(&alloc_id), + "{alloc_id:?} is neither global nor a function pointer" + ); } _ => {} } // And we need to get the provenance. - M::adjust_alloc_base_pointer(self, ptr) + M::adjust_alloc_root_pointer(self, ptr, M::GLOBAL_KIND.map(MemoryKind::Machine)) } pub fn fn_ptr(&mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>) -> Pointer { @@ -197,9 +203,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { id } }; - // Functions are global allocations, so make sure we get the right base pointer. + // Functions are global allocations, so make sure we get the right root pointer. // We know this is not an `extern static` so this cannot fail. - self.global_base_pointer(Pointer::from(id)).unwrap() + self.global_root_pointer(Pointer::from(id)).unwrap() } pub fn allocate_ptr( @@ -227,7 +233,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.allocate_raw_ptr(alloc, kind) } - /// This can fail only if `alloc` contains provenance. pub fn allocate_raw_ptr( &mut self, alloc: Allocation, @@ -241,7 +246,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); let alloc = M::adjust_allocation(self, id, Cow::Owned(alloc), Some(kind))?; self.memory.alloc_map.insert(id, (kind, alloc.into_owned())); - M::adjust_alloc_base_pointer(self, Pointer::from(id)) + M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind)) } pub fn reallocate_ptr( @@ -355,6 +360,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (alloc_id, prov), size, alloc.align, + kind, )?; // Don't forget to remember size and align of this now-dead allocation @@ -949,6 +955,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Runs the close in "validation" mode, which means the machine's memory read hooks will be /// suppressed. Needless to say, this must only be set with great care! Cannot be nested. pub(super) fn run_for_validation(&self, f: impl FnOnce() -> R) -> R { + // This deliberately uses `==` on `bool` to follow the pattern + // `assert!(val.replace(new) == old)`. assert!( self.memory.validation_in_progress.replace(true) == false, "`validation_in_progress` was already set" @@ -1157,11 +1165,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Side-step AllocRef and directly access the underlying bytes more efficiently. - // (We are staying inside the bounds here so all is good.) + // (We are staying inside the bounds here and all bytes do get overwritten so all is good.) let alloc_id = alloc_ref.alloc_id; let bytes = alloc_ref .alloc - .get_bytes_mut(&alloc_ref.tcx, alloc_ref.range) + .get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range) .map_err(move |e| e.to_interp_error(alloc_id))?; // `zip` would stop when the first iterator ends; we want to definitely // cover all of `bytes`. @@ -1182,6 +1190,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.mem_copy_repeatedly(src, dest, size, 1, nonoverlapping) } + /// Performs `num_copies` many copies of `size` many bytes from `src` to `dest + i*size` (where + /// `i` is the index of the copy). + /// + /// Either `nonoverlapping` must be true or `num_copies` must be 1; doing repeated copies that + /// may overlap is not supported. pub fn mem_copy_repeatedly( &mut self, src: Pointer>, @@ -1243,8 +1256,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (dest_alloc_id, dest_prov), dest_range, )?; + // Yes we do overwrite all bytes in `dest_bytes`. let dest_bytes = dest_alloc - .get_bytes_mut_ptr(&tcx, dest_range) + .get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range) .map_err(|e| e.to_interp_error(dest_alloc_id))? .as_mut_ptr(); @@ -1278,6 +1292,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } } + if num_copies > 1 { + assert!(nonoverlapping, "multi-copy only supported in non-overlapping mode"); + } let size_in_bytes = size.bytes_usize(); // For particularly large arrays (where this is perf-sensitive) it's common that @@ -1290,6 +1307,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else if src_alloc_id == dest_alloc_id { let mut dest_ptr = dest_bytes; for _ in 0..num_copies { + // Here we rely on `src` and `dest` being non-overlapping if there is more than + // one copy. ptr::copy(src_bytes, dest_ptr, size_in_bytes); dest_ptr = dest_ptr.add(size_in_bytes); } diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 474d35b2aa3a2..7d7b421f86935 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -18,11 +18,13 @@ mod util; mod validity; mod visitor; +#[doc(no_inline)] pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup}; pub use self::intern::{ intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind, + InternResult, }; pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump}; pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index e49067a2f0063..718c91b2f7676 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -6,9 +6,10 @@ use std::assert_matches::assert_matches; use either::{Either, Left, Right}; use rustc_hir::def::Namespace; +use rustc_middle::mir::interpret::ScalarSizeMismatch; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter}; -use rustc_middle::ty::{ConstInt, Ty, TyCtxt}; +use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt}; use rustc_middle::{mir, ty}; use rustc_target::abi::{self, Abi, HasDataLayout, Size}; @@ -210,6 +211,12 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { ImmTy { imm: Immediate::Uninit, layout } } + #[inline] + pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self { + assert_eq!(s.size(), layout.size); + Self::from_scalar(Scalar::from(s), layout) + } + #[inline] pub fn try_from_uint(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) @@ -223,7 +230,6 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { pub fn try_from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Option { Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout)) } - #[inline] pub fn from_int(i: impl Into, layout: TyAndLayout<'tcx>) -> Self { Self::from_scalar(Scalar::from_int(i, layout.size), layout) @@ -235,6 +241,27 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { Self::from_scalar(Scalar::from_bool(b), layout) } + #[inline] + pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self { + let ty = tcx.ty_ordering_enum(None); + let layout = tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap(); + Self::from_scalar(Scalar::from_i8(c as i8), layout) + } + + /// Return the immediate as a `ScalarInt`. Ensures that it has the size that the layout of the + /// immediate indicates. + #[inline] + pub fn to_scalar_int(&self) -> InterpResult<'tcx, ScalarInt> { + let s = self.to_scalar().to_scalar_int()?; + if s.size() != self.layout.size { + throw_ub!(ScalarSizeMismatch(ScalarSizeMismatch { + target_size: self.layout.size.bytes(), + data_size: s.size().bytes(), + })); + } + Ok(s) + } + #[inline] pub fn to_const_int(self) -> ConstInt { assert!(self.layout.ty.is_integral()); @@ -741,7 +768,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all // checked yet. // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. - self.eval_mir_constant(&c, Some(constant.span), layout)? + self.eval_mir_constant(&c, constant.span, layout)? } }; trace!("{:?}: {:?}", mir_op, op); @@ -757,7 +784,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Other cases need layout. let adjust_scalar = |scalar| -> InterpResult<'tcx, _> { Ok(match scalar { - Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size), + Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_root_pointer(ptr)?, size), Scalar::Int(int) => Scalar::Int(int), }) }; @@ -765,7 +792,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let imm = match val_val { mir::ConstValue::Indirect { alloc_id, offset } => { // This is const data, no mutation allowed. - let ptr = self.global_base_pointer(Pointer::new( + let ptr = self.global_root_pointer(Pointer::new( CtfeProvenance::from(alloc_id).as_immutable(), offset, ))?; @@ -777,7 +804,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // This is const data, no mutation allowed. let alloc_id = self.tcx.reserve_and_set_memory_alloc(data); let ptr = Pointer::new(CtfeProvenance::from(alloc_id).as_immutable(), Size::ZERO); - Immediate::new_slice(self.global_base_pointer(ptr)?.into(), meta, self) + Immediate::new_slice(self.global_root_pointer(ptr)?.into(), meta, self) } }; Ok(OpTy { op: Operand::Immediate(imm), layout }) @@ -785,7 +812,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 475c533077af5..9af755e40de87 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -2,7 +2,7 @@ use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty}; use rustc_span::symbol::sym; use rustc_target::abi::Abi; @@ -61,6 +61,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + fn three_way_compare(&self, lhs: T, rhs: T) -> (ImmTy<'tcx, M::Provenance>, bool) { + let res = Ord::cmp(&lhs, &rhs); + return (ImmTy::from_ordering(res, *self.tcx), false); + } + fn binary_char_op( &self, bin_op: mir::BinOp, @@ -69,6 +74,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> (ImmTy<'tcx, M::Provenance>, bool) { use rustc_middle::mir::BinOp::*; + if bin_op == Cmp { + return self.three_way_compare(l, r); + } + let res = match bin_op { Eq => l == r, Ne => l != r, @@ -137,14 +146,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn binary_int_op( &self, bin_op: mir::BinOp, - // passing in raw bits - l: u128, - left_layout: TyAndLayout<'tcx>, - r: u128, - right_layout: TyAndLayout<'tcx>, + left: &ImmTy<'tcx, M::Provenance>, + right: &ImmTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { use rustc_middle::mir::BinOp::*; + // This checks the size, so that we can just assert it below. + let l = left.to_scalar_int()?; + let r = right.to_scalar_int()?; + // Prepare to convert the values to signed or unsigned form. + let l_signed = || l.assert_int(left.layout.size); + let l_unsigned = || l.assert_uint(left.layout.size); + let r_signed = || r.assert_int(right.layout.size); + let r_unsigned = || r.assert_uint(right.layout.size); + let throw_ub_on_overflow = match bin_op { AddUnchecked => Some(sym::unchecked_add), SubUnchecked => Some(sym::unchecked_sub), @@ -156,69 +171,72 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Shift ops can have an RHS with a different numeric type. if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) { - let size = left_layout.size.bits(); + let size = left.layout.size.bits(); // The shift offset is implicitly masked to the type size. (This is the one MIR operator // that does *not* directly map to a single LLVM operation.) Compute how much we // actually shift and whether there was an overflow due to shifting too much. - let (shift_amount, overflow) = if right_layout.abi.is_signed() { - let shift_amount = self.sign_extend(r, right_layout) as i128; + let (shift_amount, overflow) = if right.layout.abi.is_signed() { + let shift_amount = r_signed(); let overflow = shift_amount < 0 || shift_amount >= i128::from(size); + // Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result + // of the `as` will be equal modulo `size` (since it is a power of two). let masked_amount = (shift_amount as u128) % u128::from(size); - debug_assert_eq!(overflow, shift_amount != (masked_amount as i128)); + assert_eq!(overflow, shift_amount != (masked_amount as i128)); (masked_amount, overflow) } else { - let shift_amount = r; + let shift_amount = r_unsigned(); let masked_amount = shift_amount % u128::from(size); (masked_amount, shift_amount != masked_amount) }; let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit // Compute the shifted result. - let result = if left_layout.abi.is_signed() { - let l = self.sign_extend(l, left_layout) as i128; + let result = if left.layout.abi.is_signed() { + let l = l_signed(); let result = match bin_op { Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(), Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(), _ => bug!(), }; - result as u128 + ScalarInt::truncate_from_int(result, left.layout.size).0 } else { - match bin_op { + let l = l_unsigned(); + let result = match bin_op { Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(), Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(), _ => bug!(), - } + }; + ScalarInt::truncate_from_uint(result, left.layout.size).0 }; - let truncated = self.truncate(result, left_layout); if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { throw_ub_custom!( fluent::const_eval_overflow_shift, - val = if right_layout.abi.is_signed() { - (self.sign_extend(r, right_layout) as i128).to_string() + val = if right.layout.abi.is_signed() { + r_signed().to_string() } else { - r.to_string() + r_unsigned().to_string() }, name = intrinsic_name ); } - return Ok((ImmTy::from_uint(truncated, left_layout), overflow)); + return Ok((ImmTy::from_scalar_int(result, left.layout), overflow)); } // For the remaining ops, the types must be the same on both sides - if left_layout.ty != right_layout.ty { + if left.layout.ty != right.layout.ty { span_bug!( self.cur_span(), "invalid asymmetric binary op {bin_op:?}: {l:?} ({l_ty}), {r:?} ({r_ty})", - l_ty = left_layout.ty, - r_ty = right_layout.ty, + l_ty = left.layout.ty, + r_ty = right.layout.ty, ) } - let size = left_layout.size; + let size = left.layout.size; // Operations that need special treatment for signed integers - if left_layout.abi.is_signed() { + if left.layout.abi.is_signed() { let op: Option bool> = match bin_op { Lt => Some(i128::lt), Le => Some(i128::le), @@ -227,13 +245,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => None, }; if let Some(op) = op { - let l = self.sign_extend(l, left_layout) as i128; - let r = self.sign_extend(r, right_layout) as i128; - return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false)); + return Ok((ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx), false)); + } + if bin_op == Cmp { + return Ok(self.three_way_compare(l_signed(), r_signed())); } let op: Option (i128, bool)> = match bin_op { - Div if r == 0 => throw_ub!(DivisionByZero), - Rem if r == 0 => throw_ub!(RemainderByZero), + Div if r.is_null() => throw_ub!(DivisionByZero), + Rem if r.is_null() => throw_ub!(RemainderByZero), Div => Some(i128::overflowing_div), Rem => Some(i128::overflowing_rem), Add | AddUnchecked => Some(i128::overflowing_add), @@ -242,8 +261,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => None, }; if let Some(op) = op { - let l = self.sign_extend(l, left_layout) as i128; - let r = self.sign_extend(r, right_layout) as i128; + let l = l_signed(); + let r = r_signed(); // We need a special check for overflowing Rem and Div since they are *UB* // on overflow, which can happen with "int_min $OP -1". @@ -258,17 +277,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } let (result, oflo) = op(l, r); - // This may be out-of-bounds for the result type, so we have to truncate ourselves. + // This may be out-of-bounds for the result type, so we have to truncate. // If that truncation loses any information, we have an overflow. - let result = result as u128; - let truncated = self.truncate(result, left_layout); - let overflow = oflo || self.sign_extend(truncated, left_layout) != result; + let (result, lossy) = ScalarInt::truncate_from_int(result, left.layout.size); + let overflow = oflo || lossy; if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name); } - return Ok((ImmTy::from_uint(truncated, left_layout), overflow)); + return Ok((ImmTy::from_scalar_int(result, left.layout), overflow)); } } + // From here on it's okay to treat everything as unsigned. + let l = l_unsigned(); + let r = r_unsigned(); + + if bin_op == Cmp { + return Ok(self.three_way_compare(l, r)); + } let val = match bin_op { Eq => ImmTy::from_bool(l == r, *self.tcx), @@ -279,12 +304,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Gt => ImmTy::from_bool(l > r, *self.tcx), Ge => ImmTy::from_bool(l >= r, *self.tcx), - BitOr => ImmTy::from_uint(l | r, left_layout), - BitAnd => ImmTy::from_uint(l & r, left_layout), - BitXor => ImmTy::from_uint(l ^ r, left_layout), + BitOr => ImmTy::from_uint(l | r, left.layout), + BitAnd => ImmTy::from_uint(l & r, left.layout), + BitXor => ImmTy::from_uint(l ^ r, left.layout), Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => { - assert!(!left_layout.abi.is_signed()); + assert!(!left.layout.abi.is_signed()); let op: fn(u128, u128) -> (u128, bool) = match bin_op { Add | AddUnchecked => u128::overflowing_add, Sub | SubUnchecked => u128::overflowing_sub, @@ -298,21 +323,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (result, oflo) = op(l, r); // Truncate to target type. // If that truncation loses any information, we have an overflow. - let truncated = self.truncate(result, left_layout); - let overflow = oflo || truncated != result; + let (result, lossy) = ScalarInt::truncate_from_uint(result, left.layout.size); + let overflow = oflo || lossy; if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name); } - return Ok((ImmTy::from_uint(truncated, left_layout), overflow)); + return Ok((ImmTy::from_scalar_int(result, left.layout), overflow)); } _ => span_bug!( self.cur_span(), "invalid binary op {:?}: {:?}, {:?} (both {})", bin_op, - l, - r, - right_layout.ty, + left, + right, + right.layout.ty, ), }; @@ -409,9 +434,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { right.layout.ty ); - let l = left.to_scalar().to_bits(left.layout.size)?; - let r = right.to_scalar().to_bits(right.layout.size)?; - self.binary_int_op(bin_op, l, left.layout, r, right.layout) + self.binary_int_op(bin_op, left, right) } _ if left.layout.ty.is_any_ptr() => { // The RHS type must be a `pointer` *or an integer type* (for `Offset`). diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 1a2f1194f89a2..e5241f1ba19e1 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -1010,7 +1010,7 @@ where ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { // This must be an allocation in `tcx` let _ = self.tcx.global_alloc(raw.alloc_id); - let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?; + let ptr = self.global_root_pointer(Pointer::from(raw.alloc_id))?; let layout = self.layout_of(raw.ty)?; Ok(self.ptr_to_mplace(ptr.into(), layout)) } @@ -1020,16 +1020,20 @@ where pub(super) fn unpack_dyn_trait( &self, mplace: &MPlaceTy<'tcx, M::Provenance>, + expected_trait: &'tcx ty::List>, ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer>)> { assert!( matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), "`unpack_dyn_trait` only makes sense on `dyn*` types" ); let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; - let (ty, _) = self.get_ptr_vtable(vtable)?; - let layout = self.layout_of(ty)?; + let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?; + if expected_trait.principal() != vtable_trait { + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + } // This is a kind of transmute, from a place with unsized type and metadata to // a place with sized type and no metadata. + let layout = self.layout_of(ty)?; let mplace = MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout }; Ok((mplace, vtable)) @@ -1040,6 +1044,7 @@ where pub(super) fn unpack_dyn_star>( &self, val: &P, + expected_trait: &'tcx ty::List>, ) -> InterpResult<'tcx, (P, Pointer>)> { assert!( matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), @@ -1048,17 +1053,19 @@ where let data = self.project_field(val, 0)?; let vtable = self.project_field(val, 1)?; let vtable = self.read_pointer(&vtable.to_op(self)?)?; - let (ty, _) = self.get_ptr_vtable(vtable)?; + let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?; + if expected_trait.principal() != vtable_trait { + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + } + // `data` is already the right thing but has the wrong type. So we transmute it. let layout = self.layout_of(ty)?; - // `data` is already the right thing but has the wrong type. So we transmute it, by - // projecting with offset 0. let data = data.transmute(layout, self)?; Ok((data, vtable)) } } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 54bac70da3881..b29034e991e30 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -9,7 +9,9 @@ use rustc_middle::mir; use rustc_middle::ty::layout::LayoutOf; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; -use super::{ImmTy, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar}; +use super::{ + ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar, +}; use crate::util; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -144,7 +146,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { use rustc_middle::mir::Rvalue::*; match *rvalue { ThreadLocalRef(did) => { - let ptr = M::thread_local_static_base_pointer(self, did)?; + let ptr = M::thread_local_static_pointer(self, did)?; self.write_pointer(ptr, &dest)?; } @@ -258,17 +260,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = layout.offset_of_subfield(self, fields.iter()).bytes(); Scalar::from_target_usize(val, self) } - mir::NullOp::UbCheck(kind) => { - // We want to enable checks for library UB, because the interpreter doesn't - // know about those on its own. - // But we want to disable checks for language UB, because the interpreter - // has its own better checks for that. - let should_check = match kind { - mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions, - mir::UbKind::LanguageUb => false, - }; - Scalar::from_bool(should_check) - } + mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.ub_checks()), }; self.write_scalar(val, &dest)?; } @@ -313,6 +305,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variant_dest = self.project_downcast(dest, variant_index)?; (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::RawPtr(..) => { + // Pointers don't have "fields" in the normal sense, so the + // projection-based code below would either fail in projection + // or in type mismatches. Instead, build an `Immediate` from + // the parts and write that to the destination. + let [data, meta] = &operands.raw else { + bug!("{kind:?} should have 2 operands, had {operands:?}"); + }; + let data = self.eval_operand(data, None)?; + let data = self.read_pointer(&data)?; + let meta = self.eval_operand(meta, None)?; + let meta = if meta.layout.is_zst() { + MemPlaceMeta::None + } else { + MemPlaceMeta::Meta(self.read_scalar(&meta)?) + }; + let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self); + let ptr = ImmTy::from_immediate(ptr_imm, dest.layout); + self.copy_op(&ptr, dest)?; + return Ok(()); + } _ => (FIRST_VARIANT, dest.clone(), None), }; if active_field_index.is_some() { diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 82fb7ff1840f0..9c31532a9ce49 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -375,7 +375,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We cannot use `builtin_deref` here since we need to reject `Box`. Ok(Some(match ty.kind() { ty::Ref(_, ty, _) => *ty, - ty::RawPtr(mt) => mt.ty, + ty::RawPtr(ty, _) => *ty, // We only accept `Box` with the default allocator. _ if ty.is_box_global(*self.tcx) => ty.boxed_ty(), _ => return Ok(None), @@ -558,6 +558,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::FnPtrAddrShim(..) | ty::InstanceDef::ThreadLocalShim(..) + | ty::InstanceDef::AsyncDropGlueCtorShim(..) | ty::InstanceDef::Item(_) => { // We need MIR for this fn let Some((body, instance)) = M::find_mir_or_eval_fn( @@ -802,11 +803,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() { - let (recv, vptr) = self.unpack_dyn_star(&receiver_place)?; - let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; - if dyn_trait != data.principal() { - throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch); - } + let (recv, vptr) = self.unpack_dyn_star(&receiver_place, data)?; + let (dyn_ty, _dyn_trait) = self.get_ptr_vtable(vptr)?; (vptr, dyn_ty, recv.ptr()) } else { @@ -828,7 +826,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?; let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; if dyn_trait != data.principal() { - throw_ub_custom!(fluent::const_eval_dyn_call_vtable_mismatch); + throw_ub!(InvalidVTableTrait { + expected_trait: data, + vtable_trait: dyn_trait, + }); } // It might be surprising that we use a pointer as the receiver even if this @@ -938,13 +939,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let place = self.force_allocation(place)?; let place = match place.layout.ty.kind() { - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(data, _, ty::Dyn) => { // Dropping a trait object. Need to find actual drop fn. - self.unpack_dyn_trait(&place)?.0 + self.unpack_dyn_trait(&place, data)?.0 } - ty::Dynamic(_, _, ty::DynStar) => { + ty::Dynamic(data, _, ty::DynStar) => { // Dropping a `dyn*`. Need to find actual drop fn. - self.unpack_dyn_star(&place)?.0 + self.unpack_dyn_star(&place, data)?.0 } _ => { debug_assert_eq!( diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index a9ca268a2a96f..b603ef0d27a47 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -28,7 +28,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?; let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref); - let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_symbolic_allocation))?; + let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?; Ok(vtable_ptr.into()) } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index d18600ce7d755..14566719ccd72 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -339,16 +339,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); match tail.kind() { - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(data, _, ty::Dyn) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; // Make sure it is a genuine vtable pointer. - let (_ty, _trait) = try_validation!( + let (_dyn_ty, dyn_trait) = try_validation!( self.ecx.get_ptr_vtable(vtable), self.path, Ub(DanglingIntPointer(..) | InvalidVTablePointer(..)) => InvalidVTablePtr { value: format!("{vtable}") } ); - // FIXME: check if the type/trait match what ty::Dynamic says? + // Make sure it is for the right trait. + if dyn_trait != data.principal() { + throw_validation_failure!( + self.path, + InvalidMetaWrongTrait { expected_trait: data, vtable_trait: dyn_trait } + ); + } } ty::Slice(..) | ty::Str => { let _len = meta.unwrap_meta().to_target_usize(self.ecx)?; @@ -449,67 +455,41 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // `!` is a ZST and we want to validate it. if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) { let mut skip_recursive_check = false; - // Let's see what kind of memory this points to. - // `unwrap` since dangling pointers have already been handled. - let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id).unwrap(); - let alloc_actual_mutbl = match alloc_kind { - GlobalAlloc::Static(did) => { - // Special handling for pointers to statics (irrespective of their type). - assert!(!self.ecx.tcx.is_thread_local_static(did)); - assert!(self.ecx.tcx.is_static(did)); - // Mode-specific checks - match self.ctfe_mode { - Some( - CtfeValidationMode::Static { .. } - | CtfeValidationMode::Promoted { .. }, - ) => { - // We skip recursively checking other statics. These statics must be sound by - // themselves, and the only way to get broken statics here is by using - // unsafe code. - // The reasons we don't check other statics is twofold. For one, in all - // sound cases, the static was already validated on its own, and second, we - // trigger cycle errors if we try to compute the value of the other static - // and that static refers back to us (potentially through a promoted). - // This could miss some UB, but that's fine. - skip_recursive_check = true; - } - Some(CtfeValidationMode::Const { .. }) => { - // We can't recursively validate `extern static`, so we better reject them. - if self.ecx.tcx.is_foreign_item(did) { - throw_validation_failure!(self.path, ConstRefToExtern); - } - } - None => {} + let alloc_actual_mutbl = mutability(self.ecx, alloc_id); + if let GlobalAlloc::Static(did) = self.ecx.tcx.global_alloc(alloc_id) { + let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() }; + // Special handling for pointers to statics (irrespective of their type). + assert!(!self.ecx.tcx.is_thread_local_static(did)); + assert!(self.ecx.tcx.is_static(did)); + // Mode-specific checks + match self.ctfe_mode { + Some( + CtfeValidationMode::Static { .. } | CtfeValidationMode::Promoted { .. }, + ) => { + // We skip recursively checking other statics. These statics must be sound by + // themselves, and the only way to get broken statics here is by using + // unsafe code. + // The reasons we don't check other statics is twofold. For one, in all + // sound cases, the static was already validated on its own, and second, we + // trigger cycle errors if we try to compute the value of the other static + // and that static refers back to us (potentially through a promoted). + // This could miss some UB, but that's fine. + // We still walk nested allocations, as they are fundamentally part of this validation run. + // This means we will also recurse into nested statics of *other* + // statics, even though we do not recurse into other statics directly. + // That's somewhat inconsistent but harmless. + skip_recursive_check = !nested; } - // Return alloc mutability. For "root" statics we look at the type to account for interior - // mutability; for nested statics we have no type and directly use the annotated mutability. - let DefKind::Static { mutability, nested } = self.ecx.tcx.def_kind(did) - else { - bug!() - }; - match (mutability, nested) { - (Mutability::Mut, _) => Mutability::Mut, - (Mutability::Not, true) => Mutability::Not, - (Mutability::Not, false) - if !self - .ecx - .tcx - .type_of(did) - .no_bound_vars() - .expect("statics should not have generic parameters") - .is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all()) => - { - Mutability::Mut + Some(CtfeValidationMode::Const { .. }) => { + // We can't recursively validate `extern static`, so we better reject them. + if self.ecx.tcx.is_foreign_item(did) { + throw_validation_failure!(self.path, ConstRefToExtern); } - (Mutability::Not, false) => Mutability::Not, } + None => {} } - GlobalAlloc::Memory(alloc) => alloc.inner().mutability, - GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => { - // These are immutable, we better don't allow mutable pointers here. - Mutability::Not - } - }; + } + // Mutability check. // If this allocation has size zero, there is no actual mutability here. let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id); @@ -640,6 +620,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' | ty::Str | ty::Dynamic(..) | ty::Closure(..) + | ty::Pat(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => Ok(false), // Some types only occur during typechecking, they have no layout. @@ -707,17 +688,58 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn in_mutable_memory(&self, op: &OpTy<'tcx, M::Provenance>) -> bool { if let Some(mplace) = op.as_mplace_or_imm().left() { if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) { - let mutability = match self.ecx.tcx.global_alloc(alloc_id) { - GlobalAlloc::Static(_) => { - self.ecx.memory.alloc_map.get(alloc_id).unwrap().1.mutability + return mutability(self.ecx, alloc_id).is_mut(); + } + } + false + } +} + +/// Returns whether the allocation is mutable, and whether it's actually a static. +/// For "root" statics we look at the type to account for interior +/// mutability; for nested statics we have no type and directly use the annotated mutability. +fn mutability<'mir, 'tcx: 'mir>( + ecx: &InterpCx<'mir, 'tcx, impl Machine<'mir, 'tcx>>, + alloc_id: AllocId, +) -> Mutability { + // Let's see what kind of memory this points to. + // We're not using `try_global_alloc` since dangling pointers have already been handled. + match ecx.tcx.global_alloc(alloc_id) { + GlobalAlloc::Static(did) => { + let DefKind::Static { mutability, nested } = ecx.tcx.def_kind(did) else { bug!() }; + if nested { + assert!( + ecx.memory.alloc_map.get(alloc_id).is_none(), + "allocations of nested statics are already interned: {alloc_id:?}, {did:?}" + ); + // Nested statics in a `static` are never interior mutable, + // so just use the declared mutability. + mutability + } else { + let mutability = match mutability { + Mutability::Not + if !ecx + .tcx + .type_of(did) + .no_bound_vars() + .expect("statics should not have generic parameters") + .is_freeze(*ecx.tcx, ty::ParamEnv::reveal_all()) => + { + Mutability::Mut } - GlobalAlloc::Memory(alloc) => alloc.inner().mutability, - _ => span_bug!(self.ecx.tcx.span, "not a memory allocation"), + _ => mutability, }; - return mutability == Mutability::Mut; + if let Some((_, alloc)) = ecx.memory.alloc_map.get(alloc_id) { + assert_eq!(alloc.mutability, mutability); + } + mutability } } - false + GlobalAlloc::Memory(alloc) => alloc.inner().mutability, + GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => { + // These are immutable, we better don't allow mutable pointers here. + Mutability::Not + } } } @@ -917,7 +939,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } } _ => { - self.walk_value(op)?; // default handler + // default handler + try_validation!( + self.walk_value(op), + self.path, + // It's not great to catch errors here, since we can't give a very good path, + // but it's better than ICEing. + Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { + InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + }, + ); } } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 0e824f3f592d4..84557b8e2d600 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -88,22 +88,22 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { // Special treatment for special types, where the (static) layout is not sufficient. match *ty.kind() { // If it is a trait object, switch to the real type that was used to create it. - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(data, _, ty::Dyn) => { // Dyn types. This is unsized, and the actual dynamic type of the data is given by the // vtable stored in the place metadata. // unsized values are never immediate, so we can assert_mem_place let op = v.to_op(self.ecx())?; let dest = op.assert_mem_place(); - let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0; + let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?.0; trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); // recurse with the inner type return self.visit_field(v, 0, &inner_mplace.into()); } - ty::Dynamic(_, _, ty::DynStar) => { + ty::Dynamic(data, _, ty::DynStar) => { // DynStar types. Very different from a dyn type (but strangely part of the // same variant in `TyKind`): These are pairs where the 2nd component is the // vtable, and the first component is the data (which must be ptr-sized). - let data = self.ecx().unpack_dyn_star(v)?.0; + let data = self.ecx().unpack_dyn_star(v, data)?.0; return self.visit_field(v, 0, &data); } // Slices do not need special handling here: they have `Array` field diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1e7ee208af1ab..d27d42737cd6c 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -11,7 +11,6 @@ Rust MIR: a lowered representation of Rust. #![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(slice_ptr_get)] #![feature(strict_provenance)] @@ -32,6 +31,8 @@ pub mod interpret; pub mod transform; pub mod util; +use std::sync::atomic::AtomicBool; + pub use errors::ReportErrorExt; use rustc_middle::{ty, util::Providers}; @@ -40,6 +41,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { const_eval::provide(providers); + providers.tag_for_variant = const_eval::tag_for_variant_provider; providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; providers.eval_static_initializer = const_eval::eval_static_initializer_provider; @@ -57,3 +59,8 @@ pub fn provide(providers: &mut Providers) { util::check_validity_requirement(tcx, init_kind, param_env_and_ty) }; } + +/// `rustc_driver::main` installs a handler that will set this to `true` if +/// the compiler has been sent a request to shut down, such as by a Ctrl-C. +/// This static lives here because it is only read by the interpreter. +pub static CTRL_C_RECEIVED: AtomicBool = AtomicBool::new(false); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index a93e8138aa412..f9786acfc6c26 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -168,7 +168,7 @@ impl<'ck, 'mir, 'tcx> TypeVisitor> for LocalReturnTyVisitor<'ck, 'm match t.kind() { ty::FnPtr(_) => {} ty::Ref(_, _, hir::Mutability::Mut) => { - self.checker.check_op(ops::ty::MutRef(self.kind)); + self.checker.check_op(ops::mut_ref::MutRef(self.kind)); t.super_visit_with(self) } _ => t.super_visit_with(self), @@ -331,6 +331,11 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { if self.tcx.is_thread_local_static(def_id) { self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`"); } + if let Some(def_id) = def_id.as_local() + && let Err(guar) = self.tcx.at(span).check_well_formed(hir::OwnerId { def_id }) + { + self.error_emitted = Some(guar); + } self.check_op_spanned(ops::StaticAccess, span) } @@ -409,7 +414,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { BorrowKind::Shared => { PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) } - BorrowKind::Fake => { + BorrowKind::Fake(_) => { PlaceContext::NonMutatingUse(NonMutatingUseContext::FakeBorrow) } BorrowKind::Mut { .. } => { @@ -482,7 +487,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } - Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake, place) + Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place) | Rvalue::AddressOf(Mutability::Not, place) => { let borrowed_place_has_mut_interior = qualifs::in_place::( self.ccx, @@ -544,10 +549,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Unsizing is implemented for CTFE. } - Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { + Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { self.check_op(ops::RawPtrToIntCast); } - Rvalue::Cast(CastKind::PointerFromExposedAddress, _, _) => { + Rvalue::Cast(CastKind::PointerWithExposedProvenance, _, _) => { // Since no pointer can ever get exposed (rejected above), this is easy to support. } @@ -558,7 +563,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, _, ) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 15720f25c5cff..dda8f3ed87d28 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -9,9 +9,10 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_middle::mir::{self, CallSource}; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::TraitRef; -use rustc_middle::ty::{suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, Ty}; -use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; +use rustc_middle::ty::{ + self, suggest_constraining_type_param, Closure, FnDef, FnPtr, GenericArgKind, GenericArgsRef, + Param, TraitRef, Ty, +}; use rustc_middle::util::{call_kind, CallDesugaringKind, CallKind}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; @@ -99,7 +100,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { #[allow(rustc::untranslatable_diagnostic)] fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> { let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self; - let ConstCx { tcx, param_env, .. } = *ccx; + let ConstCx { tcx, param_env, body, .. } = *ccx; let diag_trait = |err, self_ty: Ty<'_>, trait_id| { let trait_ref = TraitRef::from_method(tcx, trait_id, args); @@ -123,7 +124,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { ); } } - Adt(..) => { + ty::Adt(..) => { let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref); @@ -297,10 +298,12 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { ccx.const_kind(), )); - if let Some(feature) = feature - && ccx.tcx.sess.is_nightly_build() - { - err.help(format!("add `#![feature({feature})]` to the crate attributes to enable",)); + if let Some(feature) = feature { + ccx.tcx.disabled_nightly_features( + &mut err, + body.source.def_id().as_local().map(|local| ccx.tcx.local_def_id_to_hir_id(local)), + [(String::new(), feature)], + ); } if let ConstContext::Static(_) = ccx.const_kind() { @@ -618,7 +621,7 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { } /// Types that cannot appear in the signature or locals of a `const fn`. -pub mod ty { +pub mod mut_ref { use super::*; #[derive(Debug)] diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index 2c835f6750f75..5ae3ffaaec2f2 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs @@ -105,7 +105,7 @@ where fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool { match kind { mir::BorrowKind::Mut { .. } => true, - mir::BorrowKind::Shared | mir::BorrowKind::Fake => { + mir::BorrowKind::Shared | mir::BorrowKind::Fake(_) => { self.shared_borrow_allows_mutation(place) } } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index f26c8f8592dea..837c02a5b769b 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_infer::traits::Reveal; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -84,7 +85,7 @@ impl<'tcx> MirPass<'tcx> for Validator { cfg_checker.check_cleanup_control_flow(); // Also run the TypeChecker. - for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) { + for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body, body) { cfg_checker.fail(location, msg); } @@ -100,18 +101,17 @@ impl<'tcx> MirPass<'tcx> for Validator { } // Enforce that coroutine-closure layouts are identical. - if let Some(layout) = body.coroutine_layout() + if let Some(layout) = body.coroutine_layout_raw() && let Some(by_move_body) = body.coroutine_by_move_body() - && let Some(by_move_layout) = by_move_body.coroutine_layout() + && let Some(by_move_layout) = by_move_body.coroutine_layout_raw() { - if layout != by_move_layout { - // If this turns out not to be true, please let compiler-errors know. - // It is possible to support, but requires some changes to the layout - // computation code. + // FIXME(async_closures): We could do other validation here? + if layout.variant_fields.len() != by_move_layout.variant_fields.len() { cfg_checker.fail( Location::START, format!( - "Coroutine layout differs from by-move coroutine layout:\n\ + "Coroutine layout has different number of variant fields from \ + by-move coroutine layout:\n\ layout: {layout:#?}\n\ by_move_layout: {by_move_layout:#?}", ), @@ -341,15 +341,24 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { // FIXME(JakobDegen) The validator should check that `self.mir_phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. - if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) { + if matches!(kind, RetagKind::TwoPhase) { self.fail(location, format!("explicit `{kind:?}` is forbidden")); } } + StatementKind::Coverage(kind) => { + if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) + && let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind + { + self.fail( + location, + format!("{kind:?} should have been removed after analysis"), + ); + } + } StatementKind::Assign(..) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Intrinsic(_) - | StatementKind::Coverage(_) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(..) | StatementKind::Nop => {} @@ -530,19 +539,25 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { /// A faster version of the validation pass that only checks those things which may break when /// instantiating any generic parameters. +/// +/// `caller_body` is used to detect cycles in MIR inlining and MIR validation before +/// `optimized_mir` is available. pub fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, mir_phase: MirPhase, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, + caller_body: &Body<'tcx>, ) -> Vec<(Location, String)> { - let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() }; + let mut type_checker = + TypeChecker { body, caller_body, tcx, param_env, mir_phase, failures: Vec::new() }; type_checker.visit_body(body); type_checker.failures } struct TypeChecker<'a, 'tcx> { body: &'a Body<'tcx>, + caller_body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, mir_phase: MirPhase, @@ -694,13 +709,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } &ty::Coroutine(def_id, args) => { let f_ty = if let Some(var) = parent_ty.variant_index { - let gen_body = if def_id == self.body.source.def_id() { - self.body + // If we're currently validating an inlined copy of this body, + // then it will no longer be parameterized over the original + // args of the coroutine. Otherwise, we prefer to use this body + // since we may be in the process of computing this MIR in the + // first place. + let layout = if def_id == self.caller_body.source.def_id() { + // FIXME: This is not right for async closures. + self.caller_body.coroutine_layout_raw() } else { - self.tcx.optimized_mir(def_id) + self.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) }; - let Some(layout) = gen_body.coroutine_layout() else { + let Some(layout) = layout else { self.fail( location, format!("No coroutine layout for {parent_ty:?}"), @@ -902,8 +923,49 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } + AggregateKind::RawPtr(pointee_ty, mutability) => { + if !matches!(self.mir_phase, MirPhase::Runtime(_)) { + // It would probably be fine to support this in earlier phases, + // but at the time of writing it's only ever introduced from intrinsic lowering, + // so earlier things just `bug!` on it. + self.fail(location, "RawPtr should be in runtime MIR only"); + } + + if fields.len() != 2 { + self.fail(location, "raw pointer aggregate must have 2 fields"); + } else { + let data_ptr_ty = fields.raw[0].ty(self.body, self.tcx); + let metadata_ty = fields.raw[1].ty(self.body, self.tcx); + if let ty::RawPtr(in_pointee, in_mut) = data_ptr_ty.kind() { + if *in_mut != mutability { + self.fail(location, "input and output mutability must match"); + } + + // FIXME: check `Thin` instead of `Sized` + if !in_pointee.is_sized(self.tcx, self.param_env) { + self.fail(location, "input pointer must be thin"); + } + } else { + self.fail( + location, + "first operand to raw pointer aggregate must be a raw pointer", + ); + } + + // FIXME: Check metadata more generally + if pointee_ty.is_slice() { + if !self.mir_assign_valid_types(metadata_ty, self.tcx.types.usize) { + self.fail(location, "slice metadata must be usize"); + } + } else if pointee_ty.is_sized(self.tcx, self.param_env) { + if metadata_ty != self.tcx.types.unit { + self.fail(location, "metadata for pointer-to-thin must be unit"); + } + } + } + } }, - Rvalue::Ref(_, BorrowKind::Fake, _) => { + Rvalue::Ref(_, BorrowKind::Fake(_), _) => { if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, @@ -965,6 +1027,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } + Cmp => { + for x in [a, b] { + check_kinds!( + x, + "Cannot three-way compare non-integer type {:?}", + ty::Char | ty::Uint(..) | ty::Int(..) + ) + } + } AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr | ShrUnchecked => { for x in [a, b] { @@ -1046,8 +1117,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME(dyn-star): make sure nothing needs to be done here. } // FIXME: Add Checks for these - CastKind::PointerFromExposedAddress - | CastKind::PointerExposeAddress + CastKind::PointerWithExposedProvenance + | CastKind::PointerExposeProvenance | CastKind::PointerCoercion(_) => {} CastKind::IntToInt | CastKind::IntToFloat => { let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool(); @@ -1157,7 +1228,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::AddressOf(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _) + | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _) | Rvalue::Discriminant(_) => {} } self.super_rvalue(rvalue, location); @@ -1272,7 +1343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME(JakobDegen) The validator should check that `self.mir_phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. - if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) { + if matches!(kind, RetagKind::TwoPhase) { self.fail(location, format!("explicit `{kind:?}` is forbidden")); } } diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index a8060463b69ed..0c3b59a0e78ec 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -19,7 +19,7 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool { match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true, - Eq | Ne | Lt | Le | Gt | Ge => false, + Eq | Ne | Lt | Le | Gt | Ge | Cmp => false, } } @@ -30,7 +30,7 @@ pub fn binop_right_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor - | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true, + | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge | Cmp => true, Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false, } } diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 2b80623ab4545..e474b952938a6 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -31,9 +31,10 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | ty::Uint(_) | ty::Float(_) | ty::Str + | ty::Pat(_, _) | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnPtr(_) | ty::Never diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index a45f1dd72a126..30e240cf85b84 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -72,7 +72,7 @@ fn dominators_impl(graph: &G) -> Inner { IndexVec::with_capacity(graph.num_nodes()); let mut stack = vec![PreOrderFrame { - pre_order_idx: PreorderIndex::new(0), + pre_order_idx: PreorderIndex::ZERO, iter: graph.successors(graph.start_node()), }]; let mut pre_order_to_real: IndexVec = @@ -80,8 +80,8 @@ fn dominators_impl(graph: &G) -> Inner { let mut real_to_pre_order: IndexVec> = IndexVec::from_elem_n(None, graph.num_nodes()); pre_order_to_real.push(graph.start_node()); - parent.push(PreorderIndex::new(0)); // the parent of the root node is the root for now. - real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(0)); + parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now. + real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO); let mut post_order_idx = 0; // Traverse the graph, collecting a number of things: @@ -111,7 +111,7 @@ fn dominators_impl(graph: &G) -> Inner { let reachable_vertices = pre_order_to_real.len(); - let mut idom = IndexVec::from_elem_n(PreorderIndex::new(0), reachable_vertices); + let mut idom = IndexVec::from_elem_n(PreorderIndex::ZERO, reachable_vertices); let mut semi = IndexVec::from_fn_n(std::convert::identity, reachable_vertices); let mut label = semi.clone(); let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices); diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index 9eb4b5278c079..78d05a6e1951b 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -1,4 +1,4 @@ -use super::{DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors}; +use super::{DirectedGraph, StartNode, Successors}; use rustc_index::bit_set::BitSet; use rustc_index::{IndexSlice, IndexVec}; use std::ops::ControlFlow; @@ -6,14 +6,14 @@ use std::ops::ControlFlow; #[cfg(test)] mod tests; -pub fn post_order_from( +pub fn post_order_from( graph: &G, start_node: G::Node, ) -> Vec { post_order_from_to(graph, start_node, None) } -pub fn post_order_from_to( +pub fn post_order_from_to( graph: &G, start_node: G::Node, end_node: Option, @@ -27,7 +27,7 @@ pub fn post_order_from_to( result } -fn post_order_walk( +fn post_order_walk( graph: &G, node: G::Node, result: &mut Vec, @@ -60,7 +60,7 @@ fn post_order_walk( } } -pub fn reverse_post_order( +pub fn reverse_post_order( graph: &G, start_node: G::Node, ) -> Vec { @@ -70,21 +70,21 @@ pub fn reverse_post_order( } /// A "depth-first search" iterator for a directed graph. -pub struct DepthFirstSearch<'graph, G> +pub struct DepthFirstSearch where - G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, + G: DirectedGraph + Successors, { - graph: &'graph G, + graph: G, stack: Vec, visited: BitSet, } -impl<'graph, G> DepthFirstSearch<'graph, G> +impl DepthFirstSearch where - G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, + G: DirectedGraph + Successors, { - pub fn new(graph: &'graph G) -> Self { - Self { graph, stack: vec![], visited: BitSet::new_empty(graph.num_nodes()) } + pub fn new(graph: G) -> Self { + Self { stack: vec![], visited: BitSet::new_empty(graph.num_nodes()), graph } } /// Version of `push_start_node` that is convenient for chained @@ -125,9 +125,9 @@ where } } -impl std::fmt::Debug for DepthFirstSearch<'_, G> +impl std::fmt::Debug for DepthFirstSearch where - G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, + G: DirectedGraph + Successors, { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut f = fmt.debug_set(); @@ -138,9 +138,9 @@ where } } -impl Iterator for DepthFirstSearch<'_, G> +impl Iterator for DepthFirstSearch where - G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, + G: DirectedGraph + Successors, { type Item = G::Node; @@ -201,7 +201,7 @@ struct Event { /// [CLR]: https://en.wikipedia.org/wiki/Introduction_to_Algorithms pub struct TriColorDepthFirstSearch<'graph, G> where - G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, + G: ?Sized + DirectedGraph + Successors, { graph: &'graph G, stack: Vec>, @@ -211,7 +211,7 @@ where impl<'graph, G> TriColorDepthFirstSearch<'graph, G> where - G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, + G: ?Sized + DirectedGraph + Successors, { pub fn new(graph: &'graph G) -> Self { TriColorDepthFirstSearch { @@ -278,7 +278,7 @@ where impl TriColorDepthFirstSearch<'_, G> where - G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors + WithStartNode, + G: ?Sized + DirectedGraph + Successors + StartNode, { /// Performs a depth-first search, starting from `G::start_node()`. /// diff --git a/compiler/rustc_data_structures/src/graph/mod.rs b/compiler/rustc_data_structures/src/graph/mod.rs index e06ab2fe36b92..103ddd917bf84 100644 --- a/compiler/rustc_data_structures/src/graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/mod.rs @@ -12,70 +12,69 @@ mod tests; pub trait DirectedGraph { type Node: Idx; -} -pub trait WithNumNodes: DirectedGraph { fn num_nodes(&self) -> usize; } -pub trait WithNumEdges: DirectedGraph { +pub trait NumEdges: DirectedGraph { fn num_edges(&self) -> usize; } -pub trait WithSuccessors: DirectedGraph -where - Self: for<'graph> GraphSuccessors<'graph, Item = ::Node>, -{ - fn successors(&self, node: Self::Node) -> >::Iter; - - fn depth_first_search(&self, from: Self::Node) -> iterate::DepthFirstSearch<'_, Self> - where - Self: WithNumNodes, - { - iterate::DepthFirstSearch::new(self).with_start_node(from) - } +pub trait StartNode: DirectedGraph { + fn start_node(&self) -> Self::Node; } -#[allow(unused_lifetimes)] -pub trait GraphSuccessors<'graph> { - type Item; - type Iter: Iterator; +pub trait Successors: DirectedGraph { + fn successors(&self, node: Self::Node) -> impl Iterator; } -pub trait WithPredecessors: DirectedGraph -where - Self: for<'graph> GraphPredecessors<'graph, Item = ::Node>, -{ - fn predecessors(&self, node: Self::Node) -> >::Iter; +pub trait Predecessors: DirectedGraph { + fn predecessors(&self, node: Self::Node) -> impl Iterator; } -#[allow(unused_lifetimes)] -pub trait GraphPredecessors<'graph> { - type Item; - type Iter: Iterator; -} - -pub trait WithStartNode: DirectedGraph { - fn start_node(&self) -> Self::Node; -} +/// Alias for [`DirectedGraph`] + [`StartNode`] + [`Predecessors`] + [`Successors`]. +pub trait ControlFlowGraph: DirectedGraph + StartNode + Predecessors + Successors {} +impl ControlFlowGraph for T where T: DirectedGraph + StartNode + Predecessors + Successors {} -pub trait ControlFlowGraph: - DirectedGraph + WithStartNode + WithPredecessors + WithSuccessors + WithNumNodes +/// Returns `true` if the graph has a cycle that is reachable from the start node. +pub fn is_cyclic(graph: &G) -> bool +where + G: ?Sized + DirectedGraph + StartNode + Successors, { - // convenient trait + iterate::TriColorDepthFirstSearch::new(graph) + .run_from_start(&mut iterate::CycleDetector) + .is_some() } -impl ControlFlowGraph for T where - T: DirectedGraph + WithStartNode + WithPredecessors + WithSuccessors + WithNumNodes +pub fn depth_first_search(graph: G, from: G::Node) -> iterate::DepthFirstSearch +where + G: Successors, { + iterate::DepthFirstSearch::new(graph).with_start_node(from) } -/// Returns `true` if the graph has a cycle that is reachable from the start node. -pub fn is_cyclic(graph: &G) -> bool +pub fn depth_first_search_as_undirected( + graph: G, + from: G::Node, +) -> iterate::DepthFirstSearch> where - G: ?Sized + DirectedGraph + WithStartNode + WithSuccessors + WithNumNodes, + G: Successors + Predecessors, { - iterate::TriColorDepthFirstSearch::new(graph) - .run_from_start(&mut iterate::CycleDetector) - .is_some() + struct AsUndirected(G); + + impl DirectedGraph for AsUndirected { + type Node = G::Node; + + fn num_nodes(&self) -> usize { + self.0.num_nodes() + } + } + + impl Successors for AsUndirected { + fn successors(&self, node: Self::Node) -> impl Iterator { + self.0.successors(node).chain(self.0.predecessors(node)) + } + } + + iterate::DepthFirstSearch::new(AsUndirected(graph)).with_start_node(from) } diff --git a/compiler/rustc_data_structures/src/graph/reference.rs b/compiler/rustc_data_structures/src/graph/reference.rs index c259fe56c1509..7a487552f5385 100644 --- a/compiler/rustc_data_structures/src/graph/reference.rs +++ b/compiler/rustc_data_structures/src/graph/reference.rs @@ -2,38 +2,26 @@ use super::*; impl<'graph, G: DirectedGraph> DirectedGraph for &'graph G { type Node = G::Node; -} -impl<'graph, G: WithNumNodes> WithNumNodes for &'graph G { fn num_nodes(&self) -> usize { (**self).num_nodes() } } -impl<'graph, G: WithStartNode> WithStartNode for &'graph G { +impl<'graph, G: StartNode> StartNode for &'graph G { fn start_node(&self) -> Self::Node { (**self).start_node() } } -impl<'graph, G: WithSuccessors> WithSuccessors for &'graph G { - fn successors(&self, node: Self::Node) -> >::Iter { +impl<'graph, G: Successors> Successors for &'graph G { + fn successors(&self, node: Self::Node) -> impl Iterator { (**self).successors(node) } } -impl<'graph, G: WithPredecessors> WithPredecessors for &'graph G { - fn predecessors(&self, node: Self::Node) -> >::Iter { +impl<'graph, G: Predecessors> Predecessors for &'graph G { + fn predecessors(&self, node: Self::Node) -> impl Iterator { (**self).predecessors(node) } } - -impl<'iter, 'graph, G: WithPredecessors> GraphPredecessors<'iter> for &'graph G { - type Item = G::Node; - type Iter = >::Iter; -} - -impl<'iter, 'graph, G: WithSuccessors> GraphSuccessors<'iter> for &'graph G { - type Item = G::Node; - type Iter = >::Iter; -} diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index b54d75f7ed703..5021e5e8fc090 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -7,7 +7,7 @@ use crate::fx::FxHashSet; use crate::graph::vec_graph::VecGraph; -use crate::graph::{DirectedGraph, GraphSuccessors, WithNumEdges, WithNumNodes, WithSuccessors}; +use crate::graph::{DirectedGraph, NumEdges, Successors}; use rustc_index::{Idx, IndexSlice, IndexVec}; use std::ops::Range; @@ -39,7 +39,7 @@ pub struct SccData { } impl Sccs { - pub fn new(graph: &(impl DirectedGraph + WithNumNodes + WithSuccessors)) -> Self { + pub fn new(graph: &(impl DirectedGraph + Successors)) -> Self { SccsConstruction::construct(graph) } @@ -89,30 +89,22 @@ impl Sccs { } } -impl DirectedGraph for Sccs { +impl DirectedGraph for Sccs { type Node = S; -} -impl WithNumNodes for Sccs { fn num_nodes(&self) -> usize { self.num_sccs() } } -impl WithNumEdges for Sccs { +impl NumEdges for Sccs { fn num_edges(&self) -> usize { self.scc_data.all_successors.len() } } -impl<'graph, N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs { - type Item = S; - - type Iter = std::iter::Cloned>; -} - -impl WithSuccessors for Sccs { - fn successors(&self, node: S) -> >::Iter { +impl Successors for Sccs { + fn successors(&self, node: S) -> impl Iterator { self.successors(node).iter().cloned() } } @@ -158,7 +150,7 @@ impl SccData { } } -struct SccsConstruction<'c, G: DirectedGraph + WithNumNodes + WithSuccessors, S: Idx> { +struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> { graph: &'c G, /// The state of each node; used during walk to record the stack @@ -218,7 +210,7 @@ enum WalkReturn { impl<'c, G, S> SccsConstruction<'c, G, S> where - G: DirectedGraph + WithNumNodes + WithSuccessors, + G: DirectedGraph + Successors, S: Idx, { /// Identifies SCCs in the graph `G` and computes the resulting diff --git a/compiler/rustc_data_structures/src/graph/tests.rs b/compiler/rustc_data_structures/src/graph/tests.rs index 7f4ef906b361e..85c2703cc2538 100644 --- a/compiler/rustc_data_structures/src/graph/tests.rs +++ b/compiler/rustc_data_structures/src/graph/tests.rs @@ -1,7 +1,5 @@ use crate::fx::FxHashMap; use std::cmp::max; -use std::iter; -use std::slice; use super::*; @@ -36,38 +34,26 @@ impl TestGraph { impl DirectedGraph for TestGraph { type Node = usize; -} -impl WithStartNode for TestGraph { - fn start_node(&self) -> usize { - self.start_node + fn num_nodes(&self) -> usize { + self.num_nodes } } -impl WithNumNodes for TestGraph { - fn num_nodes(&self) -> usize { - self.num_nodes +impl StartNode for TestGraph { + fn start_node(&self) -> usize { + self.start_node } } -impl WithPredecessors for TestGraph { - fn predecessors(&self, node: usize) -> >::Iter { +impl Predecessors for TestGraph { + fn predecessors(&self, node: usize) -> impl Iterator { self.predecessors[&node].iter().cloned() } } -impl WithSuccessors for TestGraph { - fn successors(&self, node: usize) -> >::Iter { +impl Successors for TestGraph { + fn successors(&self, node: usize) -> impl Iterator { self.successors[&node].iter().cloned() } } - -impl<'graph> GraphPredecessors<'graph> for TestGraph { - type Item = usize; - type Iter = iter::Cloned>; -} - -impl<'graph> GraphSuccessors<'graph> for TestGraph { - type Item = usize; - type Iter = iter::Cloned>; -} diff --git a/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs index 00f6266ce1df7..120244c8918a0 100644 --- a/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs @@ -1,107 +1,235 @@ -use crate::graph::{DirectedGraph, GraphSuccessors, WithNumEdges, WithNumNodes, WithSuccessors}; +use crate::graph::{DirectedGraph, NumEdges, Predecessors, Successors}; use rustc_index::{Idx, IndexVec}; #[cfg(test)] mod tests; -pub struct VecGraph { - /// Maps from a given node to an index where the set of successors - /// for that node starts. The index indexes into the `edges` - /// vector. To find the range for a given node, we look up the - /// start for that node and then the start for the next node - /// (i.e., with an index 1 higher) and get the range between the - /// two. This vector always has an extra entry so that this works - /// even for the max element. +/// A directed graph, efficient for cases where node indices are pre-existing. +/// +/// If `BR` is true, the graph will store back-references, allowing you to get predecessors. +pub struct VecGraph { + // This is basically a `HashMap, If>)>` -- a map from a node index, to + // a list of targets of outgoing edges and (if enabled) a list of sources of incoming edges. + // + // However, it is condensed into two arrays as an optimization. + // + // `node_starts[n]` is the start of the list of targets of outgoing edges for node `n`. + // So you can get node's successors with `edge_targets[node_starts[n]..node_starts[n + 1]]`. + // + // If `BR` is true (back references are enabled), then `node_starts[n + edge_count]` is the + // start of the list of *sources* of incoming edges. You can get predecessors of a node + // similarly to its successors but offsetting by `edge_count`. `edge_count` is + // `edge_targets.len()/2` (again, in case BR is true) because half of the vec is back refs. + // + // All of this might be confusing, so here is an example graph and its representation: + // + // n3 ----+ + // ^ | (if BR = true) + // | v outgoing edges incoming edges + // n0 -> n1 -> n2 ______________ __________________ + // / \ / \ + // node indices[1]: n0, n1, n2, n3, n0, n1, n2, n3, n/a + // vec indices: n0, n1, n2, n3, n4, n5, n6, n7, n8 + // node_starts: [0, 1, 3, 4 4, 4, 5, 7, 8] + // | | | | | | | | | + // | | +---+ +---+ | +---+ | + // | | | | | | | + // v v v v v v v + // edge_targets: [n1, n2, n3, n2 n0, n1, n3, n1] + // / \____/ | | \____/ \ + // n0->n1 / | | \ n3<-n1 + // / n3->n2 [2] n1<-n0 [2] \ + // n1->n2, n1->n3 n2<-n1, n2<-n3 + // + // The incoming edges are basically stored in the same way as outgoing edges, but offset and + // the graph they store is the inverse of the original. Last index in the `node_starts` array + // always points to one-past-the-end, so that we don't need to bound check `node_starts[n + 1]` + // + // [1]: "node indices" are the indices a user of `VecGraph` might use, + // note that they are different from "vec indices", + // which are the real indices you need to index `node_starts` + // + // [2]: Note that even though n2 also points to here, + // the next index also points here, so n2 has no + // successors (`edge_targets[3..3] = []`). + // Similarly with n0 and incoming edges + // + // If this is still confusing... then sorry :( + // + /// Indices into `edge_targets` that signify a start of list of edges. node_starts: IndexVec, + /// Targets (or sources for back refs) of edges edge_targets: Vec, } -impl VecGraph { +impl VecGraph { pub fn new(num_nodes: usize, mut edge_pairs: Vec<(N, N)>) -> Self { - // Sort the edges by the source -- this is important. - edge_pairs.sort(); - let num_edges = edge_pairs.len(); - // Store the *target* of each edge into `edge_targets`. - let edge_targets: Vec = edge_pairs.iter().map(|&(_, target)| target).collect(); - - // Create the *edge starts* array. We are iterating over the - // (sorted) edge pairs. We maintain the invariant that the - // length of the `node_starts` array is enough to store the - // current source node -- so when we see that the source node - // for an edge is greater than the current length, we grow the - // edge-starts array by just enough. - let mut node_starts = IndexVec::with_capacity(num_edges); - for (index, &(source, _)) in edge_pairs.iter().enumerate() { - // If we have a list like `[(0, x), (2, y)]`: - // - // - Start out with `node_starts` of `[]` - // - Iterate to `(0, x)` at index 0: - // - Push one entry because `node_starts.len()` (0) is <= the source (0) - // - Leaving us with `node_starts` of `[0]` - // - Iterate to `(2, y)` at index 1: - // - Push one entry because `node_starts.len()` (1) is <= the source (2) - // - Push one entry because `node_starts.len()` (2) is <= the source (2) - // - Leaving us with `node_starts` of `[0, 1, 1]` - // - Loop terminates - while node_starts.len() <= source.index() { - node_starts.push(index); - } - } + let nodes_cap = match BR { + // +1 for special entry at the end, pointing one past the end of `edge_targets` + false => num_nodes + 1, + // *2 for back references + true => (num_nodes * 2) + 1, + }; - // Pad out the `node_starts` array so that it has `num_nodes + - // 1` entries. Continuing our example above, if `num_nodes` is - // be `3`, we would push one more index: `[0, 1, 1, 2]`. - // - // Interpretation of that vector: - // - // [0, 1, 1, 2] - // ---- range for N=2 - // ---- range for N=1 - // ---- range for N=0 - while node_starts.len() <= num_nodes { - node_starts.push(edge_targets.len()); - } + let edges_cap = match BR { + false => num_edges, + // *2 for back references + true => num_edges * 2, + }; - assert_eq!(node_starts.len(), num_nodes + 1); + let mut node_starts = IndexVec::with_capacity(nodes_cap); + let mut edge_targets = Vec::with_capacity(edges_cap); + + // Sort the edges by the source -- this is important. + edge_pairs.sort(); + + // Fill forward references + create_index( + num_nodes, + &mut edge_pairs.iter().map(|&(src, _)| src), + &mut edge_pairs.iter().map(|&(_, tgt)| tgt), + &mut edge_targets, + &mut node_starts, + ); + + // Fill back references + if BR { + // Pop the special "last" entry, it will be replaced by first back ref + node_starts.pop(); + + // Re-sort the edges so that they are sorted by target + edge_pairs.sort_by_key(|&(src, tgt)| (tgt, src)); + + create_index( + // Back essentially double the number of nodes + num_nodes * 2, + // NB: the source/target are switched here too + // NB: we double the key index, so that we can later use *2 to get the back references + &mut edge_pairs.iter().map(|&(_, tgt)| N::new(tgt.index() + num_nodes)), + &mut edge_pairs.iter().map(|&(src, _)| src), + &mut edge_targets, + &mut node_starts, + ); + } Self { node_starts, edge_targets } } /// Gets the successors for `source` as a slice. pub fn successors(&self, source: N) -> &[N] { + assert!(source.index() < self.num_nodes()); + let start_index = self.node_starts[source]; let end_index = self.node_starts[source.plus(1)]; &self.edge_targets[start_index..end_index] } } -impl DirectedGraph for VecGraph { - type Node = N; +impl VecGraph { + /// Gets the predecessors for `target` as a slice. + pub fn predecessors(&self, target: N) -> &[N] { + assert!(target.index() < self.num_nodes()); + + let target = N::new(target.index() + self.num_nodes()); + + let start_index = self.node_starts[target]; + let end_index = self.node_starts[target.plus(1)]; + &self.edge_targets[start_index..end_index] + } +} + +/// Creates/initializes the index for the [`VecGraph`]. A helper for [`VecGraph::new`]. +/// +/// - `num_nodes` is the target number of nodes in the graph +/// - `sorted_edge_sources` are the edge sources, sorted +/// - `associated_edge_targets` are the edge *targets* in the same order as sources +/// - `edge_targets` is the vec of targets to be extended +/// - `node_starts` is the index to be filled +fn create_index( + num_nodes: usize, + sorted_edge_sources: &mut dyn Iterator, + associated_edge_targets: &mut dyn Iterator, + edge_targets: &mut Vec, + node_starts: &mut IndexVec, +) { + let offset = edge_targets.len(); + + // Store the *target* of each edge into `edge_targets`. + edge_targets.extend(associated_edge_targets); + + // Create the *edge starts* array. We are iterating over the + // (sorted) edge pairs. We maintain the invariant that the + // length of the `node_starts` array is enough to store the + // current source node -- so when we see that the source node + // for an edge is greater than the current length, we grow the + // edge-starts array by just enough. + for (index, source) in sorted_edge_sources.enumerate() { + // If we have a list like `[(0, x), (2, y)]`: + // + // - Start out with `node_starts` of `[]` + // - Iterate to `(0, x)` at index 0: + // - Push one entry because `node_starts.len()` (0) is <= the source (0) + // - Leaving us with `node_starts` of `[0]` + // - Iterate to `(2, y)` at index 1: + // - Push one entry because `node_starts.len()` (1) is <= the source (2) + // - Push one entry because `node_starts.len()` (2) is <= the source (2) + // - Leaving us with `node_starts` of `[0, 1, 1]` + // - Loop terminates + while node_starts.len() <= source.index() { + node_starts.push(index + offset); + } + } + + // Pad out the `node_starts` array so that it has `num_nodes + + // 1` entries. Continuing our example above, if `num_nodes` is + // be `3`, we would push one more index: `[0, 1, 1, 2]`. + // + // Interpretation of that vector: + // + // [0, 1, 1, 2] + // ---- range for N=2 + // ---- range for N=1 + // ---- range for N=0 + while node_starts.len() <= num_nodes { + node_starts.push(edge_targets.len()); + } + + assert_eq!(node_starts.len(), num_nodes + 1); } -impl WithNumNodes for VecGraph { +impl DirectedGraph for VecGraph { + type Node = N; + fn num_nodes(&self) -> usize { - self.node_starts.len() - 1 + match BR { + false => self.node_starts.len() - 1, + // If back refs are enabled, half of the array is said back refs + true => (self.node_starts.len() - 1) / 2, + } } } -impl WithNumEdges for VecGraph { +impl NumEdges for VecGraph { fn num_edges(&self) -> usize { - self.edge_targets.len() + match BR { + false => self.edge_targets.len(), + // If back refs are enabled, half of the array is reversed edges for them + true => self.edge_targets.len() / 2, + } } } -impl<'graph, N: Idx> GraphSuccessors<'graph> for VecGraph { - type Item = N; - - type Iter = std::iter::Cloned>; +impl Successors for VecGraph { + fn successors(&self, node: N) -> impl Iterator { + self.successors(node).iter().cloned() + } } -impl WithSuccessors for VecGraph { - fn successors(&self, node: N) -> >::Iter { - self.successors(node).iter().cloned() +impl Predecessors for VecGraph { + fn predecessors(&self, node: Self::Node) -> impl Iterator { + self.predecessors(node).iter().cloned() } } diff --git a/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs b/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs index 7c866da60090f..a077d9d081364 100644 --- a/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs +++ b/compiler/rustc_data_structures/src/graph/vec_graph/tests.rs @@ -1,3 +1,5 @@ +use crate::graph; + use super::*; fn create_graph() -> VecGraph { @@ -16,10 +18,18 @@ fn create_graph() -> VecGraph { VecGraph::new(7, vec![(0, 1), (1, 2), (1, 3), (3, 4), (5, 1)]) } +fn create_graph_with_back_refs() -> VecGraph { + // Same as above + VecGraph::new(7, vec![(0, 1), (1, 2), (1, 3), (3, 4), (5, 1)]) +} + #[test] fn num_nodes() { let graph = create_graph(); assert_eq!(graph.num_nodes(), 7); + + let graph = create_graph_with_back_refs(); + assert_eq!(graph.num_nodes(), 7); } #[test] @@ -32,11 +42,36 @@ fn successors() { assert_eq!(graph.successors(4), &[] as &[usize]); assert_eq!(graph.successors(5), &[1]); assert_eq!(graph.successors(6), &[] as &[usize]); + + let graph = create_graph_with_back_refs(); + assert_eq!(graph.successors(0), &[1]); + assert_eq!(graph.successors(1), &[2, 3]); + assert_eq!(graph.successors(2), &[] as &[usize]); + assert_eq!(graph.successors(3), &[4]); + assert_eq!(graph.successors(4), &[] as &[usize]); + assert_eq!(graph.successors(5), &[1]); + assert_eq!(graph.successors(6), &[] as &[usize]); +} + +#[test] +fn predecessors() { + let graph = create_graph_with_back_refs(); + assert_eq!(graph.predecessors(0), &[]); + assert_eq!(graph.predecessors(1), &[0, 5]); + assert_eq!(graph.predecessors(2), &[1]); + assert_eq!(graph.predecessors(3), &[1]); + assert_eq!(graph.predecessors(4), &[3]); + assert_eq!(graph.predecessors(5), &[]); + assert_eq!(graph.predecessors(6), &[]); } #[test] fn dfs() { let graph = create_graph(); - let dfs: Vec<_> = graph.depth_first_search(0).collect(); + let dfs: Vec<_> = graph::depth_first_search(&graph, 0).collect(); + assert_eq!(dfs, vec![0, 1, 3, 4, 2]); + + let graph = create_graph_with_back_refs(); + let dfs: Vec<_> = graph::depth_first_search(&graph, 0).collect(); assert_eq!(dfs, vec![0, 1, 3, 4, 2]); } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index b82a9a909e6d0..cf54e700e2b98 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -20,12 +20,12 @@ #![feature(cfg_match)] #![feature(core_intrinsics)] #![feature(extend_one)] -#![feature(generic_nonzero)] #![feature(hash_raw_entry)] #![feature(hasher_prefixfree_extras)] #![feature(lazy_cell)] #![feature(lint_reasons)] #![feature(macro_metavar_expr)] +#![feature(map_try_insert)] #![feature(maybe_uninit_uninit_array)] #![feature(min_specialization)] #![feature(negative_impls)] @@ -42,8 +42,6 @@ #[macro_use] extern crate tracing; -#[macro_use] -extern crate rustc_macros; use std::fmt; @@ -147,9 +145,9 @@ pub fn make_display(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl Printer { f } } -// See comments in compiler/rustc_middle/src/tests.rs +// See comment in compiler/rustc_middle/src/tests.rs and issue #27438. #[doc(hidden)] -pub fn __noop_fix_for_27438() {} +pub fn __noop_fix_for_windows_dllimport_issue() {} #[macro_export] macro_rules! external_bitflags_debug { diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index 4a0ed87f77c84..4c9acfe0f7155 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_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index 1436628139fde..21d7c91ec4826 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -1,4 +1,5 @@ use crate::stable_hasher::{HashStable, StableHasher, StableOrd}; +use rustc_macros::{Decodable_Generic, Encodable_Generic}; use std::borrow::Borrow; use std::fmt::Debug; use std::mem; diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 15691804a94b2..8418b4bbd4702 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -684,26 +684,11 @@ where impl_stable_traits_for_trivial_type!(::std::path::Path); impl_stable_traits_for_trivial_type!(::std::path::PathBuf); -impl HashStable for ::std::collections::HashMap -where - K: ToStableHashKey + Eq, - V: HashStable, - R: BuildHasher, -{ - #[inline] - fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { - stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, (key, value)| { - let key = key.to_stable_hash_key(hcx); - key.hash_stable(hcx, hasher); - value.hash_stable(hcx, hasher); - }); - } -} - -// It is not safe to implement HashStable for HashSet or any other collection type +// It is not safe to implement HashStable for HashSet, HashMap or any other collection type // with unstable but observable iteration order. // See https://github.com/rust-lang/compiler-team/issues/533 for further information. impl !HashStable for std::collections::HashSet {} +impl !HashStable for std::collections::HashMap {} impl HashStable for ::std::collections::BTreeMap where @@ -730,35 +715,6 @@ where } } -fn stable_hash_reduce( - hcx: &mut HCX, - hasher: &mut StableHasher, - mut collection: C, - length: usize, - hash_function: F, -) where - C: Iterator, - F: Fn(&mut StableHasher, &mut HCX, I), -{ - length.hash_stable(hcx, hasher); - - match length { - 1 => { - hash_function(hasher, hcx, collection.next().unwrap()); - } - _ => { - let hash = collection - .map(|value| { - let mut hasher = StableHasher::new(); - hash_function(&mut hasher, hcx, value); - hasher.finish::() - }) - .reduce(|accum, value| accum.wrapping_add(value)); - hash.hash_stable(hcx, hasher); - } - } -} - /// Controls what data we do or do not hash. /// Whenever a `HashStable` implementation caches its /// result, it needs to include `HashingControls` as part diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index 1cfc9fecd47d7..38629ea9801d9 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -6,9 +6,9 @@ //! compiled from distinct sources. use crate::fingerprint::Fingerprint; -use std::fmt; - use crate::stable_hasher; +use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use std::fmt; #[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable_Generic, Decodable_Generic, Hash)] pub struct Svh { diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 32202ac3eded1..ecb85db33f70d 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -20,6 +20,7 @@ //! | ----------------------- | ------------------- | ------------------------------- | //! | `Lrc` | `rc::Rc` | `sync::Arc` | //! |` Weak` | `rc::Weak` | `sync::Weak` | +//! | `LRef<'a, T>` [^2] | `&'a mut T` | `&'a T` | //! | | | | //! | `AtomicBool` | `Cell` | `atomic::AtomicBool` | //! | `AtomicU32` | `Cell` | `atomic::AtomicU32` | @@ -38,13 +39,14 @@ //! of a `RefCell`. This is appropriate when interior mutability is not //! required. //! -//! [^2] `MTLockRef` is a typedef. +//! [^2] `MTRef`, `MTLockRef` are type aliases. pub use crate::marker::*; use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; mod lock; +#[doc(no_inline)] pub use lock::{Lock, LockGuard, Mode}; mod worker_local; @@ -198,17 +200,22 @@ cfg_match! { pub use std::rc::Rc as Lrc; pub use std::rc::Weak as Weak; + #[doc(no_inline)] pub use std::cell::Ref as ReadGuard; + #[doc(no_inline)] pub use std::cell::Ref as MappedReadGuard; + #[doc(no_inline)] pub use std::cell::RefMut as WriteGuard; + #[doc(no_inline)] pub use std::cell::RefMut as MappedWriteGuard; + #[doc(no_inline)] pub use std::cell::RefMut as MappedLockGuard; pub use std::cell::OnceCell as OnceLock; use std::cell::RefCell as InnerRwLock; - pub type MTLockRef<'a, T> = &'a mut MTLock; + pub type LRef<'a, T> = &'a mut T; #[derive(Debug, Default)] pub struct MTLock(T); @@ -274,7 +281,7 @@ cfg_match! { pub use std::sync::Arc as Lrc; pub use std::sync::Weak as Weak; - pub type MTLockRef<'a, T> = &'a MTLock; + pub type LRef<'a, T> = &'a T; #[derive(Debug, Default)] pub struct MTLock(Lock); @@ -314,6 +321,8 @@ cfg_match! { } } +pub type MTLockRef<'a, T> = LRef<'a, MTLock>; + #[derive(Default)] #[cfg_attr(parallel_compiler, repr(align(64)))] pub struct CacheAligned(pub T); diff --git a/compiler/rustc_data_structures/src/sync/lock.rs b/compiler/rustc_data_structures/src/sync/lock.rs index 040a8aa6b6380..756984642c742 100644 --- a/compiler/rustc_data_structures/src/sync/lock.rs +++ b/compiler/rustc_data_structures/src/sync/lock.rs @@ -189,6 +189,7 @@ mod no_sync { use super::Mode; use std::cell::RefCell; + #[doc(no_inline)] pub use std::cell::RefMut as LockGuard; pub struct Lock(RefCell); diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs index ff4208def319d..8b9e834b60b0d 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs @@ -243,6 +243,7 @@ where T: Tag, { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn eq(&self, other: &Self) -> bool { self.packed == other.packed } diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index a99e2062039bf..1ccd22a56c959 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -3,6 +3,8 @@ //! as required by the query system. use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use std::collections::hash_map::OccupiedError; use std::{ borrow::{Borrow, BorrowMut}, collections::hash_map::Entry, @@ -468,6 +470,11 @@ impl UnordMap { self.inner.insert(k, v) } + #[inline] + pub fn try_insert(&mut self, k: K, v: V) -> Result<&mut V, OccupiedError<'_, K, V>> { + self.inner.try_insert(k, v) + } + #[inline] pub fn contains_key(&self, k: &Q) -> bool where diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index fcc0afd3488b0..a8bba3afb7e70 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -65,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/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 1b69a6e2fbecc..5b39248302e7a 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -1,10 +1,7 @@ driver_impl_ice = the compiler unexpectedly panicked. this is a bug. driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly -driver_impl_ice_bug_report_outdated = - it seems that this compiler `{$version}` is outdated, a newer nightly should have been released in the mean time - .update = please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists - .url = if the problem still persists, we would appreciate a bug report: {$bug_report_url} +driver_impl_ice_bug_report_update_note = please make sure that you have updated to the latest nightly driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden driver_impl_ice_flags = compiler flags: {$flags} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 938f9f0beaacd..b2d38a00f0b5f 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -14,11 +14,9 @@ #![feature(panic_update_hook)] #![feature(result_flattening)] -#[macro_use] -extern crate tracing; - use rustc_ast as ast; use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults}; +use rustc_const_eval::CTRL_C_RECEIVED; use rustc_data_structures::profiling::{ get_resident_set_size, print_time_passes_entry, TimePassesFormat, }; @@ -45,7 +43,6 @@ use rustc_span::symbol::sym; use rustc_span::FileName; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTriple}; - use std::cmp::max; use std::collections::BTreeMap; use std::env; @@ -60,7 +57,8 @@ use std::str; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, OnceLock}; use std::time::{Instant, SystemTime}; -use time::{Date, OffsetDateTime, Time}; +use time::OffsetDateTime; +use tracing::trace; #[allow(unused_macros)] macro do_not_use_print($($t:tt)*) { @@ -890,7 +888,7 @@ pub fn version_at_macro_invocation( let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); let opts = config::Options::default(); let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone()); - let target = config::build_target_config(early_dcx, &opts, None, &sysroot); + let target = config::build_target_config(early_dcx, &opts, &sysroot); get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_version(); } @@ -970,7 +968,7 @@ Available lint options: let lint_store = unerased_lint_store(sess); let (loaded, builtin): (Vec<_>, _) = - lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_loaded); + lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_externally_loaded); let loaded = sort_lints(sess, loaded); let builtin = sort_lints(sess, builtin); @@ -1100,7 +1098,7 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> let opts = config::Options::default(); let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone()); - let target = config::build_target_config(early_dcx, &opts, None, &sysroot); + let target = config::build_target_config(early_dcx, &opts, &sysroot); get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_passes(); return true; @@ -1384,9 +1382,6 @@ pub fn install_ice_hook( using_internal_features } -const DATE_FORMAT: &[time::format_description::FormatItem<'static>] = - &time::macros::format_description!("[year]-[month]-[day]"); - /// Prints the ICE message, including query stack, but without backtrace. /// /// The message will point the user at `bug_report_url` to report the ICE. @@ -1415,33 +1410,14 @@ fn report_ice( dcx.emit_err(session_diagnostics::Ice); } - use time::ext::NumericalDuration; - - // Try to hint user to update nightly if applicable when reporting an ICE. - // Attempt to calculate when current version was released, and add 12 hours - // as buffer. If the current version's release timestamp is older than - // the system's current time + 24 hours + 12 hours buffer if we're on - // nightly. - if let Some("nightly") = option_env!("CFG_RELEASE_CHANNEL") - && let Some(version) = option_env!("CFG_VERSION") - && let Some(ver_date_str) = option_env!("CFG_VER_DATE") - && let Ok(ver_date) = Date::parse(&ver_date_str, DATE_FORMAT) - && let ver_datetime = OffsetDateTime::new_utc(ver_date, Time::MIDNIGHT) - && let system_datetime = OffsetDateTime::from(SystemTime::now()) - && system_datetime.checked_sub(36.hours()).is_some_and(|d| d > ver_datetime) - && !using_internal_features.load(std::sync::atomic::Ordering::Relaxed) - { - dcx.emit_note(session_diagnostics::IceBugReportOutdated { - version, - bug_report_url, - note_update: (), - note_url: (), - }); + if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) { + dcx.emit_note(session_diagnostics::IceBugReportInternalFeature); } else { - if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) { - dcx.emit_note(session_diagnostics::IceBugReportInternalFeature); - } else { - dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url }); + dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url }); + + // Only emit update nightly hint for users on nightly builds. + if rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() { + dcx.emit_note(session_diagnostics::UpdateNightlyNote); } } @@ -1518,6 +1494,23 @@ 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 + // whatever reason we don't get to that check or the cleanup we do upon finding that + // this bool has become true takes a long time, the exit here will promptly exit the + // process on the second Ctrl-C. + if CTRL_C_RECEIVED.swap(true, Ordering::Relaxed) { + std::process::exit(1); + } + }) + .expect("Unable to install ctrlc handler"); +} + pub fn main() -> ! { let start_time = Instant::now(); let start_rss = get_resident_set_size(); @@ -1528,6 +1521,8 @@ pub fn main() -> ! { signal_handler::install(); let mut callbacks = TimePassesCallbacks::default(); let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); + install_ctrlc_handler(); + let exit_code = catch_with_exit_code(|| { RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks) .set_using_internal_features(using_internal_features) diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index c9bbe45b21276..fe426b8111cc3 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -13,9 +13,9 @@ use rustc_session::Session; use rustc_smir::rustc_internal::pretty::write_smir_pretty; use rustc_span::symbol::Ident; use rustc_span::FileName; - use std::cell::Cell; use std::fmt::Write; +use tracing::debug; pub use self::PpMode::*; pub use self::PpSourceMode::*; @@ -24,20 +24,6 @@ struct AstNoAnn; impl pprust_ast::PpAnn for AstNoAnn {} -struct HirNoAnn<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> pprust_hir::PpAnn for HirNoAnn<'tcx> { - fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { - pprust_hir::PpAnn::nested( - &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>), - state, - nested, - ) - } -} - struct AstIdentifiedAnn; impl pprust_ast::PpAnn for AstIdentifiedAnn { @@ -300,10 +286,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { ) }; match s { - PpHirMode::Normal => { - let annotation = HirNoAnn { tcx }; - f(&annotation) - } + PpHirMode::Normal => f(&tcx), PpHirMode::Identified => { let annotation = HirIdentifiedAnn { tcx }; f(&annotation) @@ -336,7 +319,8 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { ThirTree => { let tcx = ex.tcx(); let mut out = String::new(); - if rustc_hir_analysis::check_crate(tcx).is_err() { + rustc_hir_analysis::check_crate(tcx); + if tcx.dcx().has_errors().is_some() { FatalError.raise(); } debug!("pretty printing THIR tree"); @@ -348,7 +332,8 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { ThirFlat => { let tcx = ex.tcx(); let mut out = String::new(); - if rustc_hir_analysis::check_crate(tcx).is_err() { + rustc_hir_analysis::check_crate(tcx); + if tcx.dcx().has_errors().is_some() { FatalError.raise(); } debug!("pretty printing THIR flat"); diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 62d0da62d2a79..1a9683e840afd 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -43,19 +43,12 @@ pub(crate) struct IceBugReport<'a> { } #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_internal_feature)] -pub(crate) struct IceBugReportInternalFeature; +#[diag(driver_impl_ice_bug_report_update_note)] +pub(crate) struct UpdateNightlyNote; #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_outdated)] -pub(crate) struct IceBugReportOutdated<'a> { - pub version: &'a str, - pub bug_report_url: &'a str, - #[note(driver_impl_update)] - pub note_update: (), - #[note(driver_impl_url)] - pub note_url: (), -} +#[diag(driver_impl_ice_bug_report_internal_feature)] +pub(crate) struct IceBugReportInternalFeature; #[derive(Diagnostic)] #[diag(driver_impl_ice_version)] diff --git a/compiler/rustc_driver_impl/src/signal_handler.rs b/compiler/rustc_driver_impl/src/signal_handler.rs index deca108222150..441219eec90dc 100644 --- a/compiler/rustc_driver_impl/src/signal_handler.rs +++ b/compiler/rustc_driver_impl/src/signal_handler.rs @@ -1,6 +1,7 @@ //! Signal handler for rustc //! Primarily used to extract a backtrace from stack overflow +use rustc_interface::util::{DEFAULT_STACK_SIZE, STACK_SIZE}; use std::alloc::{alloc, Layout}; use std::{fmt, mem, ptr}; @@ -100,7 +101,10 @@ extern "C" fn print_stack_trace(_: libc::c_int) { written += 1; } raw_errln!("note: we would appreciate a report at https://github.com/rust-lang/rust"); - written += 1; + // get the current stack size WITHOUT blocking and double it + let new_size = STACK_SIZE.get().copied().unwrap_or(DEFAULT_STACK_SIZE) * 2; + raw_errln!("help: you can increase rustc's stack size by setting RUST_MIN_STACK={new_size}"); + written += 2; if written > 24 { // We probably just scrolled the earlier "we got SIGSEGV" message off the terminal raw_errln!("note: backtrace dumped due to SIGSEGV! resuming signal"); diff --git a/compiler/rustc_error_codes/src/error_codes/E0384.md b/compiler/rustc_error_codes/src/error_codes/E0384.md index e21fac0797cb9..6680dbed3dd63 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_error_codes/src/error_codes/E0522.md b/compiler/rustc_error_codes/src/error_codes/E0522.md index 83272314a8708..b0dd5a775ac14 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0522.md +++ b/compiler/rustc_error_codes/src/error_codes/E0522.md @@ -6,7 +6,7 @@ Erroneous code example: #![feature(lang_items)] #[lang = "cookie"] -fn cookie() -> ! { // error: definition of an unknown language item: `cookie` +fn cookie() -> ! { // error: definition of an unknown lang item: `cookie` loop {} } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0626.md b/compiler/rustc_error_codes/src/error_codes/E0626.md index e2534415d8307..28d543350ffd8 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0626.md +++ b/compiler/rustc_error_codes/src/error_codes/E0626.md @@ -4,10 +4,10 @@ yield point. Erroneous code example: ```compile_fail,E0626 -# #![feature(coroutines, coroutine_trait, pin)] +# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] # use std::ops::Coroutine; # use std::pin::Pin; -let mut b = || { +let mut b = #[coroutine] || { let a = &String::new(); // <-- This borrow... yield (); // ...is still in scope here, when the yield occurs. println!("{}", a); @@ -23,10 +23,10 @@ resolve the previous example by removing the borrow and just storing the integer by value: ``` -# #![feature(coroutines, coroutine_trait, pin)] +# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] # use std::ops::Coroutine; # use std::pin::Pin; -let mut b = || { +let mut b = #[coroutine] || { let a = 3; yield (); println!("{}", a); @@ -41,10 +41,10 @@ in those cases, something like the `Rc` or `Arc` types may be useful. This error also frequently arises with iteration: ```compile_fail,E0626 -# #![feature(coroutines, coroutine_trait, pin)] +# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] # use std::ops::Coroutine; # use std::pin::Pin; -let mut b = || { +let mut b = #[coroutine] || { let v = vec![1,2,3]; for &x in &v { // <-- borrow of `v` is still in scope... yield x; // ...when this yield occurs. @@ -57,10 +57,10 @@ Such cases can sometimes be resolved by iterating "by value" (or using `into_iter()`) to avoid borrowing: ``` -# #![feature(coroutines, coroutine_trait, pin)] +# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] # use std::ops::Coroutine; # use std::pin::Pin; -let mut b = || { +let mut b = #[coroutine] || { let v = vec![1,2,3]; for x in v { // <-- Take ownership of the values instead! yield x; // <-- Now yield is OK. @@ -72,10 +72,10 @@ Pin::new(&mut b).resume(()); If taking ownership is not an option, using indices can work too: ``` -# #![feature(coroutines, coroutine_trait, pin)] +# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] # use std::ops::Coroutine; # use std::pin::Pin; -let mut b = || { +let mut b = #[coroutine] || { let v = vec![1,2,3]; let len = v.len(); // (*) for i in 0..len { diff --git a/compiler/rustc_error_codes/src/error_codes/E0627.md b/compiler/rustc_error_codes/src/error_codes/E0627.md index 5d366f78fc575..da2e2d355a160 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0627.md +++ b/compiler/rustc_error_codes/src/error_codes/E0627.md @@ -3,7 +3,7 @@ A yield expression was used outside of the coroutine literal. Erroneous code example: ```compile_fail,E0627 -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] fn fake_coroutine() -> &'static str { yield 1; @@ -19,10 +19,10 @@ The error occurs because keyword `yield` can only be used inside the coroutine literal. This can be fixed by constructing the coroutine correctly. ``` -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] fn main() { - let mut coroutine = || { + let mut coroutine = #[coroutine] || { yield 1; return "foo" }; diff --git a/compiler/rustc_error_codes/src/error_codes/E0628.md b/compiler/rustc_error_codes/src/error_codes/E0628.md index ce19bcd56cc78..d0d387cf6c7ef 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0628.md +++ b/compiler/rustc_error_codes/src/error_codes/E0628.md @@ -3,10 +3,10 @@ More than one parameter was used for a coroutine. Erroneous code example: ```compile_fail,E0628 -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] fn main() { - let coroutine = |a: i32, b: i32| { + let coroutine = #[coroutine] |a: i32, b: i32| { // error: too many parameters for a coroutine // Allowed only 0 or 1 parameter yield a; @@ -20,10 +20,10 @@ at most 1 parameter for the coroutine. For example, we might resolve the previous example by passing only one parameter. ``` -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] fn main() { - let coroutine = |a: i32| { + let coroutine = #[coroutine] |a: i32| { yield a; }; } diff --git a/compiler/rustc_error_codes/src/error_codes/E0637.md b/compiler/rustc_error_codes/src/error_codes/E0637.md index 62d5565df2798..9c2a53f51cf29 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0637.md +++ b/compiler/rustc_error_codes/src/error_codes/E0637.md @@ -1,5 +1,5 @@ `'_` lifetime name or `&T` without an explicit lifetime name has been used -on illegal place. +in an illegal place. Erroneous code example: @@ -13,7 +13,14 @@ fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { } } -fn and_without_explicit_lifetime() +fn without_explicit_lifetime() +where + T: Iterator, + //^ `&` without an explicit lifetime name +{ +} + +fn without_hrtb() where T: Into<&u32>, //^ `&` without an explicit lifetime name @@ -40,9 +47,15 @@ fn underscore_lifetime<'a>(str1: &'a str, str2: &'a str) -> &'a str { } } -fn and_without_explicit_lifetime<'foo, T>() +fn without_explicit_lifetime<'a, T>() +where + T: Iterator, +{ +} + +fn without_hrtb() where - T: Into<&'foo u32>, + T: for<'foo> Into<&'foo u32>, { } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0699.md b/compiler/rustc_error_codes/src/error_codes/E0699.md index 454d2507e5e2e..1094ebf4b8fc7 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0699.md +++ b/compiler/rustc_error_codes/src/error_codes/E0699.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + A method was called on a raw pointer whose inner type wasn't completely known. Erroneous code example: -```compile_fail,edition2018,E0699 +```compile_fail,edition2018 # #![deny(warnings)] # fn main() { let foo = &1; diff --git a/compiler/rustc_error_codes/src/error_codes/E0719.md b/compiler/rustc_error_codes/src/error_codes/E0719.md index 057a0b1645c57..cd981db1058a6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0719.md +++ b/compiler/rustc_error_codes/src/error_codes/E0719.md @@ -3,8 +3,6 @@ An associated type value was specified more than once. Erroneous code example: ```compile_fail,E0719 -#![feature(associated_type_bounds)] - trait FooTrait {} trait BarTrait {} @@ -19,8 +17,6 @@ specify the associated type with the new trait. Corrected example: ``` -#![feature(associated_type_bounds)] - trait FooTrait {} trait BarTrait {} trait FooBarTrait: FooTrait + BarTrait {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0727.md b/compiler/rustc_error_codes/src/error_codes/E0727.md index fde35885c9208..7754186508f11 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0727.md +++ b/compiler/rustc_error_codes/src/error_codes/E0727.md @@ -3,10 +3,10 @@ A `yield` clause was used in an `async` context. Erroneous code example: ```compile_fail,E0727,edition2018 -#![feature(coroutines)] +#![feature(coroutines, stmt_expr_attributes)] fn main() { - let coroutine = || { + let coroutine = #[coroutine] || { async { yield; } @@ -20,10 +20,10 @@ which is not yet supported. To fix this error, you have to move `yield` out of the `async` block: ```edition2018 -#![feature(coroutines)] +#![feature(coroutines, stmt_expr_attributes)] fn main() { - let coroutine = || { + let coroutine = #[coroutine] || { yield; }; } diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index da688e385aa09..f4a33a05c1b36 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -441,7 +441,7 @@ E0695: 0695, E0696: 0696, E0697: 0697, E0698: 0698, -E0699: 0699, +E0699: 0699, // REMOVED: merged into generic inference var error E0700: 0700, E0701: 0701, E0703: 0703, diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5d345e788e94e..1610135a0efad 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -7,6 +7,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::fluent_value_from_str_list_sep_by_and; use rustc_error_messages::FluentValue; use rustc_lint_defs::{Applicability, LintExpectationId}; +use rustc_macros::{Decodable, Encodable}; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; @@ -17,6 +18,7 @@ use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::panic; use std::thread::panicking; +use tracing::debug; /// Error type for `DiagInner`'s `suggestions` field, indicating that /// `.disable_suggestions()` was called on the `DiagInner`. @@ -177,7 +179,7 @@ where { /// Add a subdiagnostic to an existing diagnostic. fn add_to_diag(self, diag: &mut Diag<'_, G>) { - self.add_to_diag_with(diag, |_, m| m); + self.add_to_diag_with(diag, &|_, m| m); } /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used @@ -185,7 +187,7 @@ where fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - f: F, + f: &F, ); } @@ -730,7 +732,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } } #[rustc_lint_diagnostics] - fn highlighted_note(&mut self, msg: Vec) -> &mut Self { + pub fn highlighted_note(&mut self, msg: Vec) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } @@ -1197,7 +1199,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { dcx: &crate::DiagCtxt, subdiagnostic: impl Subdiagnostic, ) -> &mut Self { - subdiagnostic.add_to_diag_with(self, |diag, msg| { + subdiagnostic.add_to_diag_with(self, &|diag, msg| { let args = diag.args.iter(); let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); dcx.eagerly_translate(msg, args) diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index f90190797aee9..b3a1e29f8e2cb 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -7,6 +7,7 @@ use crate::{ use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_hir as hir; +use rustc_macros::Subdiagnostic; use rustc_span::edition::Edition; use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; use rustc_span::Span; @@ -46,6 +47,7 @@ impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { } } +#[macro_export] macro_rules! into_diag_arg_using_display { ($( $ty:ty ),+ $(,)?) => { $( @@ -226,6 +228,36 @@ impl IntoDiagArg for rustc_lint_defs::Level { } } +impl IntoDiagArg for hir::def::Res { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(Cow::Borrowed(self.descr())) + } +} + +impl IntoDiagArg for DiagLocation { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(Cow::from(self.to_string())) + } +} + +impl IntoDiagArg for Backtrace { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(Cow::from(self.to_string())) + } +} + +impl IntoDiagArg for Level { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(Cow::from(self.to_string())) + } +} + +impl IntoDiagArg for type_ir::ClosureKind { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(self.as_str().into()) + } +} + #[derive(Clone)] pub struct DiagSymbolList(Vec); @@ -243,12 +275,6 @@ impl IntoDiagArg for DiagSymbolList { } } -impl IntoDiagArg for hir::def::Res { - fn into_diag_arg(self) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.descr())) - } -} - impl Diagnostic<'_, G> for TargetDataLayoutErrors<'_> { fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { match self { @@ -301,7 +327,7 @@ impl Subdiagnostic for SingleLabelManySpans { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _: F, + _: &F, ) { diag.span_labels(self.spans, self.label); } @@ -315,24 +341,6 @@ pub struct ExpectedLifetimeParameter { pub count: usize, } -impl IntoDiagArg for DiagLocation { - fn into_diag_arg(self) -> DiagArgValue { - DiagArgValue::Str(Cow::from(self.to_string())) - } -} - -impl IntoDiagArg for Backtrace { - fn into_diag_arg(self) -> DiagArgValue { - DiagArgValue::Str(Cow::from(self.to_string())) - } -} - -impl IntoDiagArg for Level { - fn into_diag_arg(self) -> DiagArgValue { - DiagArgValue::Str(Cow::from(self.to_string())) - } -} - #[derive(Subdiagnostic)] #[suggestion(errors_indicate_anonymous_lifetime, code = "{suggestion}", style = "verbose")] pub struct IndicateAnonymousLifetime { @@ -342,8 +350,10 @@ pub struct IndicateAnonymousLifetime { pub suggestion: String, } -impl IntoDiagArg for type_ir::ClosureKind { - fn into_diag_arg(self) -> DiagArgValue { - DiagArgValue::Str(self.as_str().into()) - } +#[derive(Subdiagnostic)] +pub struct ElidedLifetimeInPathSubdiag { + #[subdiagnostic] + pub expected: ExpectedLifetimeParameter, + #[subdiagnostic] + pub indicate: Option, } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 4f033e3fefa0f..5d4d2555100ed 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -35,6 +35,7 @@ use std::iter; use std::path::Path; use termcolor::{Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream}; use termcolor::{Color, WriteColor}; +use tracing::{debug, instrument, trace, warn}; /// Default column width, used in tests and when terminal dimensions cannot be determined. const DEFAULT_COLUMN_WIDTH: usize = 140; @@ -541,6 +542,7 @@ pub struct SilentEmitter { pub fallback_bundle: LazyFallbackBundle, pub fatal_dcx: DiagCtxt, pub fatal_note: Option, + pub emit_fatal_diagnostic: bool, } impl Translate for SilentEmitter { @@ -561,7 +563,7 @@ impl Emitter for SilentEmitter { } fn emit_diagnostic(&mut self, mut diag: DiagInner) { - if diag.level == Level::Fatal { + if self.emit_fatal_diagnostic && diag.level == Level::Fatal { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); } @@ -983,7 +985,7 @@ impl HumanEmitter { // 4 | } // | for pos in 0..=line_len { - draw_col_separator(buffer, line_offset + pos + 1, width_offset - 2); + draw_col_separator_no_space(buffer, line_offset + pos + 1, width_offset - 2); } // Write the horizontal lines for multiline annotations @@ -1512,7 +1514,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()) @@ -1552,7 +1556,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); @@ -1638,6 +1645,27 @@ impl HumanEmitter { *style, ); } + if let Some(line) = annotated_file.lines.get(line_idx) { + for ann in &line.annotations { + if let AnnotationType::MultilineStart(pos) = ann.annotation_type + { + // In the case where we have elided the entire start of the + // multispan because those lines were empty, we still need + // to draw the `|`s across the `...`. + draw_multiline_line( + &mut buffer, + last_buffer_line_num, + width_offset, + pos, + if ann.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }, + ); + } + } + } } else if line_idx_delta == 2 { let unannotated_line = annotated_file .file @@ -1665,6 +1693,24 @@ impl HumanEmitter { *style, ); } + if let Some(line) = annotated_file.lines.get(line_idx) { + for ann in &line.annotations { + if let AnnotationType::MultilineStart(pos) = ann.annotation_type + { + draw_multiline_line( + &mut buffer, + last_buffer_line_num, + width_offset, + pos, + if ann.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }, + ); + } + } + } } } @@ -1974,7 +2020,7 @@ impl HumanEmitter { let offset: isize = offsets .iter() .filter_map( - |(start, v)| if span_start_pos <= *start { None } else { Some(v) }, + |(start, v)| if span_start_pos < *start { None } else { Some(v) }, ) .sum(); let underline_start = (span_start_pos + start) as isize + offset; @@ -1983,7 +2029,7 @@ impl HumanEmitter { let padding: usize = max_line_num_len + 3; for p in underline_start..underline_end { if let DisplaySuggestion::Underline = show_code_change { - // If this is a replacement, underline with `^`, if this is an addition + // If this is a replacement, underline with `~`, if this is an addition // underline with `+`. buffer.putc( row_num, @@ -2215,13 +2261,23 @@ impl HumanEmitter { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); } [] => { - draw_col_separator(buffer, *row_num, max_line_num_len + 1); + draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1); } _ => { buffer.puts(*row_num, max_line_num_len + 1, "~ ", Style::Addition); } } - buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); + // LL | line_to_add + // ++^^^ + // | | + // | magic `3` + // `max_line_num_len` + buffer.puts( + *row_num, + max_line_num_len + 3, + &normalize_whitespace(line_to_add), + Style::NoStyle, + ); } else if let DisplaySuggestion::Add = show_code_change { buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); @@ -2417,7 +2473,15 @@ impl FileWithAnnotatedLines { // the beginning doesn't have an underline, but the current logic seems to be // working correctly. let middle = min(ann.line_start + 4, ann.line_end); - for line in ann.line_start + 1..middle { + // We'll show up to 4 lines past the beginning of the multispan start. + // We will *not* include the tail of lines that are only whitespace. + let until = (ann.line_start..middle) + .rev() + .filter_map(|line| file.get_line(line - 1).map(|s| (line + 1, s))) + .find(|(_, s)| !s.trim().is_empty()) + .map(|(line, _)| line) + .unwrap_or(ann.line_start); + for line in ann.line_start + 1..until { // Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`). add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 723f13dbe8d31..3b884ff864ab1 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -7,7 +7,6 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -16,7 +15,6 @@ #![feature(box_patterns)] #![feature(error_reporter)] #![feature(extract_if)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] @@ -27,12 +25,6 @@ #![feature(yeet_expr)] // tidy-alphabetical-end -#[macro_use] -extern crate rustc_macros; - -#[macro_use] -extern crate tracing; - extern crate self as rustc_errors; pub use codes::*; @@ -42,8 +34,8 @@ pub use diagnostic::{ SubdiagMessageOp, Subdiagnostic, }; pub use diagnostic_impls::{ - DiagArgFromDisplay, DiagSymbolList, ExpectedLifetimeParameter, IndicateAnonymousLifetime, - SingleLabelManySpans, + DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, + IndicateAnonymousLifetime, SingleLabelManySpans, }; pub use emitter::ColorConfig; pub use rustc_error_messages::{ @@ -66,6 +58,7 @@ use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_data_structures::AtomicRef; use rustc_lint_defs::LintExpectationId; +use rustc_macros::{Decodable, Encodable}; use rustc_span::source_map::SourceMap; use rustc_span::{Loc, Span, DUMMY_SP}; use std::backtrace::{Backtrace, BacktraceStatus}; @@ -78,6 +71,7 @@ use std::num::NonZero; use std::ops::DerefMut; use std::panic; use std::path::{Path, PathBuf}; +use tracing::debug; use Level::*; @@ -103,9 +97,9 @@ pub type PResult<'a, T> = Result>; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] @@ -504,7 +498,7 @@ struct DiagCtxtInner { } /// A key denoting where from a diagnostic was stashed. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum StashKey { ItemNoType, UnderscoreForArrayLengths, @@ -613,12 +607,18 @@ impl DiagCtxt { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } - pub fn make_silent(&mut self, fallback_bundle: LazyFallbackBundle, fatal_note: Option) { + pub fn make_silent( + &mut self, + fallback_bundle: LazyFallbackBundle, + fatal_note: Option, + emit_fatal_diagnostic: bool, + ) { self.wrap_emitter(|old_dcx| { Box::new(emitter::SilentEmitter { fallback_bundle, fatal_dcx: DiagCtxt { inner: Lock::new(old_dcx) }, fatal_note, + emit_fatal_diagnostic, }) }); } @@ -775,7 +775,7 @@ impl DiagCtxt { // We delay a bug here so that `-Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs` // can be used to create a backtrace at the stashing site insted of whenever the // diagnostic context is dropped and thus delayed bugs are emitted. - Error => Some(self.span_delayed_bug(span, "stashing {key:?}")), + Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))), DelayedBug => return self.inner.borrow_mut().emit_diagnostic(diag), ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow | Expect(_) => None, @@ -1907,27 +1907,24 @@ impl Level { } // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. -pub fn add_elided_lifetime_in_path_suggestion( +pub fn elided_lifetime_in_path_suggestion( source_map: &SourceMap, - diag: &mut Diag<'_, G>, n: usize, path_span: Span, incl_angl_brckt: bool, insertion_span: Span, -) { - diag.subdiagnostic(diag.dcx, ExpectedLifetimeParameter { span: path_span, count: n }); - if !source_map.is_span_accessible(insertion_span) { - // Do not try to suggest anything if generated by a proc-macro. - return; - } - let anon_lts = vec!["'_"; n].join(", "); - let suggestion = - if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") }; - - diag.subdiagnostic( - diag.dcx, - IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion }, - ); +) -> ElidedLifetimeInPathSubdiag { + let expected = ExpectedLifetimeParameter { span: path_span, count: n }; + // Do not try to suggest anything if generated by a proc-macro. + let indicate = source_map.is_span_accessible(insertion_span).then(|| { + let anon_lts = vec!["'_"; n].join(", "); + let suggestion = + if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") }; + + IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion } + }); + + ElidedLifetimeInPathSubdiag { expected, indicate } } pub fn report_ambiguity_error<'a, G: EmissionGuarantee>( @@ -1946,6 +1943,39 @@ pub fn report_ambiguity_error<'a, G: EmissionGuarantee>( } } +/// Grammatical tool for displaying messages to end users in a nice form. +/// +/// Returns "an" if the given string starts with a vowel, and "a" otherwise. +pub fn a_or_an(s: &str) -> &'static str { + let mut chars = s.chars(); + let Some(mut first_alpha_char) = chars.next() else { + return "a"; + }; + if first_alpha_char == '`' { + let Some(next) = chars.next() else { + return "a"; + }; + first_alpha_char = next; + } + if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) { + "an" + } else { + "a" + } +} + +/// Grammatical tool for displaying messages to end users in a nice form. +/// +/// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c" +pub fn display_list_with_comma_and(v: &[T]) -> String { + match v.len() { + 0 => "".to_string(), + 1 => v[0].to_string(), + 2 => format!("{} and {}", v[0], v[1]), + _ => format!("{}, {}", v[0], display_list_with_comma_and(&v[1..])), + } +} + #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum TerminalUrl { No, diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index b55f78538852a..d6119fb41d225 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -1,6 +1,7 @@ // Code for annotating snippets. use crate::{Level, Loc}; +use rustc_macros::{Decodable, Encodable}; #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] pub struct Line { diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index bf0026568ce23..445e9b4fd6e04 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -6,6 +6,7 @@ pub use rustc_error_messages::FluentArgs; use std::borrow::Cow; use std::env; use std::error::Report; +use tracing::{debug, trace}; /// Convert diagnostic arguments (a rustc internal type that exists to implement /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 1f0488130b27d..b7aae2af9ef97 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -30,8 +30,8 @@ expand_duplicate_matcher_binding = duplicate matcher binding .label = duplicate binding .label2 = previous binding -expand_expected_comma_in_list = - expected token: `,` +expand_expected_paren_or_brace = + expected `(` or `{"{"}`, found `{$token}` expand_explain_doc_comment_inner = inner doc comments expand to `#![doc = "..."]`, which is what this macro attempted to match @@ -113,9 +113,6 @@ expand_must_repeat_once = expand_not_a_meta_item = not a meta item -expand_only_one_argument = - {$name} takes 1 argument - expand_only_one_word = must only be one word @@ -143,9 +140,6 @@ expand_remove_node_not_supported = expand_resolve_relative_path = cannot resolve relative path in non-file source `{$path}` -expand_takes_no_arguments = - {$name} takes no arguments - expand_trace_macro = trace_macro expand_unsupported_key_value = diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 30ee02ea3c0aa..6fe74edbd70a8 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -5,27 +5,26 @@ use crate::module::DirOwnership; use rustc_ast::attr::MarkedAttrs; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Nonterminal}; +use rustc_ast::token::Nonterminal; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{self, Lrc}; -use rustc_errors::{Applicability, Diag, DiagCtxt, ErrorGuaranteed, PResult}; +use rustc_errors::{DiagCtxt, ErrorGuaranteed, PResult}; use rustc_feature::Features; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, RegisteredTools}; use rustc_parse::{parser, MACRO_ARGUMENTS}; use rustc_session::config::CollapseMacroDebuginfo; -use rustc_session::errors::report_lit_error; use rustc_session::{parse::ParseSess, Limit, Session}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, FileName, Span, DUMMY_SP}; +use rustc_span::{FileName, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::default::Default; use std::iter; @@ -789,55 +788,50 @@ impl SyntaxExtension { } } - fn collapse_debuginfo_by_name(sess: &Session, attr: &Attribute) -> CollapseMacroDebuginfo { - use crate::errors::CollapseMacroDebuginfoIllegal; - // #[collapse_debuginfo] without enum value (#[collapse_debuginfo(no/external/yes)]) - // considered as `yes` - attr.meta_item_list().map_or(CollapseMacroDebuginfo::Yes, |l| { - let [NestedMetaItem::MetaItem(item)] = &l[..] else { - sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: attr.span }); - return CollapseMacroDebuginfo::Unspecified; - }; - if !item.is_word() { - sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: item.span }); - CollapseMacroDebuginfo::Unspecified - } else { - match item.name_or_empty() { - sym::no => CollapseMacroDebuginfo::No, - sym::external => CollapseMacroDebuginfo::External, - sym::yes => CollapseMacroDebuginfo::Yes, - _ => { - sess.dcx().emit_err(CollapseMacroDebuginfoIllegal { span: item.span }); - CollapseMacroDebuginfo::Unspecified - } - } - } - }) + fn collapse_debuginfo_by_name(attr: &Attribute) -> Result { + let list = attr.meta_item_list(); + let Some([NestedMetaItem::MetaItem(item)]) = list.as_deref() else { + return Err(attr.span); + }; + if !item.is_word() { + return Err(item.span); + } + + match item.name_or_empty() { + sym::no => Ok(CollapseMacroDebuginfo::No), + sym::external => Ok(CollapseMacroDebuginfo::External), + sym::yes => Ok(CollapseMacroDebuginfo::Yes), + _ => Err(item.path.span), + } } /// if-ext - if macro from different crate (related to callsite code) /// | cmd \ attr | no | (unspecified) | external | yes | /// | no | no | no | no | no | - /// | (unspecified) | no | no | if-ext | yes | + /// | (unspecified) | no | if-ext | if-ext | yes | /// | external | no | if-ext | if-ext | yes | /// | yes | yes | yes | yes | yes | - fn get_collapse_debuginfo(sess: &Session, attrs: &[ast::Attribute], is_local: bool) -> bool { - let mut collapse_debuginfo_attr = attr::find_by_name(attrs, sym::collapse_debuginfo) - .map(|v| Self::collapse_debuginfo_by_name(sess, v)) - .unwrap_or(CollapseMacroDebuginfo::Unspecified); - if collapse_debuginfo_attr == CollapseMacroDebuginfo::Unspecified - && attr::contains_name(attrs, sym::rustc_builtin_macro) - { - collapse_debuginfo_attr = CollapseMacroDebuginfo::Yes; - } - - let flag = sess.opts.unstable_opts.collapse_macro_debuginfo; - let attr = collapse_debuginfo_attr; - let ext = !is_local; + fn get_collapse_debuginfo(sess: &Session, attrs: &[ast::Attribute], ext: bool) -> bool { + let flag = sess.opts.cg.collapse_macro_debuginfo; + let attr = attr::find_by_name(attrs, sym::collapse_debuginfo) + .and_then(|attr| { + Self::collapse_debuginfo_by_name(attr) + .map_err(|span| { + sess.dcx().emit_err(errors::CollapseMacroDebuginfoIllegal { span }) + }) + .ok() + }) + .unwrap_or_else(|| { + if attr::contains_name(attrs, sym::rustc_builtin_macro) { + CollapseMacroDebuginfo::Yes + } else { + CollapseMacroDebuginfo::Unspecified + } + }); #[rustfmt::skip] let collapse_table = [ [false, false, false, false], - [false, false, ext, true], + [false, ext, ext, true], [false, ext, ext, true], [true, true, true, true], ]; @@ -864,7 +858,7 @@ impl SyntaxExtension { let local_inner_macros = attr::find_by_name(attrs, sym::macro_export) .and_then(|macro_export| macro_export.meta_item_list()) .is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros)); - let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, is_local); + let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro) @@ -968,7 +962,12 @@ impl SyntaxExtension { /// Error type that denotes indeterminacy. pub struct Indeterminate; -pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option>, bool)>; +pub struct DeriveResolution { + pub path: ast::Path, + pub item: Annotatable, + pub exts: Option>, + pub is_const: bool, +} pub trait ResolverExpand { fn next_node_id(&mut self) -> NodeId; @@ -1011,11 +1010,11 @@ pub trait ResolverExpand { &mut self, expn_id: LocalExpnId, force: bool, - derive_paths: &dyn Fn() -> DeriveResolutions, + derive_paths: &dyn Fn() -> Vec, ) -> Result<(), Indeterminate>; /// Take resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId` /// back from resolver. - fn take_derive_resolutions(&mut self, expn_id: LocalExpnId) -> Option; + fn take_derive_resolutions(&mut self, expn_id: LocalExpnId) -> Option>; /// Path resolution logic for `#[cfg_accessible(path)]`. fn cfg_accessible( &mut self, @@ -1252,186 +1251,23 @@ pub fn resolve_path(sess: &Session, path: impl Into, span: Span) -> PRe // after macro expansion (that is, they are unhygienic). if !path.is_absolute() { let callsite = span.source_callsite(); - let mut result = match sess.source_map().span_to_filename(callsite) { - FileName::Real(name) => name - .into_local_path() - .expect("attempting to resolve a file path in an external file"), - FileName::DocTest(path, _) => path, - other => { - return Err(sess.dcx().create_err(errors::ResolveRelativePath { - span, - path: sess.source_map().filename_for_diagnostics(&other).to_string(), - })); - } + let source_map = sess.source_map(); + let Some(mut base_path) = source_map.span_to_filename(callsite).into_local_path() else { + return Err(sess.dcx().create_err(errors::ResolveRelativePath { + span, + path: source_map + .filename_for_diagnostics(&source_map.span_to_filename(callsite)) + .to_string(), + })); }; - result.pop(); - result.push(path); - Ok(result) + base_path.pop(); + base_path.push(path); + Ok(base_path) } else { Ok(path) } } -/// `Ok` represents successfully retrieving the string literal at the correct -/// position, e.g., `println("abc")`. -type ExprToSpannedStringResult<'a> = Result<(Symbol, ast::StrStyle, Span), UnexpectedExprKind<'a>>; - -/// - `Ok` is returned when the conversion to a string literal is unsuccessful, -/// but another type of expression is obtained instead. -/// - `Err` is returned when the conversion process fails. -type UnexpectedExprKind<'a> = Result<(Diag<'a>, bool /* has_suggestions */), ErrorGuaranteed>; - -/// Extracts a string literal from the macro expanded version of `expr`, -/// returning a diagnostic error of `err_msg` if `expr` is not a string literal. -/// The returned bool indicates whether an applicable suggestion has already been -/// added to the diagnostic to avoid emitting multiple suggestions. `Err(Err(ErrorGuaranteed))` -/// indicates that an ast error was encountered. -// FIXME(Nilstrieb) Make this function setup translatable -#[allow(rustc::untranslatable_diagnostic)] -pub fn expr_to_spanned_string<'a>( - cx: &'a mut ExtCtxt<'_>, - expr: P, - err_msg: &'static str, -) -> ExpandResult, ()> { - if !cx.force_mode - && let ast::ExprKind::MacCall(m) = &expr.kind - && cx.resolver.macro_accessible(cx.current_expansion.id, &m.path).is_err() - { - return ExpandResult::Retry(()); - } - - // Perform eager expansion on the expression. - // We want to be able to handle e.g., `concat!("foo", "bar")`. - let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); - - ExpandResult::Ready(Err(match expr.kind { - ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { - Ok(ast::LitKind::Str(s, style)) => { - return ExpandResult::Ready(Ok((s, style, expr.span))); - } - Ok(ast::LitKind::ByteStr(..)) => { - let mut err = cx.dcx().struct_span_err(expr.span, err_msg); - let span = expr.span.shrink_to_lo(); - err.span_suggestion( - span.with_hi(span.lo() + BytePos(1)), - "consider removing the leading `b`", - "", - Applicability::MaybeIncorrect, - ); - Ok((err, true)) - } - Ok(ast::LitKind::Err(guar)) => Err(guar), - Err(err) => Err(report_lit_error(&cx.sess.psess, err, token_lit, expr.span)), - _ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)), - }, - ast::ExprKind::Err(guar) => Err(guar), - ast::ExprKind::Dummy => { - cx.dcx().span_bug(expr.span, "tried to get a string literal from `ExprKind::Dummy`") - } - _ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)), - })) -} - -/// Extracts a string literal from the macro expanded version of `expr`, -/// emitting `err_msg` if `expr` is not a string literal. This does not stop -/// compilation on error, merely emits a non-fatal error and returns `Err`. -pub fn expr_to_string( - cx: &mut ExtCtxt<'_>, - expr: P, - err_msg: &'static str, -) -> ExpandResult, ()> { - expr_to_spanned_string(cx, expr, err_msg).map(|res| { - res.map_err(|err| match err { - Ok((err, _)) => err.emit(), - Err(guar) => guar, - }) - .map(|(symbol, style, _)| (symbol, style)) - }) -} - -/// Non-fatally assert that `tts` is empty. Note that this function -/// returns even when `tts` is non-empty, macros that *need* to stop -/// compilation should call `cx.diagnostic().abort_if_errors()` -/// (this should be done as rarely as possible). -pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) { - if !tts.is_empty() { - cx.dcx().emit_err(errors::TakesNoArguments { span, name }); - } -} - -/// Parse an expression. On error, emit it, advancing to `Eof`, and return `Err`. -pub fn parse_expr(p: &mut parser::Parser<'_>) -> Result, ErrorGuaranteed> { - let guar = match p.parse_expr() { - Ok(expr) => return Ok(expr), - Err(err) => err.emit(), - }; - while p.token != token::Eof { - p.bump(); - } - Err(guar) -} - -/// Interpreting `tts` as a comma-separated sequence of expressions, -/// expect exactly one string literal, or emit an error and return `Err`. -pub fn get_single_str_from_tts( - cx: &mut ExtCtxt<'_>, - span: Span, - tts: TokenStream, - name: &str, -) -> ExpandResult, ()> { - let mut p = cx.new_parser_from_tts(tts); - if p.token == token::Eof { - let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); - return ExpandResult::Ready(Err(guar)); - } - let ret = match parse_expr(&mut p) { - Ok(ret) => ret, - Err(guar) => return ExpandResult::Ready(Err(guar)), - }; - let _ = p.eat(&token::Comma); - - if p.token != token::Eof { - cx.dcx().emit_err(errors::OnlyOneArgument { span, name }); - } - expr_to_string(cx, ret, "argument must be a string literal").map(|s| s.map(|(s, _)| s)) -} - -/// Extracts comma-separated expressions from `tts`. -/// On error, emit it, and return `Err`. -pub fn get_exprs_from_tts( - cx: &mut ExtCtxt<'_>, - tts: TokenStream, -) -> ExpandResult>, ErrorGuaranteed>, ()> { - let mut p = cx.new_parser_from_tts(tts); - let mut es = Vec::new(); - while p.token != token::Eof { - let expr = match parse_expr(&mut p) { - Ok(expr) => expr, - Err(guar) => return ExpandResult::Ready(Err(guar)), - }; - if !cx.force_mode - && let ast::ExprKind::MacCall(m) = &expr.kind - && cx.resolver.macro_accessible(cx.current_expansion.id, &m.path).is_err() - { - return ExpandResult::Retry(()); - } - - // Perform eager expansion on the expression. - // We want to be able to handle e.g., `concat!("foo", "bar")`. - let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); - - es.push(expr); - if p.eat(&token::Comma) { - continue; - } - if p.token != token::Eof { - let guar = cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span }); - return ExpandResult::Ready(Err(guar)); - } - } - ExpandResult::Ready(Ok(es)) -} - pub fn parse_macro_name_and_helper_attrs( dcx: &rustc_errors::DiagCtxt, attr: &Attribute, diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index e71047f94fa9a..83f120525bc5f 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -1,6 +1,6 @@ use crate::base::ExtCtxt; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, PatKind, UnOp}; +use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp}; use rustc_ast::{attr, token, util::literal}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -48,6 +48,23 @@ impl<'a> ExtCtxt<'a> { ast::Path { span, segments, tokens: None } } + pub fn macro_call( + &self, + span: Span, + path: ast::Path, + delim: ast::token::Delimiter, + tokens: ast::tokenstream::TokenStream, + ) -> P { + P(ast::MacCall { + path, + args: P(ast::DelimArgs { + dspan: ast::tokenstream::DelimSpan { open: span, close: span }, + delim, + tokens, + }), + }) + } + pub fn ty_mt(&self, ty: P, mutbl: ast::Mutability) -> ast::MutTy { ast::MutTy { ty, mutbl } } @@ -185,7 +202,7 @@ impl<'a> ExtCtxt<'a> { ex: P, ) -> ast::Stmt { let pat = if mutbl { - self.pat_ident_binding_mode(sp, ident, ast::BindingAnnotation::MUT) + self.pat_ident_binding_mode(sp, ident, ast::BindingMode::MUT) } else { self.pat_ident(sp, ident) }; @@ -265,6 +282,10 @@ impl<'a> ExtCtxt<'a> { self.expr(span, ast::ExprKind::Field(expr, field)) } + pub fn expr_macro_call(&self, span: Span, call: P) -> P { + self.expr(span, ast::ExprKind::MacCall(call)) + } + pub fn expr_binary( &self, sp: Span, @@ -410,18 +431,21 @@ impl<'a> ExtCtxt<'a> { self.expr(sp, ast::ExprKind::Tup(exprs)) } - pub fn expr_fail(&self, span: Span, msg: Symbol) -> P { - self.expr_call_global( + pub fn expr_unreachable(&self, span: Span) -> P { + self.expr_macro_call( span, - [sym::std, sym::rt, sym::begin_panic].iter().map(|s| Ident::new(*s, span)).collect(), - thin_vec![self.expr_str(span, msg)], + self.macro_call( + span, + self.path_global( + span, + [sym::std, sym::unreachable].map(|s| Ident::new(s, span)).to_vec(), + ), + ast::token::Delimiter::Parenthesis, + ast::tokenstream::TokenStream::default(), + ), ) } - pub fn expr_unreachable(&self, span: Span) -> P { - self.expr_fail(span, Symbol::intern("internal error: entered unreachable code")) - } - pub fn expr_ok(&self, sp: Span, expr: P) -> P { let ok = self.std_path(&[sym::result, sym::Result, sym::Ok]); self.expr_call_global(sp, ok, thin_vec![expr]) @@ -466,14 +490,14 @@ impl<'a> ExtCtxt<'a> { self.pat(span, PatKind::Lit(expr)) } pub fn pat_ident(&self, span: Span, ident: Ident) -> P { - self.pat_ident_binding_mode(span, ident, ast::BindingAnnotation::NONE) + self.pat_ident_binding_mode(span, ident, ast::BindingMode::NONE) } pub fn pat_ident_binding_mode( &self, span: Span, ident: Ident, - ann: ast::BindingAnnotation, + ann: ast::BindingMode, ) -> P { let pat = PatKind::Ident(ann, ident.with_span_pos(span), None); self.pat(span, pat) @@ -524,7 +548,7 @@ impl<'a> ExtCtxt<'a> { } pub fn expr_match(&self, span: Span, arg: P, arms: ThinVec) -> P { - self.expr(span, ast::ExprKind::Match(arg, arms)) + self.expr(span, ast::ExprKind::Match(arg, arms, MatchKind::Prefix)) } pub fn expr_if( diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index c95d7cdeb73ab..b554a5ccd1961 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -20,6 +20,7 @@ use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use thin_vec::ThinVec; +use tracing::instrument; /// A folder that strips out items that do not belong in the current configuration. pub struct StripUnconfigured<'a> { diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index fe901603c731c..b0563bfdea749 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -1,6 +1,6 @@ use rustc_ast::ast; use rustc_errors::codes::*; -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::Limit; use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent}; use rustc_span::{Span, Symbol}; @@ -152,29 +152,6 @@ pub(crate) struct HelperAttributeNameInvalid { pub name: Ident, } -#[derive(Diagnostic)] -#[diag(expand_expected_comma_in_list)] -pub(crate) struct ExpectedCommaInList { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_only_one_argument)] -pub(crate) struct OnlyOneArgument<'a> { - #[primary_span] - pub span: Span, - pub name: &'a str, -} - -#[derive(Diagnostic)] -#[diag(expand_takes_no_arguments)] -pub(crate) struct TakesNoArguments<'a> { - #[primary_span] - pub span: Span, - pub name: &'a str, -} - #[derive(Diagnostic)] #[diag(expand_feature_removed, code = E0557)] pub(crate) struct FeatureRemoved<'a> { @@ -448,3 +425,11 @@ pub struct InvalidFragmentSpecifier { pub fragment: Ident, pub help: String, } + +#[derive(Diagnostic)] +#[diag(expand_expected_paren_or_brace)] +pub struct ExpectedParenOrBrace<'a> { + #[primary_span] + pub span: Span, + pub token: Cow<'a, str>, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cac1e8f80e323..503c9170cab53 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -482,7 +482,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { derive_invocations.reserve(derives.len()); derives .into_iter() - .map(|(path, item, _exts, is_const)| { + .map(|DeriveResolution { path, item, exts: _, is_const }| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. let expn_id = LocalExpnId::fresh_empty(); @@ -871,7 +871,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let mut parser = self.cx.new_parser_from_tts(toks); match parse_ast_fragment(&mut parser, kind) { Ok(fragment) => { - ensure_complete_parse(&mut parser, path, kind.name(), span); + ensure_complete_parse(&parser, path, kind.name(), span); fragment } Err(mut err) => { @@ -958,7 +958,7 @@ pub fn parse_ast_fragment<'a>( } pub fn ensure_complete_parse<'a>( - parser: &mut Parser<'a>, + parser: &Parser<'a>, macro_path: &ast::Path, kind_name: &str, span: Span, @@ -1218,7 +1218,7 @@ impl InvocationCollectorNode for AstNodeWrapper, TraitItemTag> fragment.make_trait_items() } fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { - noop_flat_map_assoc_item(self.wrapped, visitor) + noop_flat_map_item(self.wrapped, visitor) } fn is_mac_call(&self) -> bool { matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) @@ -1243,7 +1243,7 @@ impl InvocationCollectorNode for AstNodeWrapper, ImplItemTag> fragment.make_impl_items() } fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { - noop_flat_map_assoc_item(self.wrapped, visitor) + noop_flat_map_item(self.wrapped, visitor) } fn is_mac_call(&self) -> bool { matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) @@ -1266,7 +1266,7 @@ impl InvocationCollectorNode for P { fragment.make_foreign_items() } fn noop_flat_map(self, visitor: &mut V) -> Self::OutputTy { - noop_flat_map_foreign_item(self, visitor) + noop_flat_map_item(self, visitor) } fn is_mac_call(&self) -> bool { matches!(self.kind, ForeignItemKind::MacCall(..)) diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index e550f7242c338..3c9d2e8706835 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,7 +1,6 @@ #![doc(rust_logo)] #![feature(rustdoc_internals)] #![feature(array_windows)] -#![feature(associated_type_bounds)] #![feature(associated_type_defaults)] #![feature(if_let_guard)] #![feature(let_chains)] @@ -12,15 +11,10 @@ #![feature(proc_macro_internals)] #![feature(proc_macro_span)] #![feature(try_blocks)] +#![feature(yeet_expr)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(internal_features)] -#[macro_use] -extern crate rustc_macros; - -#[macro_use] -extern crate tracing; - extern crate proc_macro as pm; mod placeholders; diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index ca4a1f327adc8..bd6a9b7cb07da 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -14,6 +14,7 @@ pub(crate) mod transcribe; use metavar_expr::MetaVarExpr; use rustc_ast::token::{Delimiter, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan}; +use rustc_macros::{Decodable, Encodable}; use rustc_span::symbol::Ident; use rustc_span::Span; diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 15193298ccaa2..5d6c3e8aa3a4a 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -8,11 +8,13 @@ use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagCtxt, DiagMessage}; +use rustc_macros::Subdiagnostic; use rustc_parse::parser::{Parser, Recovery}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span}; use std::borrow::Cow; +use tracing::debug; use super::macro_rules::{parser_from_cx, NoopTracker}; diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index ac5136539c382..ffb50f4c92e14 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -266,7 +266,7 @@ struct MatcherPos { } // This type is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(MatcherPos, 16); impl MatcherPos { @@ -392,12 +392,7 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { #[derive(Debug, Clone)] pub(crate) enum NamedMatch { MatchedSeq(Vec), - - // A metavar match of type `tt`. - MatchedTokenTree(rustc_ast::tokenstream::TokenTree), - - // A metavar match of any type other than `tt`. - MatchedNonterminal(Lrc<(Nonterminal, rustc_span::Span)>), + MatchedSingle(ParseNtResult>), } /// Performs a token equality check, ignoring syntax context (that is, an unhygienic comparison) @@ -691,11 +686,11 @@ impl TtParser { } Ok(nt) => nt, }; - let m = match nt { - ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new((nt, span))), - ParseNtResult::Tt(tt) => MatchedTokenTree(tt), - }; - mp.push_match(next_metavar, seq_depth, m); + mp.push_match( + next_metavar, + seq_depth, + MatchedSingle(nt.map_nt(|nt| (Lrc::new((nt, span))))), + ); mp.idx += 1; } else { unreachable!() diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 3f29d7f74659c..df2643ad1ddea 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -5,7 +5,7 @@ use crate::mbe; use crate::mbe::diagnostics::{annotate_doc_comment, parse_failure_msg}; use crate::mbe::macro_check; use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success, TtParser}; -use crate::mbe::macro_parser::{MatchedSeq, MatchedTokenTree, MatcherLoc}; +use crate::mbe::macro_parser::{MatcherLoc, NamedMatch::*}; use crate::mbe::transcribe::transcribe; use ast::token::IdentIsRaw; @@ -22,13 +22,14 @@ use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, }; use rustc_lint_defs::BuiltinLintDiag; -use rustc_parse::parser::{Parser, Recovery}; +use rustc_parse::parser::{ParseNtResult, Parser, Recovery}; use rustc_session::parse::ParseSess; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent}; use rustc_span::Span; +use tracing::{debug, instrument, trace, trace_span}; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -479,7 +480,7 @@ pub fn compile_declarative_macro( MatchedSeq(s) => s .iter() .map(|m| { - if let MatchedTokenTree(tt) = m { + if let MatchedSingle(ParseNtResult::Tt(tt)) = m { let tt = mbe::quoted::parse( &TokenStream::new(vec![tt.clone()]), true, @@ -505,7 +506,7 @@ pub fn compile_declarative_macro( MatchedSeq(s) => s .iter() .map(|m| { - if let MatchedTokenTree(tt) = m { + if let MatchedSingle(ParseNtResult::Tt(tt)) = m { return mbe::quoted::parse( &TokenStream::new(vec![tt.clone()]), false, diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 81e1de5b09544..8239cfd46cbfe 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -3,6 +3,7 @@ use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; use rustc_ast::{LitIntType, LitKind}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, PResult}; +use rustc_macros::{Decodable, Encodable}; use rustc_session::parse::ParseSess; use rustc_span::symbol::Ident; use rustc_span::Span; diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 5fd3716743b22..06c1612ddbaea 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -194,9 +194,11 @@ fn parse_tree<'a>( } Delimiter::Parenthesis => {} _ => { - let tok = pprust::token_kind_to_string(&token::OpenDelim(delim)); - let msg = format!("expected `(` or `{{`, found `{tok}`"); - sess.dcx().span_err(delim_span.entire(), msg); + let token = pprust::token_kind_to_string(&token::OpenDelim(delim)); + sess.dcx().emit_err(errors::ExpectedParenOrBrace { + span: delim_span.entire(), + token, + }); } } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 8fcd468e34b7a..dad83984c8b15 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -3,14 +3,14 @@ use crate::errors::{ CountRepetitionMisplaced, MetaVarExprUnrecognizedVar, MetaVarsDifSeqMatchers, MustRepeatOnce, NoSyntaxVarsExprRepeat, VarStillRepeating, }; -use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch}; +use crate::mbe::macro_parser::{NamedMatch, NamedMatch::*}; use crate::mbe::{self, KleeneOp, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Diag; -use rustc_errors::{pluralize, PResult}; +use rustc_errors::{pluralize, Diag, PResult}; +use rustc_parse::parser::ParseNtResult; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; use rustc_span::{with_metavar_spans, Span, SyntaxContext}; @@ -250,26 +250,25 @@ pub(super) fn transcribe<'a>( // the meta-var. let ident = MacroRulesNormalizedIdent::new(original_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { - match cur_matched { - MatchedTokenTree(tt) => { + let tt = match cur_matched { + MatchedSingle(ParseNtResult::Tt(tt)) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. - let tt = maybe_use_metavar_location(cx, &stack, sp, tt, &mut marker); - result.push(tt); + maybe_use_metavar_location(cx, &stack, sp, tt, &mut marker) } - MatchedNonterminal(nt) => { + MatchedSingle(ParseNtResult::Nt(nt)) => { // Other variables are emitted into the output stream as groups with // `Delimiter::Invisible` to maintain parsing priorities. // `Interpolated` is currently used for such groups in rustc parser. marker.visit_span(&mut sp); - result - .push(TokenTree::token_alone(token::Interpolated(nt.clone()), sp)); + TokenTree::token_alone(token::Interpolated(nt.clone()), sp) } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. return Err(cx.dcx().create_err(VarStillRepeating { span: sp, ident })); } - } + }; + result.push(tt) } else { // If we aren't able to match the meta-var, we push it back into the result but // with modified syntax context. (I believe this supports nested macros). @@ -424,7 +423,7 @@ fn lookup_cur_matched<'a>( interpolations.get(&ident).map(|mut matched| { for &(idx, _) in repeats { match matched { - MatchedTokenTree(_) | MatchedNonterminal(_) => break, + MatchedSingle(_) => break, MatchedSeq(ads) => matched = ads.get(idx).unwrap(), } } @@ -514,7 +513,7 @@ fn lockstep_iter_size( let name = MacroRulesNormalizedIdent::new(*name); match lookup_cur_matched(name, interpolations, repeats) { Some(matched) => match matched { - MatchedTokenTree(_) | MatchedNonterminal(_) => LockstepIterSize::Unconstrained, + MatchedSingle(_) => LockstepIterSize::Unconstrained, MatchedSeq(ads) => LockstepIterSize::Constraint(ads.len(), name), }, _ => LockstepIterSize::Unconstrained, @@ -557,7 +556,7 @@ fn count_repetitions<'a>( // (or at the top-level of `matched` if no depth is given). fn count<'a>(depth_curr: usize, depth_max: usize, matched: &NamedMatch) -> PResult<'a, usize> { match matched { - MatchedTokenTree(_) | MatchedNonterminal(_) => Ok(1), + MatchedSingle(_) => Ok(1), MatchedSeq(named_matches) => { if depth_curr == depth_max { Ok(named_matches.len()) @@ -571,7 +570,7 @@ fn count_repetitions<'a>( /// Maximum depth fn depth(counter: usize, matched: &NamedMatch) -> usize { match matched { - MatchedTokenTree(_) | MatchedNonterminal(_) => counter, + MatchedSingle(_) => counter, MatchedSeq(named_matches) => { let rslt = counter + 1; if let Some(elem) = named_matches.first() { depth(rslt, elem) } else { rslt } @@ -599,7 +598,7 @@ fn count_repetitions<'a>( } } - if let MatchedTokenTree(_) | MatchedNonterminal(_) = matched { + if let MatchedSingle(_) = matched { return Err(cx.dcx().create_err(CountRepetitionMisplaced { span: sp.entire() })); } diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 8a68b39e4965e..c8983619e7016 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -62,7 +62,7 @@ pub(crate) fn parse_external_mod( // Ensure file paths are acyclic. if let Some(pos) = module.file_path_stack.iter().position(|p| p == &mp.file_path) { - Err(ModError::CircularInclusion(module.file_path_stack[pos..].to_vec()))?; + do yeet ModError::CircularInclusion(module.file_path_stack[pos..].to_vec()); } // Actually parse the external file as a module. diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 2c4187031cacf..581d71875bd07 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -271,14 +271,14 @@ impl MutVisitor for PlaceholderExpander { fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { match item.kind { ast::AssocItemKind::MacCall(_) => self.remove(item.id).make_trait_items(), - _ => noop_flat_map_assoc_item(item, self), + _ => noop_flat_map_item(item, self), } } fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { match item.kind { ast::AssocItemKind::MacCall(_) => self.remove(item.id).make_impl_items(), - _ => noop_flat_map_assoc_item(item, self), + _ => noop_flat_map_item(item, self), } } @@ -288,7 +288,7 @@ impl MutVisitor for PlaceholderExpander { ) -> SmallVec<[P; 1]> { match item.kind { ast::ForeignItemKind::MacCall(_) => self.remove(item.id).make_foreign_items(), - _ => noop_flat_map_foreign_item(item, self), + _ => noop_flat_map_item(item, self), } } diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index ddc685c9d07da..5a66b0fbdef10 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -208,7 +208,7 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec op(","), Semi => op(";"), Colon => op(":"), - ModSep => op("::"), + PathSep => op("::"), RArrow => op("->"), LArrow => op("<-"), FatArrow => op("=>"), diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index a3510dc9bff4d..2b0b58eb1d924 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -276,8 +276,7 @@ error: foo | 2 | fn foo() { | __________^ -3 | | -4 | | +... | 5 | | } | |___^ test diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 7e5197f16f91e..943cc63285787 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -59,6 +59,8 @@ declare_features! ( (accepted, asm_sym, "1.66.0", Some(93333)), /// Allows the definition of associated constants in `trait` or `impl` blocks. (accepted, associated_consts, "1.20.0", Some(29646)), + /// Allows the user of associated type bounds. + (accepted, associated_type_bounds, "1.79.0", Some(52662)), /// Allows using associated `type`s in `trait`s. (accepted, associated_types, "1.0.0", None), /// Allows free and inherent `async fn`s, `async` blocks, and `.await` expressions. @@ -85,7 +87,7 @@ declare_features! ( /// Enables `#[cfg(panic = "...")]` config key. (accepted, cfg_panic, "1.60.0", Some(77443)), /// Allows `cfg(target_abi = "...")`. - (accepted, cfg_target_abi, "CURRENT_RUSTC_VERSION", Some(80970)), + (accepted, cfg_target_abi, "1.78.0", Some(80970)), /// Allows `cfg(target_feature = "...")`. (accepted, cfg_target_feature, "1.27.0", Some(29717)), /// Allows `cfg(target_vendor = "...")`. @@ -96,6 +98,8 @@ declare_features! ( (accepted, closure_to_fn_coercion, "1.19.0", Some(39817)), /// Allows using the CMPXCHG16B target feature. (accepted, cmpxchg16b_target_feature, "1.69.0", Some(44839)), + /// Allows use of the `#[collapse_debuginfo]` attribute. + (accepted, collapse_debuginfo, "1.79.0", Some(100758)), /// Allows usage of the `compile_error!` macro. (accepted, compile_error, "1.20.0", Some(40872)), /// Allows `impl Trait` in function return types. @@ -147,7 +151,7 @@ declare_features! ( /// Allows the use of destructuring assignments. (accepted, destructuring_assignment, "1.59.0", Some(71126)), /// Allows using the `#[diagnostic]` attribute tool namespace - (accepted, diagnostic_namespace, "CURRENT_RUSTC_VERSION", Some(111996)), + (accepted, diagnostic_namespace, "1.78.0", Some(111996)), /// Allows `#[doc(alias = "...")]`. (accepted, doc_alias, "1.48.0", Some(50146)), /// Allows `..` in tuple (struct) patterns. @@ -203,10 +207,14 @@ declare_features! ( (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872)), /// Allows referencing `Self` and projections in impl-trait. (accepted, impl_trait_projections, "1.74.0", Some(103532)), + /// Allows using imported `main` function + (accepted, imported_main, "1.79.0", Some(28937)), /// Allows using `a..=b` and `..=b` as inclusive range syntaxes. (accepted, inclusive_range_syntax, "1.26.0", Some(28237)), /// Allows inferring outlives requirements (RFC 2093). (accepted, infer_outlives_requirements, "1.30.0", Some(44493)), + /// Allow anonymous constants from an inline `const` block + (accepted, inline_const, "1.79.0", Some(76001)), /// Allows irrefutable patterns in `if let` and `while let` statements (RFC 2086). (accepted, irrefutable_let_patterns, "1.33.0", Some(44495)), /// Allows `#[instruction_set(_)]` attribute. @@ -351,6 +359,8 @@ declare_features! ( (accepted, type_alias_enum_variants, "1.37.0", Some(49683)), /// Allows macros to appear in the type position. (accepted, type_macros, "1.13.0", Some(27245)), + /// Allows using type privacy lints (`private_interfaces`, `private_bounds`, `unnameable_types`). + (accepted, type_privacy_lints, "1.79.0", Some(48054)), /// Allows `const _: TYPE = VALUE`. (accepted, underscore_const_names, "1.37.0", Some(54912)), /// Allows `use path as _;` and `extern crate c as _;`. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e5a29f3a8462e..9d3aac66c4103 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -25,6 +25,7 @@ pub type GatedCfg = (Symbol, Symbol, GateFn); const GATED_CFGS: &[GatedCfg] = &[ // (name in cfg, feature, function to check if the feature is enabled) (sym::overflow_checks, sym::cfg_overflow_checks, cfg_fn!(cfg_overflow_checks)), + (sym::ub_checks, sym::cfg_ub_checks, cfg_fn!(cfg_ub_checks)), (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)), ( sym::target_has_atomic_equal_alignment, @@ -150,16 +151,6 @@ pub enum AttributeDuplicates { FutureWarnPreceding, } -/// A convenience macro to deal with `$($expr)?`. -macro_rules! or_default { - ($default:expr,) => { - $default - }; - ($default:expr, $next:expr) => { - $next - }; -} - /// A convenience macro for constructing attribute templates. /// E.g., `template!(Word, List: "description")` means that the attribute /// supports forms `#[attr]` and `#[attr(description)]`. @@ -181,10 +172,10 @@ macro_rules! template { } macro_rules! ungated { - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)? $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, - only_local: or_default!(false, $($only_local)?), + encode_cross_crate: $encode_cross_crate, type_: $typ, template: $tpl, gate: Ungated, @@ -194,20 +185,20 @@ macro_rules! ungated { } macro_rules! gated { - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $gate:ident, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, - only_local: or_default!(false, $($only_local)?), + encode_cross_crate: $encode_cross_crate, type_: $typ, template: $tpl, duplicates: $duplicates, gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)), } }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, - only_local: or_default!(false, $($only_local)?), + encode_cross_crate: $encode_cross_crate, type_: $typ, template: $tpl, duplicates: $duplicates, @@ -217,13 +208,13 @@ macro_rules! gated { } macro_rules! rustc_attr { - (TEST, $attr:ident, $typ:expr, $tpl:expr, $duplicate:expr $(, @only_local: $only_local:expr)? $(,)?) => { + (TEST, $attr:ident, $typ:expr, $tpl:expr, $duplicate:expr, $encode_cross_crate:expr $(,)?) => { rustc_attr!( $attr, $typ, $tpl, $duplicate, - $(@only_local: $only_local,)? + $encode_cross_crate, concat!( "the `#[", stringify!($attr), @@ -232,10 +223,10 @@ macro_rules! rustc_attr { ), ) }; - ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr $(, @only_local: $only_local:expr)?, $msg:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, - only_local: or_default!(false, $($only_local)?), + encode_cross_crate: $encode_cross_crate, type_: $typ, template: $tpl, duplicates: $duplicates, @@ -253,12 +244,19 @@ macro_rules! experimental { const IMPL_DETAIL: &str = "internal implementation detail"; const INTERNAL_UNSTABLE: &str = "this is an internal attribute that will never be stable"; +#[derive(PartialEq)] +pub enum EncodeCrossCrate { + Yes, + No, +} + pub struct BuiltinAttribute { pub name: Symbol, - /// Whether this attribute is only used in the local crate. + /// Whether this attribute is encode cross crate. /// - /// If so, it is not encoded in the crate metadata. - pub only_local: bool, + /// If so, it is encoded in the crate metadata. + /// Otherwise, it can only be used in the local crate. + pub encode_cross_crate: EncodeCrossCrate, pub type_: AttributeType, pub template: AttributeTemplate, pub duplicates: AttributeDuplicates, @@ -273,65 +271,71 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== // Conditional compilation: - ungated!(cfg, Normal, template!(List: "predicate"), DuplicatesOk), - ungated!(cfg_attr, Normal, template!(List: "predicate, attr1, attr2, ..."), DuplicatesOk), + ungated!(cfg, Normal, template!(List: "predicate"), DuplicatesOk, EncodeCrossCrate::Yes), + ungated!(cfg_attr, Normal, template!(List: "predicate, attr1, attr2, ..."), DuplicatesOk, EncodeCrossCrate::Yes), // Testing: ungated!( ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, - @only_local: true, + EncodeCrossCrate::No, ), ungated!( should_panic, Normal, template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing, - @only_local: true, + EncodeCrossCrate::No, ), // FIXME(Centril): This can be used on stable but shouldn't. ungated!( reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing, - @only_local: true, + EncodeCrossCrate::No, ), // Macros: - ungated!(automatically_derived, Normal, template!(Word), WarnFollowing), + ungated!(automatically_derived, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), ungated!( macro_use, Normal, template!(Word, List: "name1, name2, ..."), WarnFollowingWordOnly, - @only_local: true, + EncodeCrossCrate::No, + ), + ungated!(macro_escape, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), // Deprecated synonym for `macro_use`. + ungated!( + macro_export, Normal, template!(Word, List: "local_inner_macros"), + WarnFollowing, EncodeCrossCrate::Yes ), - ungated!(macro_escape, Normal, template!(Word), WarnFollowing, @only_local: true), // Deprecated synonym for `macro_use`. - ungated!(macro_export, Normal, template!(Word, List: "local_inner_macros"), WarnFollowing), - ungated!(proc_macro, Normal, template!(Word), ErrorFollowing, @only_local: true), + ungated!(proc_macro, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No), ungated!( proc_macro_derive, Normal, template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"), - ErrorFollowing, @only_local: true, + ErrorFollowing, EncodeCrossCrate::No, ), - ungated!(proc_macro_attribute, Normal, template!(Word), ErrorFollowing, @only_local: true), + ungated!(proc_macro_attribute, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No), // Lints: ungated!( warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), - DuplicatesOk, @only_local: true, + DuplicatesOk, EncodeCrossCrate::No, ), ungated!( allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), - DuplicatesOk, @only_local: true, + DuplicatesOk, EncodeCrossCrate::No, ), gated!( expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk, - @only_local: true, lint_reasons, experimental!(expect) + EncodeCrossCrate::No, lint_reasons, experimental!(expect) ), ungated!( forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), - DuplicatesOk, @only_local: true, + DuplicatesOk, EncodeCrossCrate::No ), ungated!( deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), - DuplicatesOk, @only_local: true, + DuplicatesOk, EncodeCrossCrate::No + ), + ungated!( + must_use, Normal, template!(Word, NameValueStr: "reason"), + FutureWarnFollowing, EncodeCrossCrate::Yes ), - ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing), gated!( must_not_suspend, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, - experimental!(must_not_suspend) + EncodeCrossCrate::Yes, experimental!(must_not_suspend) ), ungated!( deprecated, Normal, @@ -340,22 +344,22 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#, NameValueStr: "reason" ), - ErrorFollowing + ErrorFollowing, EncodeCrossCrate::Yes ), // Crate properties: ungated!( crate_name, CrateLevel, template!(NameValueStr: "name"), FutureWarnFollowing, - @only_local: true, + EncodeCrossCrate::No, ), ungated!( crate_type, CrateLevel, template!(NameValueStr: "bin|lib|..."), DuplicatesOk, - @only_local: true, + EncodeCrossCrate::No, ), // crate_id is deprecated ungated!( crate_id, CrateLevel, template!(NameValueStr: "ignored"), FutureWarnFollowing, - @only_local: true, + EncodeCrossCrate::No, ), // ABI, linking, symbols, and FFI @@ -363,81 +367,91 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ link, Normal, template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#), DuplicatesOk, - @only_local: true, + EncodeCrossCrate::No, ), - ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), - ungated!(no_link, Normal, template!(Word), WarnFollowing, @only_local: true), - ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true), - ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, @only_local: true), - ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, @only_local: true), - ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true), - ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true), - ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding), + ungated!( + link_name, Normal, template!(NameValueStr: "name"), + FutureWarnPreceding, EncodeCrossCrate::Yes + ), + ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No), + ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), + ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), + ungated!(no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No), + ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes), // Limits: ungated!( recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing, - @only_local: true + EncodeCrossCrate::No ), ungated!( type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing, - @only_local: true + EncodeCrossCrate::No ), gated!( move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing, - @only_local: true, large_assignments, experimental!(move_size_limit) + EncodeCrossCrate::No, large_assignments, experimental!(move_size_limit) ), // Entry point: - gated!(unix_sigpipe, Normal, template!(NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)), - ungated!(start, Normal, template!(Word), WarnFollowing, @only_local: true), - ungated!(no_start, CrateLevel, template!(Word), WarnFollowing, @only_local: true), - ungated!(no_main, CrateLevel, template!(Word), WarnFollowing, @only_local: true), + gated!( + unix_sigpipe, Normal, template!(NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, + EncodeCrossCrate::Yes, experimental!(unix_sigpipe) + ), + ungated!(start, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(no_start, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(no_main, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No), // Modules, prelude, and resolution: - ungated!(path, Normal, template!(NameValueStr: "file"), FutureWarnFollowing, @only_local: true), - ungated!(no_std, CrateLevel, template!(Word), WarnFollowing, @only_local: true), - ungated!(no_implicit_prelude, Normal, template!(Word), WarnFollowing, @only_local: true), - ungated!(non_exhaustive, Normal, template!(Word), WarnFollowing), + ungated!(path, Normal, template!(NameValueStr: "file"), FutureWarnFollowing, EncodeCrossCrate::No), + ungated!(no_std, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(no_implicit_prelude, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(non_exhaustive, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), // Runtime ungated!( windows_subsystem, CrateLevel, template!(NameValueStr: "windows|console"), FutureWarnFollowing, - @only_local: true + EncodeCrossCrate::No ), - ungated!(panic_handler, Normal, template!(Word), WarnFollowing), // RFC 2070 + ungated!(panic_handler, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), // RFC 2070 // Code generation: - ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true), - ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true), - ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing), + ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, EncodeCrossCrate::No), + ungated!(cold, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), + ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), ungated!( target_feature, Normal, template!(List: r#"enable = "name""#), - DuplicatesOk, @only_local: true, + DuplicatesOk, EncodeCrossCrate::No, ), - ungated!(track_caller, Normal, template!(Word), WarnFollowing), - ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding, @only_local: true), + ungated!(track_caller, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), + ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding, EncodeCrossCrate::No), gated!( no_sanitize, Normal, template!(List: "address, kcfi, memory, thread"), DuplicatesOk, - @only_local: true, experimental!(no_sanitize) + EncodeCrossCrate::No, experimental!(no_sanitize) ), gated!( coverage, Normal, template!(Word, List: "on|off"), - WarnFollowing, @only_local: true, + WarnFollowing, EncodeCrossCrate::No, coverage_attribute, experimental!(coverage) ), ungated!( - doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk + doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk, + EncodeCrossCrate::Yes ), // Debugging ungated!( debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), - DuplicatesOk, @only_local: true + DuplicatesOk, EncodeCrossCrate::No + ), + ungated!(collapse_debuginfo, Normal, template!(List: "no|external|yes"), ErrorFollowing, + EncodeCrossCrate::Yes ), // ========================================================================== @@ -446,54 +460,55 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Linking: gated!( - naked, Normal, template!(Word), WarnFollowing, @only_local: true, + naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, naked_functions, experimental!(naked) ), // Testing: gated!( - test_runner, CrateLevel, template!(List: "path"), ErrorFollowing, custom_test_frameworks, + test_runner, CrateLevel, template!(List: "path"), ErrorFollowing, + EncodeCrossCrate::Yes, custom_test_frameworks, "custom test frameworks are an unstable feature", ), // RFC #1268 gated!( - marker, Normal, template!(Word), WarnFollowing, @only_local: true, + marker, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, marker_trait_attr, experimental!(marker) ), gated!( - thread_local, Normal, template!(Word), WarnFollowing, @only_local: true, + thread_local, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "`#[thread_local]` is an experimental feature, and does not currently handle destructors", ), gated!( no_core, CrateLevel, template!(Word), WarnFollowing, - @only_local: true, experimental!(no_core) + EncodeCrossCrate::No, experimental!(no_core) ), // RFC 2412 gated!( optimize, Normal, template!(List: "size|speed"), ErrorPreceding, - @only_local: true, optimize_attribute, experimental!(optimize) + EncodeCrossCrate::No, optimize_attribute, experimental!(optimize) ), gated!( ffi_pure, Normal, template!(Word), WarnFollowing, - @only_local: true, experimental!(ffi_pure) + EncodeCrossCrate::No, experimental!(ffi_pure) ), gated!( ffi_const, Normal, template!(Word), WarnFollowing, - @only_local: true, experimental!(ffi_const) + EncodeCrossCrate::No, experimental!(ffi_const) ), gated!( register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk, - @only_local: true, experimental!(register_tool), + EncodeCrossCrate::No, experimental!(register_tool), ), gated!( cmse_nonsecure_entry, Normal, template!(Word), WarnFollowing, - @only_local: true, experimental!(cmse_nonsecure_entry) + EncodeCrossCrate::No, experimental!(cmse_nonsecure_entry) ), // RFC 2632 gated!( - const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl, + const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, const_trait_impl, "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \ `impls` and all default bodies as `const`, which may be removed or renamed in the \ future." @@ -501,25 +516,25 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // lang-team MCP 147 gated!( deprecated_safe, Normal, template!(List: r#"since = "version", note = "...""#), ErrorFollowing, - experimental!(deprecated_safe), - ), - - // `#[collapse_debuginfo]` - gated!( - collapse_debuginfo, Normal, template!(Word, List: "no|external|yes"), ErrorFollowing, - @only_local: true, experimental!(collapse_debuginfo) + EncodeCrossCrate::Yes, experimental!(deprecated_safe), ), // RFC 2397 gated!( do_not_recommend, Normal, template!(Word), WarnFollowing, - @only_local: true, experimental!(do_not_recommend) + EncodeCrossCrate::No, experimental!(do_not_recommend) ), // `#[cfi_encoding = ""]` gated!( cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding, - experimental!(cfi_encoding) + EncodeCrossCrate::Yes, experimental!(cfi_encoding) + ), + + // `#[coroutine]` attribute to be applied to closures to make them coroutines instead + gated!( + coroutine, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::No, coroutines, experimental!(coroutines) ), // ========================================================================== @@ -528,43 +543,48 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!( feature, CrateLevel, - template!(List: "name1, name2, ..."), DuplicatesOk, @only_local: true, + template!(List: "name1, name2, ..."), DuplicatesOk, EncodeCrossCrate::No, ), // DuplicatesOk since it has its own validation ungated!( stable, Normal, - template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, @only_local: true, + template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, EncodeCrossCrate::No, ), ungated!( unstable, Normal, template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk, + EncodeCrossCrate::Yes + ), + ungated!( + rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), + DuplicatesOk, EncodeCrossCrate::Yes ), - ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), ungated!( rustc_const_stable, Normal, - template!(List: r#"feature = "name""#), DuplicatesOk, @only_local: true, + template!(List: r#"feature = "name""#), DuplicatesOk, EncodeCrossCrate::No, ), ungated!( rustc_default_body_unstable, Normal, template!(List: r#"feature = "name", reason = "...", issue = "N""#), - DuplicatesOk, @only_local: true + DuplicatesOk, EncodeCrossCrate::No ), gated!( - allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, + allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), + DuplicatesOk, EncodeCrossCrate::Yes, "allow_internal_unstable side-steps feature gating and stability checks", ), gated!( rustc_allow_const_fn_unstable, Normal, - template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, @only_local: true, + template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, EncodeCrossCrate::No, "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" ), gated!( allow_internal_unsafe, Normal, template!(Word), WarnFollowing, - @only_local: true, "allow_internal_unsafe side-steps the unsafe_code lint", + EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint", ), rustc_attr!( rustc_allowed_through_unstable_modules, Normal, template!(Word), - WarnFollowing, @only_local: true, + WarnFollowing, EncodeCrossCrate::No, "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ through unstable paths" ), @@ -573,17 +593,20 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes: Type system related: // ========================================================================== - gated!(fundamental, Normal, template!(Word), WarnFollowing, experimental!(fundamental)), + gated!(fundamental, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, experimental!(fundamental)), gated!( may_dangle, Normal, template!(Word), WarnFollowing, - @only_local: true, dropck_eyepatch, + EncodeCrossCrate::No, dropck_eyepatch, "`may_dangle` has unstable semantics and may be removed in the future", ), rustc_attr!( - rustc_never_type_mode, Normal, template!(NameValueStr: "fallback_to_unit|fallback_to_niko|fallback_to_never|no_fallback"), ErrorFollowing, - @only_local: true, - "`rustc_never_type_fallback` is used to experiment with never type fallback and work on \ + rustc_never_type_options, + Normal, + template!(List: r#"/*opt*/ fallback = "unit|niko|never|no""#), + ErrorFollowing, + EncodeCrossCrate::No, + "`rustc_never_type_options` is used to experiment with never type fallback and work on \ never type stabilization, and will never be stable" ), @@ -593,49 +616,49 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_allocator, Normal, template!(Word), WarnFollowing, - @only_local: true, IMPL_DETAIL + EncodeCrossCrate::No, IMPL_DETAIL ), rustc_attr!( rustc_nounwind, Normal, template!(Word), WarnFollowing, - @only_local: true, IMPL_DETAIL + EncodeCrossCrate::No, IMPL_DETAIL ), rustc_attr!( rustc_reallocator, Normal, template!(Word), WarnFollowing, - @only_local: true, IMPL_DETAIL + EncodeCrossCrate::No, IMPL_DETAIL ), rustc_attr!( rustc_deallocator, Normal, template!(Word), WarnFollowing, - @only_local: true, IMPL_DETAIL + EncodeCrossCrate::No, IMPL_DETAIL ), rustc_attr!( rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, - @only_local: true, IMPL_DETAIL + EncodeCrossCrate::No, IMPL_DETAIL ), gated!( default_lib_allocator, Normal, template!(Word), WarnFollowing, - @only_local: true, allocator_internals, experimental!(default_lib_allocator), + EncodeCrossCrate::No, allocator_internals, experimental!(default_lib_allocator), ), gated!( needs_allocator, Normal, template!(Word), WarnFollowing, - @only_local: true, allocator_internals, experimental!(needs_allocator), + EncodeCrossCrate::No, allocator_internals, experimental!(needs_allocator), ), gated!( panic_runtime, Normal, template!(Word), WarnFollowing, - @only_local: true, experimental!(panic_runtime) + EncodeCrossCrate::No, experimental!(panic_runtime) ), gated!( needs_panic_runtime, Normal, template!(Word), WarnFollowing, - @only_local: true, experimental!(needs_panic_runtime) + EncodeCrossCrate::No, experimental!(needs_panic_runtime) ), gated!( compiler_builtins, Normal, template!(Word), WarnFollowing, - @only_local: true, + EncodeCrossCrate::No, "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \ which contains compiler-rt intrinsics and will never be stable", ), gated!( profiler_runtime, Normal, template!(Word), WarnFollowing, - @only_local: true, + EncodeCrossCrate::No, "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \ which contains the profiler runtime and will never be stable", ), @@ -645,11 +668,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== gated!( - linkage, Normal, template!(NameValueStr: "external|internal|..."), ErrorPreceding, @only_local: true, + linkage, Normal, template!(NameValueStr: "external|internal|..."), + ErrorPreceding, EncodeCrossCrate::No, "the `linkage` attribute is experimental and not portable across platforms", ), rustc_attr!( - rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, @only_local: true, INTERNAL_UNSTABLE + rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, INTERNAL_UNSTABLE ), // ========================================================================== @@ -659,16 +684,16 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_builtin_macro, Normal, template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing, - IMPL_DETAIL, + EncodeCrossCrate::Yes, IMPL_DETAIL ), rustc_attr!( rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, - @only_local: true, INTERNAL_UNSTABLE + EncodeCrossCrate::No, INTERNAL_UNSTABLE ), rustc_attr!( rustc_macro_transparency, Normal, template!(NameValueStr: "transparent|semitransparent|opaque"), ErrorFollowing, - "used internally for testing macro hygiene", + EncodeCrossCrate::Yes, "used internally for testing macro hygiene", ), // ========================================================================== @@ -681,36 +706,50 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ List: r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#, NameValueStr: "message" ), - ErrorFollowing, + ErrorFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), rustc_attr!( rustc_confusables, Normal, template!(List: r#""name1", "name2", ..."#), - ErrorFollowing, + ErrorFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE, ), // Enumerates "identity-like" conversion methods to suggest on type mismatch. rustc_attr!( - rustc_conversion_suggestion, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + rustc_conversion_suggestion, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), // Prevents field reads in the marked trait or method to be considered // during dead code analysis. rustc_attr!( - rustc_trivial_field_reads, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + rustc_trivial_field_reads, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), // Used by the `rustc::potential_query_instability` lint to warn methods which // might not be stable during incremental compilation. - rustc_attr!(rustc_lint_query_instability, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + rustc_attr!( + rustc_lint_query_instability, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + ), // Used by the `rustc::diagnostic_outside_of_impl` lints to assist in changes to diagnostic // APIs. Any function with this attribute will be checked by that lint. - rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + rustc_attr!( + rustc_lint_diagnostics, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + ), // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` // types (as well as any others in future). - rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + rustc_attr!( + rustc_lint_opt_ty, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + ), // Used by the `rustc::bad_opt_access` lint on fields // types (as well as any others in future). - rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE), + rustc_attr!( + rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), + WarnFollowing, EncodeCrossCrate::Yes, INTERNAL_UNSTABLE + ), // ========================================================================== // Internal attributes, Const related: @@ -718,18 +757,20 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_promotable, Normal, template!(Word), WarnFollowing, - @only_local: true, IMPL_DETAIL), + EncodeCrossCrate::No, IMPL_DETAIL), rustc_attr!( rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing, - INTERNAL_UNSTABLE + EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), // Do not const-check this function's body. It will always get replaced during CTFE. rustc_attr!( - rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), // Ensure the argument to this function is &&str during const-check. rustc_attr!( - rustc_const_panic_str, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE + rustc_const_panic_str, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes, INTERNAL_UNSTABLE ), // ========================================================================== @@ -738,16 +779,19 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), ErrorFollowing, + EncodeCrossCrate::Yes, "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \ niche optimizations in libcore and libstd and will never be stable", ), rustc_attr!( rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), ErrorFollowing, + EncodeCrossCrate::Yes, "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ niche optimizations in libcore and libstd and will never be stable", ), rustc_attr!( rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes, "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable \ niche optimizations in libcore and libstd and will never be stable", ), @@ -756,44 +800,51 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes, Misc: // ========================================================================== gated!( - lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, @only_local: true, lang_items, - "language items are subject to change", + lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, EncodeCrossCrate::No, lang_items, + "lang items are subject to change", ), rustc_attr!( rustc_pass_by_value, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." ), rustc_attr!( rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers." ), rustc_attr!( - rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true, + rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." ), rustc_attr!( - rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true, + rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver." ), rustc_attr!( - rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true, + rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." ), + rustc_attr!( + rustc_preserve_ub_checks, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, + "`#![rustc_preserve_ub_checks]` prevents the designated crate from evaluating whether UB checks are enabled when optimizing MIR", + ), rustc_attr!( rustc_deny_explicit_impl, AttributeType::Normal, template!(List: "implement_via_object = (true|false)"), ErrorFollowing, - @only_local: true, + EncodeCrossCrate::No, "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls" ), rustc_attr!( - rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, + rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::Yes, "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ the given type by annotating all impl items with #[rustc_allow_incoherent_impl]." ), rustc_attr!( - rustc_box, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true, + rustc_box, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "#[rustc_box] allows creating boxes \ and it is only intended to be used in `alloc`." ), @@ -801,7 +852,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ BuiltinAttribute { name: sym::rustc_diagnostic_item, // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. - only_local: false, + encode_cross_crate: EncodeCrossCrate::Yes, type_: Normal, template: template!(NameValueStr: "name"), duplicates: ErrorFollowing, @@ -815,74 +866,74 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ gated!( // Used in resolve: prelude_import, Normal, template!(Word), WarnFollowing, - @only_local: true, "`#[prelude_import]` is for use by rustc only", + EncodeCrossCrate::No, "`#[prelude_import]` is for use by rustc only", ), gated!( - rustc_paren_sugar, Normal, template!(Word), WarnFollowing, @only_local: true, + rustc_paren_sugar, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, unboxed_closures, "unboxed_closures are still evolving", ), rustc_attr!( - rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, @only_local: true, + rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \ overflow checking behavior of several libcore functions that are inlined \ across crates and will never be stable", ), rustc_attr!( rustc_reservation_impl, Normal, - template!(NameValueStr: "reservation message"), ErrorFollowing, + template!(NameValueStr: "reservation message"), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_reservation_impl]` attribute is internally used \ for reserving for `for From for T` impl" ), rustc_attr!( rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing, - @only_local: true, "the `#[rustc_test_marker]` attribute is used internally to track tests", + EncodeCrossCrate::No, "the `#[rustc_test_marker]` attribute is used internally to track tests", ), rustc_attr!( rustc_unsafe_specialization_marker, Normal, template!(Word), - WarnFollowing, @only_local: true, + WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_unsafe_specialization_marker]` attribute is used to check specializations" ), rustc_attr!( rustc_specialization_trait, Normal, template!(Word), - WarnFollowing, @only_local: true, + WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_specialization_trait]` attribute is used to check specializations" ), rustc_attr!( - rustc_main, Normal, template!(Word), WarnFollowing, @only_local: true, + rustc_main, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_main]` attribute is used internally to specify test entry point function", ), rustc_attr!( rustc_skip_array_during_method_dispatch, Normal, template!(Word), - WarnFollowing, @only_local: true, + WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \ from method dispatch when the receiver is an array, for compatibility in editions < 2021." ), rustc_attr!( rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."), - ErrorFollowing, @only_local: true, + ErrorFollowing, EncodeCrossCrate::No, "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \ definition of a trait, it's currently in experimental form and should be changed before \ being exposed outside of the std" ), rustc_attr!( rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing, - r#"`rustc_doc_primitive` is a rustc internal attribute"#, + EncodeCrossCrate::Yes, r#"`rustc_doc_primitive` is a rustc internal attribute"#, ), rustc_attr!( rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing, - @only_local: true, + EncodeCrossCrate::No, "the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe" ), rustc_attr!( - rustc_intrinsic, Normal, template!(Word), ErrorFollowing, + rustc_intrinsic, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies", ), rustc_attr!( - rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, + rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, "#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen" ), rustc_attr!( - rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing, + rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, "the `#[rustc_intrinsic_must_be_overridden]` attribute is used to declare intrinsics without real bodies", ), @@ -890,109 +941,135 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes, Testing: // ========================================================================== - rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), rustc_attr!( TEST, rustc_outlives, Normal, template!(Word), - WarnFollowing, @only_local: true + WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_capture_analysis, Normal, template!(Word), - WarnFollowing, @only_local: true + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_insignificant_dtor, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes + ), + rustc_attr!( + TEST, rustc_strict_coherence, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes + ), + rustc_attr!( + TEST, rustc_variance, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No ), - rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_strict_coherence, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing, @only_local: true), rustc_attr!( TEST, rustc_variance_of_opaques, Normal, template!(Word), - WarnFollowing, @only_local: true + WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_hidden_type_of_opaques, Normal, template!(Word), - WarnFollowing, @only_local: true), - rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), WarnFollowing), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), + WarnFollowing, EncodeCrossCrate::Yes + ), rustc_attr!( TEST, rustc_abi, Normal, template!(List: "field1, field2, ..."), - WarnFollowing, @only_local: true + WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_regions, Normal, template!(Word), - WarnFollowing, @only_local: true + WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_error, Normal, - template!(Word, List: "delayed_bug_from_inside_query"), WarnFollowingWordOnly + template!(Word, List: "delayed_bug_from_inside_query"), + WarnFollowingWordOnly, EncodeCrossCrate::Yes ), rustc_attr!( - TEST, rustc_dump_user_args, Normal, template!(Word), WarnFollowing, - @only_local: true + TEST, rustc_dump_user_args, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No ), - rustc_attr!(TEST, rustc_evaluate_where_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!( - TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), - DuplicatesOk, @only_local: true + TEST, rustc_evaluate_where_clauses, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes ), rustc_attr!( - TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode"), - DuplicatesOk, @only_local: true + TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), DuplicatesOk, + EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode"), DuplicatesOk, + EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_clean, Normal, template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), - DuplicatesOk, @only_local: true + DuplicatesOk, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_partition_reused, Normal, - template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, @only_local: true + template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_partition_codegened, Normal, - template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, @only_local: true + template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_expected_cgu_reuse, Normal, template!(List: r#"cfg = "...", module = "...", kind = "...""#), DuplicatesOk, - @only_local: true + EncodeCrossCrate::No ), rustc_attr!( - TEST, rustc_symbol_name, Normal, template!(Word), WarnFollowing, - @only_local: true + TEST, rustc_symbol_name, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No ), - rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing), rustc_attr!( - TEST, rustc_def_path, Normal, template!(Word), WarnFollowing, - @only_local: true + TEST, rustc_polymorphize_error, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes + ), + rustc_attr!( + TEST, rustc_def_path, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), + DuplicatesOk, EncodeCrossCrate::Yes ), - rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk), gated!( custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#), - ErrorFollowing, @only_local: true, + ErrorFollowing, EncodeCrossCrate::No, "the `#[custom_mir]` attribute is just used for the Rust test suite", ), rustc_attr!( - TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing, - @only_local: true + TEST, rustc_dump_program_clauses, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( - TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing, - @only_local: true + TEST, rustc_dump_env_program_clauses, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( - TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing, - @only_local: true + TEST, rustc_object_lifetime_default, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No ), - rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word), WarnFollowing), rustc_attr!( - TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk, - @only_local: true + TEST, rustc_dump_vtable, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes + ), + rustc_attr!( + TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), + DuplicatesOk, EncodeCrossCrate::No ), gated!( - omit_gdb_pretty_printer_section, Normal, template!(Word), WarnFollowing, - @only_local: true, + omit_gdb_pretty_printer_section, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No, "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite", ), rustc_attr!( TEST, pattern_complexity, CrateLevel, template!(NameValueStr: "N"), - ErrorFollowing, @only_local: true, + ErrorFollowing, EncodeCrossCrate::No, ), ]; @@ -1004,10 +1081,14 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() } -/// Whether this builtin attribute is only used in the local crate. -/// If so, it is not encoded in the crate metadata. -pub fn is_builtin_only_local(name: Symbol) -> bool { - BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| attr.only_local) +/// Whether this builtin attribute is encoded cross crate. +/// 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) { + attr.encode_cross_crate == EncodeCrossCrate::Yes + } else { + true + } } pub fn is_valid_for_get_attr(name: Symbol) -> bool { diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index cbc0ce8c97468..36ef8fe7816fe 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -12,7 +12,6 @@ //! symbol to the `accepted` or `removed` modules respectively. #![allow(internal_features)] -#![feature(generic_nonzero)] #![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(lazy_cell)] @@ -124,7 +123,7 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option`. (unstable, associated_const_equality, "1.58.0", Some(92827)), - /// Allows the user of associated type bounds. - (unstable, associated_type_bounds, "1.34.0", Some(52662)), /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), /// Allows `async || body` closures. @@ -383,6 +383,8 @@ declare_features! ( (unstable, cfg_target_has_atomic_equal_alignment, "1.60.0", Some(93822)), /// Allows `cfg(target_thread_local)`. (unstable, cfg_target_thread_local, "1.7.0", Some(29594)), + /// Allows the use of `#[cfg(ub_checks)` to check if UB checks are enabled. + (unstable, cfg_ub_checks, "1.79.0", Some(123499)), /// Allow conditional compilation depending on rust version (unstable, cfg_version, "1.45.0", Some(64796)), /// Allows to use the `#[cfi_encoding = ""]` attribute. @@ -393,8 +395,6 @@ declare_features! ( (unstable, closure_track_caller, "1.57.0", Some(87417)), /// Allows to use the `#[cmse_nonsecure_entry]` attribute. (unstable, cmse_nonsecure_entry, "1.48.0", Some(75835)), - /// Allows use of the `#[collapse_debuginfo]` attribute. - (unstable, collapse_debuginfo, "1.65.0", Some(100758)), /// Allows `async {}` expressions in const contexts. (unstable, const_async_blocks, "1.53.0", Some(85368)), /// Allows `const || {}` closures in const contexts. @@ -412,7 +412,7 @@ declare_features! ( /// Allows references to types with interior mutability within constants (unstable, const_refs_to_cell, "1.51.0", Some(80384)), /// Allows creating pointers and references to `static` items in constants. - (unstable, const_refs_to_static, "CURRENT_RUSTC_VERSION", Some(119618)), + (unstable, const_refs_to_static, "1.78.0", Some(119618)), /// Allows `impl const Trait for T` syntax. (unstable, const_trait_impl, "1.42.0", Some(67792)), /// Allows the `?` operator in const contexts. @@ -438,6 +438,8 @@ declare_features! ( (unstable, deprecated_safe, "1.61.0", Some(94978)), /// Allows having using `suggestion` in the `#[deprecated]` attribute. (unstable, deprecated_suggestion, "1.61.0", Some(94785)), + /// Allows deref patterns. + (incomplete, deref_patterns, "1.79.0", Some(87121)), /// Controls errors in trait implementations. (unstable, do_not_recommend, "1.67.0", Some(51992)), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. @@ -464,9 +466,9 @@ declare_features! ( /// Allows defining `extern type`s. (unstable, extern_types, "1.23.0", Some(43467)), /// Allow using 128-bit (quad precision) floating point numbers. - (unstable, f128, "CURRENT_RUSTC_VERSION", Some(116909)), + (unstable, f128, "1.78.0", Some(116909)), /// Allow using 16-bit (half precision) floating point numbers. - (unstable, f16, "CURRENT_RUSTC_VERSION", Some(116909)), + (unstable, f16, "1.78.0", Some(116909)), /// Allows the use of `#[ffi_const]` on foreign functions. (unstable, ffi_const, "1.45.0", Some(58328)), /// Allows the use of `#[ffi_pure]` on foreign functions. @@ -476,7 +478,7 @@ declare_features! ( /// Support delegating implementation of functions to other already implemented functions. (incomplete, fn_delegation, "1.76.0", Some(118212)), /// Allows impls for the Freeze trait. - (internal, freeze_impls, "CURRENT_RUSTC_VERSION", Some(121675)), + (internal, freeze_impls, "1.78.0", Some(121675)), /// Allows defining gen blocks and `gen fn`. (unstable, gen_blocks, "1.75.0", Some(117078)), /// Infer generic args for both consts and types. @@ -495,12 +497,8 @@ declare_features! ( (unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)), /// Allows `impl Trait` as output type in `Fn` traits in return position of functions. (unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)), - /// Allows using imported `main` function - (unstable, imported_main, "1.53.0", Some(28937)), /// Allows associated types in inherent impls. (incomplete, inherent_associated_types, "1.52.0", Some(8995)), - /// Allow anonymous constants from an inline `const` block - (unstable, inline_const, "1.49.0", Some(76001)), /// Allow anonymous constants from an inline `const` block in pattern position (unstable, inline_const_pat, "1.58.0", Some(76001)), /// Allows using `pointer` and `reference` in intra-doc links @@ -531,6 +529,10 @@ declare_features! ( (unstable, more_qualified_paths, "1.54.0", Some(86935)), /// Allows the `#[must_not_suspend]` attribute. (unstable, must_not_suspend, "1.57.0", Some(83310)), + /// Make `mut` not reset the binding mode on edition >= 2024. + (incomplete, mut_preserve_binding_mode_2024, "1.79.0", Some(123076)), + /// Allows `mut ref` and `mut ref mut` identifier patterns. + (incomplete, mut_ref, "1.79.0", Some(123076)), /// Allows using `#[naked]` on functions. (unstable, naked_functions, "1.9.0", Some(90957)), /// Allows specifying the as-needed link modifier @@ -561,10 +563,18 @@ declare_features! ( (unstable, offset_of_nested, "1.77.0", Some(120140)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), + /// Allows postfix match `expr.match { ... }` + (unstable, postfix_match, "1.79.0", Some(121618)), + /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args. + (incomplete, precise_capturing, "1.79.0", Some(123432)), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. (unstable, raw_ref_op, "1.41.0", Some(64490)), + /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. + (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)), + /// Allows `&` and `&mut` patterns to consume match-ergonomics-inserted references. + (incomplete, ref_pat_everywhere, "1.79.0", Some(123076)), /// Allows using the `#[register_tool]` attribute. (unstable, register_tool, "1.41.0", Some(66079)), /// Allows the `#[repr(i128)]` attribute for enums. @@ -609,8 +619,6 @@ declare_features! ( /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (unstable, type_changing_struct_update, "1.58.0", Some(86555)), - /// Allows using type privacy lints (`private_interfaces`, `private_bounds`, `unnameable_types`). - (unstable, type_privacy_lints, "1.72.0", Some(48054)), /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE. (unstable, unix_sigpipe, "1.65.0", Some(97889)), /// Allows unnamed fields of struct and union type diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 3359fef8c1c46..d376c24cb589c 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(absolute_path)] - use std::ffi::CString; use std::fs; use std::io; diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 1810193c16bd9..649a08b6972fb 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -5,7 +5,7 @@ use rustc_ast as ast; use rustc_ast::NodeId; use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_data_structures::unord::UnordMap; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::kw; @@ -113,6 +113,9 @@ pub enum DefKind { InlineConst, /// Opaque type, aka `impl Trait`. OpaqueTy, + /// A field in a struct, enum or union. e.g. + /// - `bar` in `struct Foo { bar: u8 }` + /// - `Foo::Bar::0` in `enum Foo { Bar(u8) }` Field, /// Lifetime parameter: the `'a` in `struct Foo<'a> { ... }` LifetimeParam, @@ -204,7 +207,6 @@ impl DefKind { | DefKind::Enum | DefKind::Variant | DefKind::Trait - | DefKind::OpaqueTy | DefKind::TyAlias | DefKind::ForeignTy | DefKind::TraitAlias @@ -231,7 +233,8 @@ impl DefKind { | DefKind::Use | DefKind::ForeignMod | DefKind::GlobalAsm - | DefKind::Impl { .. } => None, + | DefKind::Impl { .. } + | DefKind::OpaqueTy => None, } } @@ -810,6 +813,8 @@ pub enum LifetimeRes { param: NodeId, /// Id of the introducing place. See `Param`. binder: NodeId, + /// Kind of elided lifetime + kind: hir::MissingLifetimeKind, }, /// This variant is used for anonymous lifetimes that we did not resolve during /// late resolution. Those lifetimes will be inferred by typechecking. diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index f538d6bcb9816..35833e258d558 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -7,14 +7,14 @@ pub use crate::def_id::DefPathHash; use crate::def_id::{CrateNum, DefIndex, LocalDefId, StableCrateId, CRATE_DEF_INDEX, LOCAL_CRATE}; use crate::def_path_hash_map::DefPathHashMap; - use rustc_data_structures::stable_hasher::{Hash64, StableHasher}; use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; +use rustc_macros::{Decodable, Encodable}; use rustc_span::symbol::{kw, sym, Symbol}; - use std::fmt::{self, Write}; use std::hash::Hash; +use tracing::{debug, instrument}; /// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa. /// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey` @@ -380,14 +380,19 @@ impl Definitions { pub fn local_def_path_hash_to_def_id( &self, hash: DefPathHash, - err: &mut dyn FnMut() -> !, + err_msg: &dyn std::fmt::Debug, ) -> LocalDefId { debug_assert!(hash.stable_crate_id() == self.table.stable_crate_id); + #[cold] + #[inline(never)] + fn err(err_msg: &dyn std::fmt::Debug) -> ! { + panic!("{err_msg:?}") + } self.table .def_path_hash_to_index .get(&hash.local_hash()) .map(|local_def_index| LocalDefId { local_def_index }) - .unwrap_or_else(|| err()) + .unwrap_or_else(|| err(err_msg)) } pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e21d31238e0dd..fc8f7466694c7 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3,17 +3,16 @@ use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; use crate::intravisit::FnKind; use crate::LangItem; - use rustc_ast as ast; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, TraitObjectSyntax, UintTy}; -pub use rustc_ast::{BinOp, BinOpKind, BindingAnnotation, BorrowKind, ByRef, CaptureBy}; +pub use rustc_ast::{BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy}; pub use rustc_ast::{ImplPolarity, IsAuto, Movability, Mutability, UnOp}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_index::IndexVec; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -21,9 +20,9 @@ use rustc_span::ErrorGuaranteed; use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP}; use rustc_target::asm::InlineAsmRegOrRegClass; use rustc_target::spec::abi::Abi; - use smallvec::SmallVec; use std::fmt; +use tracing::debug; #[derive(Debug, Copy, Clone, HashStable_Generic)] pub struct Lifetime { @@ -428,10 +427,6 @@ pub enum TraitBoundModifier { MaybeConst, } -/// The AST represents all type param bounds as types. -/// `typeck::collect::compute_bounds` matches these against -/// the "special" built-in traits (see `middle::lang_items`) and -/// detects `Copy`, `Send` and `Sync`. #[derive(Clone, Copy, Debug, HashStable_Generic)] pub enum GenericBound<'hir> { Trait(PolyTraitRef<'hir>, TraitBoundModifier), @@ -456,6 +451,18 @@ impl GenericBound<'_> { pub type GenericBounds<'hir> = &'hir [GenericBound<'hir>]; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable_Generic, Debug)] +pub enum MissingLifetimeKind { + /// An explicit `'_`. + Underscore, + /// An elided lifetime `&' ty`. + Ampersand, + /// An elided lifetime in brackets with written brackets. + Comma, + /// An elided lifetime with elided brackets. + Brackets, +} + #[derive(Copy, Clone, Debug, HashStable_Generic)] pub enum LifetimeParamKind { // Indicates that the lifetime definition was explicitly declared (e.g., in @@ -464,7 +471,7 @@ pub enum LifetimeParamKind { // Indication that the lifetime was elided (e.g., in both cases in // `fn foo(x: &u8) -> &'_ u8 { x }`). - Elided, + Elided(MissingLifetimeKind), // Indication that the lifetime name was somehow in error. Error, @@ -512,7 +519,7 @@ impl<'hir> GenericParam<'hir> { /// /// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information. pub fn is_elided_lifetime(&self) -> bool { - matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided }) + matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(_) }) } } @@ -636,13 +643,49 @@ impl<'hir> Generics<'hir> { }) } - pub fn bounds_span_for_suggestions(&self, param_def_id: LocalDefId) -> Option { + /// Returns a suggestable empty span right after the "final" bound of the generic parameter. + /// + /// If that bound needs to be wrapped in parentheses to avoid ambiguity with + /// subsequent bounds, it also returns an empty span for an open parenthesis + /// as the second component. + /// + /// E.g., adding `+ 'static` after `Fn() -> dyn Future` or + /// `Fn() -> &'static dyn Debug` requires parentheses: + /// `Fn() -> (dyn Future) + 'static` and + /// `Fn() -> &'static (dyn Debug) + 'static`, respectively. + pub fn bounds_span_for_suggestions( + &self, + param_def_id: LocalDefId, + ) -> Option<(Span, Option)> { self.bounds_for_param(param_def_id).flat_map(|bp| bp.bounds.iter().rev()).find_map( |bound| { - // We include bounds that come from a `#[derive(_)]` but point at the user's code, - // as we use this method to get a span appropriate for suggestions. - let bs = bound.span(); - bs.can_be_used_for_suggestions().then(|| bs.shrink_to_hi()) + let span_for_parentheses = if let Some(trait_ref) = bound.trait_ref() + && let [.., segment] = trait_ref.path.segments + && segment.args().parenthesized == GenericArgsParentheses::ParenSugar + && let [binding] = segment.args().bindings + && let TypeBindingKind::Equality { term: Term::Ty(ret_ty) } = binding.kind + && let ret_ty = ret_ty.peel_refs() + && let TyKind::TraitObject( + _, + _, + TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar, + ) = ret_ty.kind + && ret_ty.span.can_be_used_for_suggestions() + { + Some(ret_ty.span) + } else { + None + }; + + span_for_parentheses.map_or_else( + || { + // We include bounds that come from a `#[derive(_)]` but point at the user's code, + // as we use this method to get a span appropriate for suggestions. + let bs = bound.span(); + bs.can_be_used_for_suggestions().then(|| (bs.shrink_to_hi(), None)) + }, + |span| Some((span.shrink_to_hi(), Some(span.shrink_to_lo()))), + ) }, ) } @@ -838,9 +881,8 @@ pub struct OwnerNodes<'tcx> { impl<'tcx> OwnerNodes<'tcx> { pub fn node(&self) -> OwnerNode<'tcx> { - use rustc_index::Idx; // Indexing must ensure it is an OwnerNode. - self.nodes[ItemLocalId::new(0)].node.as_owner().unwrap() + self.nodes[ItemLocalId::ZERO].node.as_owner().unwrap() } } @@ -848,7 +890,7 @@ impl fmt::Debug for OwnerNodes<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OwnerNodes") // Do not print all the pointers to all the nodes, as it would be unreadable. - .field("node", &self.nodes[ItemLocalId::from_u32(0)]) + .field("node", &self.nodes[ItemLocalId::ZERO]) .field( "parents", &self @@ -1003,7 +1045,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, - Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), Slice(before, slice, after) => { @@ -1030,7 +1072,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} - Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), Slice(before, slice, after) => { @@ -1144,7 +1186,7 @@ pub enum PatKind<'hir> { /// The `HirId` is the canonical ID for the variable being bound, /// (e.g., in `Ok(x) | Err(x)`, both `x` use the same canonical ID), /// which is the pattern ID of the first `x`. - Binding(BindingAnnotation, HirId, Ident, Option<&'hir Pat<'hir>>), + Binding(BindingMode, HirId, Ident, Option<&'hir Pat<'hir>>), /// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`). /// The `bool` is `true` in the presence of a `..`. @@ -1173,6 +1215,9 @@ pub enum PatKind<'hir> { /// A `box` pattern. Box(&'hir Pat<'hir>), + /// A `deref` pattern (currently `deref!()` macro-based syntax). + Deref(&'hir Pat<'hir>), + /// A reference pattern (e.g., `&mut (a, b)`). Ref(&'hir Pat<'hir>, Mutability), @@ -1209,7 +1254,7 @@ pub struct Stmt<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum StmtKind<'hir> { /// A local (`let`) binding. - Let(&'hir Local<'hir>), + Let(&'hir LetStmt<'hir>), /// An item binding. Item(ItemId), @@ -1223,7 +1268,7 @@ pub enum StmtKind<'hir> { /// Represents a `let` statement (i.e., `let : = ;`). #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct Local<'hir> { +pub struct LetStmt<'hir> { pub pat: &'hir Pat<'hir>, /// Type annotation, if any (otherwise the type will be inferred). pub ty: Option<&'hir Ty<'hir>>, @@ -1253,13 +1298,13 @@ pub struct Arm<'hir> { pub body: &'hir Expr<'hir>, } -/// Represents a `let [: ] = ` expression (not a [`Local`]), occurring in an `if-let` +/// Represents a `let [: ] = ` expression (not a [`LetStmt`]), occurring in an `if-let` /// or `let-else`, evaluating to a boolean. Typically the pattern is refutable. /// /// In an `if let`, imagine it as `if (let = ) { ... }`; in a let-else, it is part of /// the desugaring to if-let. Only let-else supports the type annotation at present. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct Let<'hir> { +pub struct LetExpr<'hir> { pub span: Span, pub pat: &'hir Pat<'hir>, pub ty: Option<&'hir Ty<'hir>>, @@ -1765,6 +1810,44 @@ impl Expr<'_> { } } + /// Whether this and the `other` expression are the same for purposes of an indexing operation. + /// + /// This is only used for diagnostics to see if we have things like `foo[i]` where `foo` is + /// borrowed multiple times with `i`. + pub fn equivalent_for_indexing(&self, other: &Expr<'_>) -> bool { + match (self.kind, other.kind) { + (ExprKind::Lit(lit1), ExprKind::Lit(lit2)) => lit1.node == lit2.node, + ( + ExprKind::Path(QPath::LangItem(item1, _)), + ExprKind::Path(QPath::LangItem(item2, _)), + ) => item1 == item2, + ( + ExprKind::Path(QPath::Resolved(None, path1)), + ExprKind::Path(QPath::Resolved(None, path2)), + ) => path1.res == path2.res, + ( + ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val1], None), + ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, _), [val2], None), + ) + | ( + ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val1], None), + ExprKind::Struct(QPath::LangItem(LangItem::RangeToInclusive, _), [val2], None), + ) + | ( + ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val1], None), + ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, _), [val2], None), + ) => val1.expr.equivalent_for_indexing(val2.expr), + ( + ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val1, val3], None), + ExprKind::Struct(QPath::LangItem(LangItem::Range, _), [val2, val4], None), + ) => { + val1.expr.equivalent_for_indexing(val2.expr) + && val3.expr.equivalent_for_indexing(val4.expr) + } + _ => false, + } + } + pub fn method_ident(&self) -> Option { match self.kind { ExprKind::MethodCall(receiver_method, ..) => Some(receiver_method.ident), @@ -1845,14 +1928,14 @@ pub enum ExprKind<'hir> { /// Wraps the expression in a terminating scope. /// This makes it semantically equivalent to `{ let _t = expr; _t }`. /// - /// This construct only exists to tweak the drop order in HIR lowering. + /// This construct only exists to tweak the drop order in AST lowering. /// An example of that is the desugaring of `for` loops. DropTemps(&'hir Expr<'hir>), /// A `let $pat = $expr` expression. /// - /// These are not `Local` and only occur as expressions. + /// These are not [`LetStmt`] and only occur as expressions. /// The `let Some(x) = foo()` in `if let Some(x) = foo()` is an example of `Let(..)`. - Let(&'hir Let<'hir>), + Let(&'hir LetExpr<'hir>), /// An `if` block, with an optional else block. /// /// I.e., `if { } else { }`. @@ -2004,6 +2087,8 @@ pub enum LocalSource { pub enum MatchSource { /// A `match _ { .. }`. Normal, + /// A `expr.match { .. }`. + Postfix, /// A desugared `for _ in _ { .. }` loop. ForLoopDesugar, /// A desugared `?` operator. @@ -2020,6 +2105,7 @@ impl MatchSource { use MatchSource::*; match self { Normal => "match", + Postfix => ".match", ForLoopDesugar => "for", TryDesugar(_) => "?", AwaitDesugar => ".await", @@ -2049,7 +2135,7 @@ impl LoopSource { } } -#[derive(Copy, Clone, Debug, HashStable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, HashStable_Generic)] pub enum LoopIdError { OutsideLoopScope, UnlabeledCfInWhileCondition, @@ -2275,21 +2361,15 @@ pub enum ImplItemKind<'hir> { Type(&'hir Ty<'hir>), } -/// Bind a type to an associated type (i.e., `A = Foo`). -/// -/// Bindings like `A: Debug` are represented as a special type `A = -/// $::Debug` that is understood by the astconv code. +/// An associated item binding. /// -/// FIXME(alexreg): why have a separate type for the binding case, -/// wouldn't it be better to make the `ty` field an enum like the -/// following? +/// ### Examples /// -/// ```ignore (pseudo-rust) -/// enum TypeBindingKind { -/// Equals(...), -/// Binding(...), -/// } -/// ``` +/// * `Trait` +/// * `Trait = Ty>` +/// * `Trait` +/// * `Trait` (under feature `associated_const_equality`) +/// * `Trait` (under feature `return_type_notation`) #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct TypeBinding<'hir> { pub hir_id: HirId, @@ -2322,7 +2402,7 @@ impl<'hir> From for Term<'hir> { pub enum TypeBindingKind<'hir> { /// E.g., `Foo`. Constraint { bounds: &'hir [GenericBound<'hir>] }, - /// E.g., `Foo`, `Foo` + /// E.g., `Foo`. Equality { term: Term<'hir> }, } @@ -2550,11 +2630,27 @@ pub struct OpaqueTy<'hir> { /// originating from a trait method. This makes it so that the opaque is /// lowered as an associated type. pub in_trait: bool, + /// List of arguments captured via `impl use<'a, P, ...> Trait` syntax. + pub precise_capturing_args: Option<&'hir [PreciseCapturingArg<'hir>]>, } -#[derive(Copy, Clone, Debug, HashStable_Generic)] -pub struct AssocOpaqueTy { - // Add some data if necessary +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum PreciseCapturingArg<'hir> { + Lifetime(&'hir Lifetime), + /// Non-lifetime argument (type or const) + Param(PreciseCapturingNonLifetimeArg), +} + +/// We need to have a [`Node`] for the [`HirId`] that we attach the type/const param +/// resolution to. Lifetimes don't have this problem, and for them, it's actually +/// kind of detrimental to use a custom node type versus just using [`Lifetime`], +/// since resolve_bound_vars operates on `Lifetime`s. +// FIXME(precise_capturing): Investigate storing this as a path instead? +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct PreciseCapturingNonLifetimeArg { + pub hir_id: HirId, + pub ident: Ident, + pub res: Res, } /// From whence the opaque type came. @@ -2622,6 +2718,8 @@ pub enum TyKind<'hir> { Infer, /// Placeholder for a type that has failed to be defined. Err(rustc_span::ErrorGuaranteed), + /// Pattern types (`pattern_type!(u32 is 1..)`) + Pat(&'hir Ty<'hir>, &'hir Pat<'hir>), } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -2730,9 +2828,9 @@ pub enum ImplicitSelfKind { /// Represents a `fn x(mut self);`. Mut, /// Represents a `fn x(&self);`. - ImmRef, + RefImm, /// Represents a `fn x(&mut self);`. - MutRef, + RefMut, /// Represents when a function does not have a self argument or /// when a function has a `self: X` argument. None, @@ -3367,7 +3465,7 @@ pub enum OwnerNode<'hir> { TraitItem(&'hir TraitItem<'hir>), ImplItem(&'hir ImplItem<'hir>), Crate(&'hir Mod<'hir>), - AssocOpaqueTy(&'hir AssocOpaqueTy), + Synthetic, } impl<'hir> OwnerNode<'hir> { @@ -3377,7 +3475,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::ForeignItem(ForeignItem { ident, .. }) | OwnerNode::ImplItem(ImplItem { ident, .. }) | OwnerNode::TraitItem(TraitItem { ident, .. }) => Some(*ident), - OwnerNode::Crate(..) | OwnerNode::AssocOpaqueTy(..) => None, + OwnerNode::Crate(..) | OwnerNode::Synthetic => None, } } @@ -3390,7 +3488,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::ImplItem(ImplItem { span, .. }) | OwnerNode::TraitItem(TraitItem { span, .. }) => span, OwnerNode::Crate(Mod { spans: ModSpans { inner_span, .. }, .. }) => inner_span, - OwnerNode::AssocOpaqueTy(..) => unreachable!(), + OwnerNode::Synthetic => unreachable!(), } } @@ -3449,7 +3547,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::ImplItem(ImplItem { owner_id, .. }) | OwnerNode::ForeignItem(ForeignItem { owner_id, .. }) => *owner_id, OwnerNode::Crate(..) => crate::CRATE_HIR_ID.owner, - OwnerNode::AssocOpaqueTy(..) => unreachable!(), + OwnerNode::Synthetic => unreachable!(), } } @@ -3493,7 +3591,7 @@ impl<'hir> Into> for OwnerNode<'hir> { OwnerNode::ImplItem(n) => Node::ImplItem(n), OwnerNode::TraitItem(n) => Node::TraitItem(n), OwnerNode::Crate(n) => Node::Crate(n), - OwnerNode::AssocOpaqueTy(n) => Node::AssocOpaqueTy(n), + OwnerNode::Synthetic => Node::Synthetic, } } } @@ -3520,7 +3618,7 @@ pub enum Node<'hir> { PatField(&'hir PatField<'hir>), Arm(&'hir Arm<'hir>), Block(&'hir Block<'hir>), - Local(&'hir Local<'hir>), + LetStmt(&'hir LetStmt<'hir>), /// `Ctor` refers to the constructor of an enum variant or struct. Only tuple or unit variants /// with synthesized constructors. Ctor(&'hir VariantData<'hir>), @@ -3531,7 +3629,9 @@ pub enum Node<'hir> { WhereBoundPredicate(&'hir WhereBoundPredicate<'hir>), // FIXME: Merge into `Node::Infer`. ArrayLenInfer(&'hir InferArg), - AssocOpaqueTy(&'hir AssocOpaqueTy), + PreciseCapturingNonLifetimeArg(&'hir PreciseCapturingNonLifetimeArg), + // Created by query feeding + Synthetic, // Span by reference to minimize `Node`'s size #[allow(rustc::pass_by_value)] Err(&'hir Span), @@ -3566,6 +3666,7 @@ impl<'hir> Node<'hir> { Node::TypeBinding(b) => Some(b.ident), Node::PatField(f) => Some(f.ident), Node::ExprField(f) => Some(f.ident), + Node::PreciseCapturingNonLifetimeArg(a) => Some(a.ident), Node::Param(..) | Node::AnonConst(..) | Node::ConstBlock(..) @@ -3575,14 +3676,14 @@ impl<'hir> Node<'hir> { | Node::Ctor(..) | Node::Pat(..) | Node::Arm(..) - | Node::Local(..) + | Node::LetStmt(..) | Node::Crate(..) | Node::Ty(..) | Node::TraitRef(..) | Node::Infer(..) | Node::WhereBoundPredicate(..) | Node::ArrayLenInfer(..) - | Node::AssocOpaqueTy(..) + | Node::Synthetic | Node::Err(..) => None, } } @@ -3640,35 +3741,42 @@ impl<'hir> Node<'hir> { } } - pub fn body_id(&self) -> Option { + #[inline] + pub fn associated_body(&self) -> Option<(LocalDefId, BodyId)> { match self { Node::Item(Item { + owner_id, kind: - ItemKind::Static(_, _, body) - | ItemKind::Const(_, _, body) - | ItemKind::Fn(_, _, body), + ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), .. }) | Node::TraitItem(TraitItem { + owner_id, kind: - TraitItemKind::Fn(_, TraitFn::Provided(body)) | TraitItemKind::Const(_, Some(body)), + TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)), .. }) | Node::ImplItem(ImplItem { - kind: ImplItemKind::Fn(_, body) | ImplItemKind::Const(_, body), + owner_id, + kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body), .. - }) - | Node::Expr(Expr { - kind: - ExprKind::ConstBlock(ConstBlock { body, .. }) - | ExprKind::Closure(Closure { body, .. }) - | ExprKind::Repeat(_, ArrayLen::Body(AnonConst { body, .. })), - .. - }) => Some(*body), + }) => Some((owner_id.def_id, *body)), + + Node::Expr(Expr { kind: ExprKind::Closure(Closure { def_id, body, .. }), .. }) => { + Some((*def_id, *body)) + } + + Node::AnonConst(constant) => Some((constant.def_id, constant.body)), + Node::ConstBlock(constant) => Some((constant.def_id, constant.body)), + _ => None, } } + pub fn body_id(&self) -> Option { + Some(self.associated_body()?.1) + } + pub fn generics(self) -> Option<&'hir Generics<'hir>> { match self { Node::ForeignItem(ForeignItem { @@ -3688,7 +3796,7 @@ impl<'hir> Node<'hir> { Node::TraitItem(i) => Some(OwnerNode::TraitItem(i)), Node::ImplItem(i) => Some(OwnerNode::ImplItem(i)), Node::Crate(i) => Some(OwnerNode::Crate(i)), - Node::AssocOpaqueTy(i) => Some(OwnerNode::AssocOpaqueTy(i)), + Node::Synthetic => Some(OwnerNode::Synthetic), _ => None, } } @@ -3740,7 +3848,7 @@ impl<'hir> Node<'hir> { expect_pat_field, &'hir PatField<'hir>, Node::PatField(n), n; expect_arm, &'hir Arm<'hir>, Node::Arm(n), n; expect_block, &'hir Block<'hir>, Node::Block(n), n; - expect_local, &'hir Local<'hir>, Node::Local(n), n; + expect_let_stmt, &'hir LetStmt<'hir>, Node::LetStmt(n), n; expect_ctor, &'hir VariantData<'hir>, Node::Ctor(n), n; expect_lifetime, &'hir Lifetime, Node::Lifetime(n), n; expect_generic_param, &'hir GenericParam<'hir>, Node::GenericParam(n), n; @@ -3751,9 +3859,10 @@ impl<'hir> Node<'hir> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; + use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(Block<'_>, 48); static_assert_size!(Body<'_>, 24); @@ -3770,7 +3879,7 @@ mod size_asserts { static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); static_assert_size!(ItemKind<'_>, 56); - static_assert_size!(Local<'_>, 64); + static_assert_size!(LetStmt<'_>, 64); static_assert_size!(Param<'_>, 32); static_assert_size!(Pat<'_>, 72); static_assert_size!(Path<'_>, 40); diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index d339075c171d1..ac48746950709 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -1,5 +1,6 @@ use crate::def_id::{DefId, DefIndex, LocalDefId, CRATE_DEF_ID}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::{def_id::DefPathHash, HashStableContext}; use std::fmt::{self, Debug}; @@ -17,7 +18,7 @@ impl Debug for OwnerId { impl From for HirId { fn from(owner: OwnerId) -> HirId { - HirId { owner, local_id: ItemLocalId::from_u32(0) } + HirId { owner, local_id: ItemLocalId::ZERO } } } @@ -110,7 +111,7 @@ impl HirId { #[inline] pub fn make_owner(owner: LocalDefId) -> Self { - Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::from_u32(0) } + Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO } } pub fn index(self) -> (usize, usize) { @@ -172,6 +173,6 @@ unsafe impl StableOrd for ItemLocalId { /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. pub const CRATE_HIR_ID: HirId = - HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::from_u32(0) }; + HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::ZERO }; pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index fbbad38d17f10..cd9f9ff9109c1 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -320,7 +320,7 @@ pub trait Visitor<'v>: Sized { fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) -> Self::Result { walk_foreign_item(self, i) } - fn visit_local(&mut self, l: &'v Local<'v>) -> Self::Result { + fn visit_local(&mut self, l: &'v LetStmt<'v>) -> Self::Result { walk_local(self, l) } fn visit_block(&mut self, b: &'v Block<'v>) -> Self::Result { @@ -356,6 +356,11 @@ pub trait Visitor<'v>: Sized { fn visit_ty(&mut self, t: &'v Ty<'v>) -> Self::Result { walk_ty(self, t) } + fn visit_pattern_type_pattern(&mut self, _p: &'v Pat<'v>) { + // Do nothing. Only a few visitors need to know the details of the pattern type, + // and they opt into it. All other visitors will just choke on our fake patterns + // because they aren't in a body. + } fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) -> Self::Result { walk_generic_param(self, p) } @@ -408,6 +413,9 @@ pub trait Visitor<'v>: Sized { fn visit_param_bound(&mut self, bounds: &'v GenericBound<'v>) -> Self::Result { walk_param_bound(self, bounds) } + fn visit_precise_capturing_arg(&mut self, arg: &'v PreciseCapturingArg<'v>) -> Self::Result { + walk_precise_capturing_arg(self, arg) + } fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef<'v>) -> Self::Result { walk_poly_trait_ref(self, t) } @@ -521,10 +529,15 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_ty(ty)); try_visit!(visitor.visit_generics(generics)); } - ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => { + ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, precise_capturing_args, .. }) => { try_visit!(visitor.visit_id(item.hir_id())); try_visit!(walk_generics(visitor, generics)); walk_list!(visitor, visit_param_bound, bounds); + if let Some(precise_capturing_args) = precise_capturing_args { + for arg in precise_capturing_args { + try_visit!(visitor.visit_precise_capturing_arg(arg)); + } + } } ItemKind::Enum(ref enum_definition, ref generics) => { try_visit!(visitor.visit_generics(generics)); @@ -606,7 +619,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( V::Result::output() } -pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) -> V::Result { +pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v LetStmt<'v>) -> V::Result { // Intentionally visiting the expr first - the initialization expr // dominates the local's definition. visit_opt!(visitor, visit_expr, local.init); @@ -660,7 +673,9 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: PatKind::Tuple(tuple_elements, _) => { walk_list!(visitor, visit_pat, tuple_elements); } - PatKind::Box(ref subpattern) | PatKind::Ref(ref subpattern, _) => { + PatKind::Box(ref subpattern) + | PatKind::Deref(ref subpattern) + | PatKind::Ref(ref subpattern, _) => { try_visit!(visitor.visit_pat(subpattern)); } PatKind::Binding(_, _hir_id, ident, ref optional_subpattern) => { @@ -753,7 +768,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::DropTemps(ref subexpression) => { try_visit!(visitor.visit_expr(subexpression)); } - ExprKind::Let(Let { span: _, pat, ty, init, is_recovered: _ }) => { + ExprKind::Let(LetExpr { span: _, pat, ty, init, is_recovered: _ }) => { // match the visit order in walk_local try_visit!(visitor.visit_expr(init)); try_visit!(visitor.visit_pat(pat)); @@ -880,6 +895,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul TyKind::AnonAdt(item_id) => { try_visit!(visitor.visit_nested_item(item_id)); } + TyKind::Pat(ty, pat) => { + try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_pattern_type_pattern(pat)); + } } V::Result::output() } @@ -899,7 +918,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( GenericParamKind::Const { ref ty, ref default, is_host_effect: _ } => { try_visit!(visitor.visit_ty(ty)); if let Some(ref default) = default { - visitor.visit_const_param_default(param.hir_id, default); + try_visit!(visitor.visit_const_param_default(param.hir_id, default)); } } } @@ -1126,6 +1145,16 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>( } } +pub fn walk_precise_capturing_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + arg: &'v PreciseCapturingArg<'v>, +) -> V::Result { + match *arg { + PreciseCapturingArg::Lifetime(lt) => visitor.visit_lifetime(lt), + PreciseCapturingArg::Param(param) => visitor.visit_id(param.hir_id), + } +} + pub fn walk_poly_trait_ref<'v, V: Visitor<'v>>( visitor: &mut V, trait_ref: &'v PolyTraitRef<'v>, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 5118bf5c3b7ab..e870a04127ada 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -1,4 +1,4 @@ -//! Defines language items. +//! Defines lang items. //! //! Language items are items that represent concepts intrinsic to the language //! itself. Examples are: @@ -12,11 +12,11 @@ use crate::{MethodKind, Target}; use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -/// All of the language items, defined or not. +/// All of the lang items, defined or not. /// Defined lang items can come from the current crate or its dependencies. #[derive(HashStable_Generic, Debug)] pub struct LanguageItems { @@ -56,8 +56,8 @@ macro_rules! language_item_table { $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )* ) => { - enum_from_u32! { - /// A representation of all the valid language items in Rust. + rustc_data_structures::enum_from_u32! { + /// A representation of all the valid lang items in Rust. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum LangItem { $( @@ -162,10 +162,22 @@ language_item_table! { Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None; Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None; + AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::Exact(0); + AsyncDestruct, sym::async_destruct, async_destruct_trait, Target::Trait, GenericRequirement::Exact(0); + AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); + SurfaceAsyncDropInPlace, sym::surface_async_drop_in_place, surface_async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropSurfaceDropInPlace, sym::async_drop_surface_drop_in_place, async_drop_surface_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2); + AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0); + AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3); + CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); - // language items relating to transmutability + // lang items relating to transmutability TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(2); @@ -199,6 +211,7 @@ language_item_table! { Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); + DerefPure, sym::deref_pure, deref_pure_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; @@ -214,6 +227,7 @@ language_item_table! { FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0); + FusedIterator, sym::fused_iterator, fused_iterator_trait, Target::Trait, GenericRequirement::Exact(0); Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0); @@ -224,6 +238,7 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; + OrderingEnum, sym::Ordering, ordering_enum, Target::Enum, GenericRequirement::Exact(0); PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; @@ -246,6 +261,25 @@ language_item_table! { PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0); PanicInCleanup, sym::panic_in_cleanup, panic_in_cleanup, Target::Fn, GenericRequirement::Exact(0); + /// Constant panic messages, used for codegen of MIR asserts. + PanicAddOverflow, sym::panic_const_add_overflow, panic_const_add_overflow, Target::Fn, GenericRequirement::None; + PanicSubOverflow, sym::panic_const_sub_overflow, panic_const_sub_overflow, Target::Fn, GenericRequirement::None; + PanicMulOverflow, sym::panic_const_mul_overflow, panic_const_mul_overflow, Target::Fn, GenericRequirement::None; + PanicDivOverflow, sym::panic_const_div_overflow, panic_const_div_overflow, Target::Fn, GenericRequirement::None; + PanicRemOverflow, sym::panic_const_rem_overflow, panic_const_rem_overflow, Target::Fn, GenericRequirement::None; + PanicNegOverflow, sym::panic_const_neg_overflow, panic_const_neg_overflow, Target::Fn, GenericRequirement::None; + PanicShrOverflow, sym::panic_const_shr_overflow, panic_const_shr_overflow, Target::Fn, GenericRequirement::None; + PanicShlOverflow, sym::panic_const_shl_overflow, panic_const_shl_overflow, Target::Fn, GenericRequirement::None; + PanicDivZero, sym::panic_const_div_by_zero, panic_const_div_by_zero, Target::Fn, GenericRequirement::None; + PanicRemZero, sym::panic_const_rem_by_zero, panic_const_rem_by_zero, Target::Fn, GenericRequirement::None; + PanicCoroutineResumed, sym::panic_const_coroutine_resumed, panic_const_coroutine_resumed, Target::Fn, GenericRequirement::None; + PanicAsyncFnResumed, sym::panic_const_async_fn_resumed, panic_const_async_fn_resumed, Target::Fn, GenericRequirement::None; + PanicAsyncGenFnResumed, sym::panic_const_async_gen_fn_resumed, panic_const_async_gen_fn_resumed, Target::Fn, GenericRequirement::None; + PanicGenFnNone, sym::panic_const_gen_fn_none, panic_const_gen_fn_none, Target::Fn, GenericRequirement::None; + PanicCoroutineResumedPanic, sym::panic_const_coroutine_resumed_panic, panic_const_coroutine_resumed_panic, Target::Fn, GenericRequirement::None; + PanicAsyncFnResumedPanic, sym::panic_const_async_fn_resumed_panic, panic_const_async_fn_resumed_panic, Target::Fn, GenericRequirement::None; + PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None; + PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None; /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; @@ -259,6 +293,7 @@ language_item_table! { ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); + FallbackSurfaceDrop, sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); @@ -269,7 +304,7 @@ language_item_table! { OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1); GlobalAlloc, sym::global_alloc_ty, global_alloc_ty, Target::Struct, GenericRequirement::None; - // Experimental language item for Miri + // Experimental lang item for Miri PtrUnique, sym::ptr_unique, ptr_unique, Target::Struct, GenericRequirement::Exact(1); PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 9921686ce289c..600a0dce03b94 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -5,21 +5,11 @@ #![feature(associated_type_defaults)] #![feature(closure_track_caller)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![feature(never_type)] #![feature(rustc_attrs)] #![feature(variant_count)] #![allow(internal_features)] -#[macro_use] -extern crate rustc_macros; - -#[macro_use] -extern crate tracing; - -#[macro_use] -extern crate rustc_data_structures; - extern crate self as rustc_hir; mod arena; diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index 1eaab3d2acac3..9991b02b1e120 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -1,6 +1,6 @@ use crate::def::{CtorOf, DefKind, Res}; use crate::def_id::{DefId, DefIdSet}; -use crate::hir::{self, BindingAnnotation, ByRef, HirId, PatKind}; +use crate::hir::{self, BindingMode, ByRef, HirId, PatKind}; use rustc_span::symbol::Ident; use rustc_span::Span; @@ -60,7 +60,7 @@ impl EnumerateAndAdjustIterator for T { impl hir::Pat<'_> { /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` - pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) { + pub fn each_binding(&self, mut f: impl FnMut(hir::BindingMode, HirId, Span, Ident)) { self.walk_always(|p| { if let PatKind::Binding(binding_mode, _, ident, _) = p.kind { f(binding_mode, p.hir_id, p.span, ident); @@ -74,10 +74,7 @@ impl hir::Pat<'_> { /// When encountering an or-pattern `p_0 | ... | p_n` only the first non-never pattern will be /// visited. If they're all never patterns we visit nothing, which is ok since a never pattern /// cannot have bindings. - pub fn each_binding_or_first( - &self, - f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident), - ) { + pub fn each_binding_or_first(&self, f: &mut impl FnMut(hir::BindingMode, HirId, Span, Ident)) { self.walk(|p| match &p.kind { PatKind::Or(ps) => { for p in *ps { @@ -98,7 +95,7 @@ impl hir::Pat<'_> { pub fn simple_ident(&self) -> Option { match self.kind { - PatKind::Binding(BindingAnnotation(ByRef::No, _), _, ident, None) => Some(ident), + PatKind::Binding(BindingMode(ByRef::No, _), _, ident, None) => Some(ident), _ => None, } } @@ -135,8 +132,8 @@ impl hir::Pat<'_> { pub fn contains_explicit_ref_binding(&self) -> Option { let mut result = None; self.each_binding(|annotation, _, _, _| match annotation { - hir::BindingAnnotation::REF if result.is_none() => result = Some(hir::Mutability::Not), - hir::BindingAnnotation::REF_MUT => result = Some(hir::Mutability::Mut), + hir::BindingMode::REF if result.is_none() => result = Some(hir::Mutability::Not), + hir::BindingMode::REF_MUT => result = Some(hir::Mutability::Mut), _ => {} }); result diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs index 74b8e88a97790..571923b546293 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -14,7 +14,7 @@ fn def_path_hash_depends_on_crate_id() { // the crate by changing the crate disambiguator (e.g. via bumping the // crate's version number). - create_session_globals_then(Edition::Edition2024, || { + create_session_globals_then(Edition::Edition2024, None, || { let id0 = StableCrateId::new(Symbol::intern("foo"), false, vec!["1".to_string()], ""); let id1 = StableCrateId::new(Symbol::intern("foo"), false, vec!["2".to_string()], ""); diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 1c68de51ba6a0..3edea0191faf1 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -37,6 +37,8 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh .label = deref recursion limit reached .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) +hir_analysis_bad_precise_capture = expected {$kind} parameter in `use<...>` precise captures list, found {$found} + hir_analysis_cannot_capture_late_bound_const = cannot capture late-bound const parameter in {$what} .label = parameter defined here @@ -111,6 +113,9 @@ hir_analysis_drop_impl_on_wrong_item = hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported +hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice + .label = parameter captured again here + hir_analysis_empty_specialization = specialization impl does not specialize any associated items .note = impl is a specialization of this impl @@ -118,6 +123,11 @@ hir_analysis_enum_discriminant_overflowed = enum discriminant overflowed .label = overflowed on value after {$discr} .note = explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome +hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding = + the type of the associated constant `{$assoc_const}` cannot capture late-bound generic parameters + .label = its type cannot capture the late-bound {$var_def_kind} `{$var_name}` + .var_defined_here_label = the late-bound {$var_def_kind} `{$var_name}` is defined here + hir_analysis_field_already_declared = field `{$field_name}` is already declared .label = field already declared @@ -191,6 +201,12 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a .help = consider moving this inherent impl into the crate defining the type if possible .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items +hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty}` + .note = type of `self` must be `Self` or a type that dereferences to it + +hir_analysis_invalid_receiver_ty_help = + consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) + hir_analysis_invalid_union_field = field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union .note = union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` @@ -209,6 +225,13 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl .label = type parameter declared here +hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters + .label = move the lifetime before this parameter + +hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + .label = lifetime captured due to being mentioned in the bounds of the `impl Trait` + .param_label = this lifetime parameter is captured + hir_analysis_lifetimes_or_bounds_mismatch_on_trait = lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration .label = lifetimes do not match {$item_kind} in trait @@ -290,6 +313,8 @@ hir_analysis_not_supported_delegation = {$descr} is not supported yet .label = callee defined here +hir_analysis_only_current_traits_adt = `{$name}` is not defined in the current crate + hir_analysis_only_current_traits_arbitrary = only traits defined in the current crate can be implemented for arbitrary types hir_analysis_only_current_traits_foreign = this is not defined in the current crate because this is a foreign trait @@ -316,6 +341,26 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur .label = `impl Trait` implicitly captures all lifetimes in scope .note = lifetime declared here +hir_analysis_param_in_ty_of_assoc_const_binding = + the type of the associated constant `{$assoc_const}` must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] generic parameters + } + .label = its type must not depend on {$param_category -> + [self] `Self` + [synthetic] `impl Trait` + *[normal] the {$param_def_kind} `{$param_name}` + } + .param_defined_here_label = {$param_category -> + [synthetic] the `impl Trait` is specified here + *[normal] the {$param_def_kind} `{$param_name}` is defined here + } + +hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope in `use<...>` + .label = {$kind} parameter is implicitly captured by this `impl Trait` + .note = currently, all {$kind} parameters are required to be mentioned in the precise captures list + hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it @@ -326,9 +371,18 @@ hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` .help = cast the value to `{$cast_ty}` +hir_analysis_pattern_type_non_const_range = "range patterns must have constant range start and end" +hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pattern types" + .label = "this type is the same as the inner type without a pattern" hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures +hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias + .label = `Self` is not a generic argument, but an alias to the type of the {$what} + +hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` + .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` + hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` hir_analysis_return_type_notation_equality_bound = @@ -357,6 +411,10 @@ hir_analysis_self_in_impl_self = `Self` is not valid in the self type of an impl block .note = replace `Self` with a different type +hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>` + .label = `Self` type parameter is implicitly captured by this `impl Trait` + .note = currently, all type parameters are required to be mentioned in the precise captures list + hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code .help = add `#![feature(simd_ffi)]` to the crate attributes to enable @@ -432,13 +490,15 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de .label = needs at most one field with non-trivial size or alignment, but has {$field_count} .labels = this field has non-zero size or requires alignment -hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) - .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`) +hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}` + +hir_analysis_ty_param_first_local = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`) + .label = type parameter `{$param}` must be covered by another type when it appears before the first local type (`{$local_type}`) .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type .case_note = in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last -hir_analysis_ty_param_some = type parameter `{$param_ty}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param_ty}>`) - .label = type parameter `{$param_ty}` must be used as the type parameter for some local type +hir_analysis_ty_param_some = type parameter `{$param}` must be used as the type parameter for some local type (e.g., `MyStruct<{$param}>`) + .label = type parameter `{$param}` must be used as the type parameter for some local type .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local .only_note = only traits defined in the current crate can be implemented for a type parameter diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs deleted file mode 100644 index c6942b0f4567b..0000000000000 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ /dev/null @@ -1,532 +0,0 @@ -use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{codes::*, struct_span_code_err}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::{self as ty, Ty}; -use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::traits; -use smallvec::SmallVec; - -use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter}; -use crate::bounds::Bounds; -use crate::errors; - -impl<'tcx> dyn AstConv<'tcx> + '_ { - /// Sets `implicitly_sized` to true on `Bounds` if necessary - pub(crate) fn add_implicitly_sized( - &self, - bounds: &mut Bounds<'tcx>, - self_ty: Ty<'tcx>, - ast_bounds: &'tcx [hir::GenericBound<'tcx>], - self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, - span: Span, - ) { - let tcx = self.tcx(); - let sized_def_id = tcx.lang_items().sized_trait(); - let mut seen_negative_sized_bound = false; - let mut seen_positive_sized_bound = false; - - // Try to find an unbound in bounds. - let mut unbounds: SmallVec<[_; 1]> = SmallVec::new(); - let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| { - for ab in ast_bounds { - let hir::GenericBound::Trait(ptr, modifier) = ab else { - continue; - }; - match modifier { - hir::TraitBoundModifier::Maybe => unbounds.push(ptr), - hir::TraitBoundModifier::Negative => { - if let Some(sized_def_id) = sized_def_id - && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) - { - seen_negative_sized_bound = true; - } - } - hir::TraitBoundModifier::None => { - if let Some(sized_def_id) = sized_def_id - && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) - { - seen_positive_sized_bound = true; - } - } - _ => {} - } - } - }; - search_bounds(ast_bounds); - if let Some((self_ty, where_clause)) = self_ty_where_predicates { - for clause in where_clause { - if let hir::WherePredicate::BoundPredicate(pred) = clause - && pred.is_param_bound(self_ty.to_def_id()) - { - search_bounds(pred.bounds); - } - } - } - - if unbounds.len() > 1 { - tcx.dcx().emit_err(errors::MultipleRelaxedDefaultBounds { - spans: unbounds.iter().map(|ptr| ptr.span).collect(), - }); - } - - let mut seen_sized_unbound = false; - for unbound in unbounds { - if let Some(sized_def_id) = sized_def_id - && unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) - { - seen_sized_unbound = true; - continue; - } - // There was a `?Trait` bound, but it was not `?Sized`; warn. - tcx.dcx().span_warn( - unbound.span, - "relaxing a default bound only does something for `?Sized`; \ - all other traits are not bound by default", - ); - } - - if seen_sized_unbound || seen_negative_sized_bound || seen_positive_sized_bound { - // There was in fact a `?Sized`, `!Sized` or explicit `Sized` bound; - // we don't need to do anything. - } else if sized_def_id.is_some() { - // There was no `?Sized`, `!Sized` or explicit `Sized` bound; - // add `Sized` if it's available. - bounds.push_sized(tcx, self_ty, span); - } - } - - /// This helper takes a *converted* parameter type (`param_ty`) - /// and an *unconverted* list of bounds: - /// - /// ```text - /// fn foo - /// ^ ^^^^^ `ast_bounds` parameter, in HIR form - /// | - /// `param_ty`, in ty form - /// ``` - /// - /// It adds these `ast_bounds` into the `bounds` structure. - /// - /// **A note on binders:** there is an implied binder around - /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` - /// for more details. - #[instrument(level = "debug", skip(self, ast_bounds, bounds))] - pub(crate) fn add_bounds<'hir, I: Iterator>>( - &self, - param_ty: Ty<'tcx>, - ast_bounds: I, - bounds: &mut Bounds<'tcx>, - bound_vars: &'tcx ty::List, - only_self_bounds: OnlySelfBounds, - ) where - 'tcx: 'hir, - { - for ast_bound in ast_bounds { - match ast_bound { - hir::GenericBound::Trait(poly_trait_ref, modifier) => { - let (constness, polarity) = match modifier { - hir::TraitBoundModifier::Const => { - (ty::BoundConstness::Const, ty::ImplPolarity::Positive) - } - hir::TraitBoundModifier::MaybeConst => { - (ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive) - } - hir::TraitBoundModifier::None => { - (ty::BoundConstness::NotConst, ty::ImplPolarity::Positive) - } - hir::TraitBoundModifier::Negative => { - (ty::BoundConstness::NotConst, ty::ImplPolarity::Negative) - } - hir::TraitBoundModifier::Maybe => continue, - }; - let _ = self.instantiate_poly_trait_ref( - &poly_trait_ref.trait_ref, - poly_trait_ref.span, - constness, - polarity, - param_ty, - bounds, - only_self_bounds, - ); - } - hir::GenericBound::Outlives(lifetime) => { - let region = self.ast_region_to_region(lifetime, None); - bounds.push_region_bound( - self.tcx(), - ty::Binder::bind_with_vars( - ty::OutlivesPredicate(param_ty, region), - bound_vars, - ), - lifetime.ident.span, - ); - } - } - } - } - - /// Translates a list of bounds from the HIR into the `Bounds` data structure. - /// The self-type for the bounds is given by `param_ty`. - /// - /// Example: - /// - /// ```ignore (illustrative) - /// fn foo() { } - /// // ^ ^^^^^^^^^ ast_bounds - /// // param_ty - /// ``` - /// - /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be - /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the - /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`. - /// - /// `span` should be the declaration size of the parameter. - pub(crate) fn compute_bounds( - &self, - param_ty: Ty<'tcx>, - ast_bounds: &[hir::GenericBound<'tcx>], - filter: PredicateFilter, - ) -> Bounds<'tcx> { - let mut bounds = Bounds::default(); - - let only_self_bounds = match filter { - PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { - OnlySelfBounds(false) - } - PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => OnlySelfBounds(true), - }; - - self.add_bounds( - param_ty, - ast_bounds.iter().filter(|bound| match filter { - PredicateFilter::All - | PredicateFilter::SelfOnly - | PredicateFilter::SelfAndAssociatedTypeBounds => true, - PredicateFilter::SelfThatDefines(assoc_name) => { - if let Some(trait_ref) = bound.trait_ref() - && let Some(trait_did) = trait_ref.trait_def_id() - && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) - { - true - } else { - false - } - } - }), - &mut bounds, - ty::List::empty(), - only_self_bounds, - ); - debug!(?bounds); - - bounds - } - - /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates - /// onto `bounds`. - /// - /// **A note on binders:** given something like `T: for<'a> Iterator`, the - /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside* - /// the binder (e.g., `&'a u32`) and hence may reference bound regions. - #[instrument(level = "debug", skip(self, bounds, dup_bindings, path_span))] - pub(super) fn add_predicates_for_ast_type_binding( - &self, - hir_ref_id: hir::HirId, - trait_ref: ty::PolyTraitRef<'tcx>, - binding: &hir::TypeBinding<'tcx>, - bounds: &mut Bounds<'tcx>, - dup_bindings: &mut FxIndexMap, - path_span: Span, - only_self_bounds: OnlySelfBounds, - ) -> Result<(), ErrorGuaranteed> { - // Given something like `U: SomeTrait`, we want to produce a - // predicate like `::T = X`. This is somewhat - // subtle in the event that `T` is defined in a supertrait of - // `SomeTrait`, because in that case we need to upcast. - // - // That is, consider this case: - // - // ``` - // trait SubTrait: SuperTrait { } - // trait SuperTrait { type T; } - // - // ... B: SubTrait ... - // ``` - // - // We want to produce `>::T == foo`. - - let tcx = self.tcx(); - - let assoc_kind = if binding.gen_args.parenthesized - == hir::GenericArgsParentheses::ReturnTypeNotation - { - ty::AssocKind::Fn - } else if let hir::TypeBindingKind::Equality { term: hir::Term::Const(_) } = binding.kind { - ty::AssocKind::Const - } else { - ty::AssocKind::Type - }; - - let candidate = if self.trait_defines_associated_item_named( - trait_ref.def_id(), - assoc_kind, - binding.ident, - ) { - // Simple case: The assoc item is defined in the current trait. - trait_ref - } else { - // Otherwise, we have to walk through the supertraits to find - // one that does define it. - self.one_bound_for_assoc_item( - || traits::supertraits(tcx, trait_ref), - trait_ref.skip_binder().print_only_trait_name(), - None, - assoc_kind, - binding.ident, - path_span, - Some(binding), - )? - }; - - let (assoc_ident, def_scope) = - tcx.adjust_ident_and_get_scope(binding.ident, candidate.def_id(), hir_ref_id); - - // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` - // instead of calling `filter_by_name_and_kind` which would needlessly normalize the - // `assoc_ident` again and again. - let assoc_item = tcx - .associated_items(candidate.def_id()) - .filter_by_name_unhygienic(assoc_ident.name) - .find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) - .expect("missing associated item"); - - if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) { - let reported = tcx - .dcx() - .struct_span_err( - binding.span, - format!("{} `{}` is private", assoc_item.kind, binding.ident), - ) - .with_span_label(binding.span, format!("private {}", assoc_item.kind)) - .emit(); - self.set_tainted_by_errors(reported); - } - tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); - - dup_bindings - .entry(assoc_item.def_id) - .and_modify(|prev_span| { - tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified { - span: binding.span, - prev_span: *prev_span, - item_name: binding.ident, - def_path: tcx.def_path_str(assoc_item.container_id(tcx)), - }); - }) - .or_insert(binding.span); - - let projection_ty = if let ty::AssocKind::Fn = assoc_kind { - let mut emitted_bad_param_err = None; - // If we have an method return type bound, then we need to instantiate - // the method's early bound params with suitable late-bound params. - let mut num_bound_vars = candidate.bound_vars().len(); - let args = - candidate.skip_binder().args.extend_to(tcx, assoc_item.def_id, |param, _| { - let arg = match param.kind { - ty::GenericParamDefKind::Lifetime => ty::Region::new_bound( - tcx, - ty::INNERMOST, - ty::BoundRegion { - var: ty::BoundVar::from_usize(num_bound_vars), - kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name), - }, - ) - .into(), - ty::GenericParamDefKind::Type { .. } => { - let guar = *emitted_bad_param_err.get_or_insert_with(|| { - tcx.dcx().emit_err( - crate::errors::ReturnTypeNotationIllegalParam::Type { - span: path_span, - param_span: tcx.def_span(param.def_id), - }, - ) - }); - Ty::new_error(tcx, guar).into() - } - ty::GenericParamDefKind::Const { .. } => { - let guar = *emitted_bad_param_err.get_or_insert_with(|| { - tcx.dcx().emit_err( - crate::errors::ReturnTypeNotationIllegalParam::Const { - span: path_span, - param_span: tcx.def_span(param.def_id), - }, - ) - }); - let ty = tcx - .type_of(param.def_id) - .no_bound_vars() - .expect("ct params cannot have early bound vars"); - ty::Const::new_error(tcx, guar, ty).into() - } - }; - num_bound_vars += 1; - arg - }); - - // Next, we need to check that the return-type notation is being used on - // an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait). - let output = tcx.fn_sig(assoc_item.def_id).skip_binder().output(); - let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind() - && tcx.is_impl_trait_in_trait(alias_ty.def_id) - { - alias_ty - } else { - return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit { - span: binding.span, - ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output), - fn_span: tcx.hir().span_if_local(assoc_item.def_id), - note: (), - })); - }; - - // Finally, move the fn return type's bound vars over to account for the early bound - // params (and trait ref's late bound params). This logic is very similar to - // `rustc_middle::ty::predicate::Clause::instantiate_supertrait` - // and it's no coincidence why. - let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output); - let instantiation_output = ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args); - - let bound_vars = tcx.late_bound_vars(binding.hir_id); - ty::Binder::bind_with_vars(instantiation_output, bound_vars) - } else { - // Create the generic arguments for the associated type or constant by joining the - // parent arguments (the arguments of the trait) and the own arguments (the ones of - // the associated item itself) and construct an alias type using them. - let alias_ty = candidate.map_bound(|trait_ref| { - let ident = Ident::new(assoc_item.name, binding.ident.span); - let item_segment = hir::PathSegment { - ident, - hir_id: binding.hir_id, - res: Res::Err, - args: Some(binding.gen_args), - infer_args: false, - }; - - let alias_args = self.create_args_for_associated_item( - path_span, - assoc_item.def_id, - &item_segment, - trait_ref.args, - ); - debug!(?alias_args); - - // Note that we're indeed also using `AliasTy` (alias *type*) for associated - // *constants* to represent *const projections*. Alias *term* would be a more - // appropriate name but alas. - ty::AliasTy::new(tcx, assoc_item.def_id, alias_args) - }); - - // Provide the resolved type of the associated constant to `type_of(AnonConst)`. - if let hir::TypeBindingKind::Equality { term: hir::Term::Const(anon_const) } = - binding.kind - { - let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args)); - // Since the arguments passed to the alias type above may contain early-bound - // generic parameters, the instantiated type may contain some as well. - // Therefore wrap it in `EarlyBinder`. - // FIXME(fmease): Reject escaping late-bound vars. - tcx.feed_anon_const_type( - anon_const.def_id, - ty::EarlyBinder::bind(ty.skip_binder()), - ); - } - - alias_ty - }; - - match binding.kind { - hir::TypeBindingKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => { - return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound { - span: binding.span, - })); - } - hir::TypeBindingKind::Equality { term } => { - let term = match term { - hir::Term::Ty(ty) => self.ast_ty_to_ty(ty).into(), - hir::Term::Const(ct) => ty::Const::from_anon_const(tcx, ct.def_id).into(), - }; - - // Find any late-bound regions declared in `ty` that are not - // declared in the trait-ref or assoc_item. These are not well-formed. - // - // Example: - // - // for<'a> ::Item = &'a str // <-- 'a is bad - // for<'a> >::Output = &'a str // <-- 'a is ok - let late_bound_in_projection_ty = - tcx.collect_constrained_late_bound_regions(projection_ty); - let late_bound_in_term = - tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term)); - debug!(?late_bound_in_projection_ty); - debug!(?late_bound_in_term); - - // FIXME: point at the type params that don't have appropriate lifetimes: - // struct S1 Fn(&i32, &i32) -> &'a i32>(F); - // ---- ---- ^^^^^^^ - // NOTE(associated_const_equality): This error should be impossible to trigger - // with associated const equality bounds. - self.validate_late_bound_regions( - late_bound_in_projection_ty, - late_bound_in_term, - |br_name| { - struct_span_code_err!( - tcx.dcx(), - binding.span, - E0582, - "binding for associated type `{}` references {}, \ - which does not appear in the trait input types", - binding.ident, - br_name - ) - }, - ); - - // "Desugar" a constraint like `T: Iterator` this to - // the "projection predicate" for: - // - // `::Item = u32` - bounds.push_projection_bound( - tcx, - projection_ty - .map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }), - binding.span, - ); - } - hir::TypeBindingKind::Constraint { bounds: ast_bounds } => { - // "Desugar" a constraint like `T: Iterator` to - // - // `::Item: Debug` - // - // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` - // parameter to have a skipped binder. - // - // NOTE: If `only_self_bounds` is true, do NOT expand this associated - // type bound into a trait predicate, since we only want to add predicates - // for the `Self` type. - if !only_self_bounds.0 { - let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); - self.add_bounds( - param_ty, - ast_bounds.iter(), - bounds, - projection_ty.bound_vars(), - only_self_bounds, - ); - } - } - } - Ok(()) - } -} diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs deleted file mode 100644 index 37fbf45235a86..0000000000000 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ /dev/null @@ -1,1104 +0,0 @@ -use crate::astconv::AstConv; -use crate::errors::{ - self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, - ParenthesizedFnTraitExpansion, -}; -use crate::fluent_generated as fluent; -use crate::traits::error_reporting::report_object_safety_error; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::sorted_map::SortedMap; -use rustc_data_structures::unord::UnordMap; -use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, -}; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_infer::traits::FulfillmentError; -use rustc_middle::query::Key; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt}; -use rustc_session::parse::feature_err; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::{Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::traits::object_safety_violations_for_assoc_item; - -impl<'tcx> dyn AstConv<'tcx> + '_ { - /// On missing type parameters, emit an E0393 error and provide a structured suggestion using - /// the type parameter's name as a placeholder. - pub(crate) fn complain_about_missing_type_params( - &self, - missing_type_params: Vec, - def_id: DefId, - span: Span, - empty_generic_args: bool, - ) { - if missing_type_params.is_empty() { - return; - } - - self.tcx().dcx().emit_err(MissingTypeParams { - span, - def_span: self.tcx().def_span(def_id), - span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(), - missing_type_params, - empty_generic_args, - }); - } - - /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit - /// an error and attempt to build a reasonable structured suggestion. - pub(crate) fn complain_about_internal_fn_trait( - &self, - span: Span, - trait_def_id: DefId, - trait_segment: &'_ hir::PathSegment<'_>, - is_impl: bool, - ) { - if self.tcx().features().unboxed_closures { - return; - } - - let trait_def = self.tcx().trait_def(trait_def_id); - if !trait_def.paren_sugar { - if trait_segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar { - // For now, require that parenthetical notation be used only with `Fn()` etc. - feature_err( - &self.tcx().sess, - sym::unboxed_closures, - span, - "parenthetical notation is only stable when used with `Fn`-family traits", - ) - .emit(); - } - - return; - } - - let sess = self.tcx().sess; - - if trait_segment.args().parenthesized != hir::GenericArgsParentheses::ParenSugar { - // For now, require that parenthetical notation be used only with `Fn()` etc. - let mut err = feature_err( - sess, - sym::unboxed_closures, - span, - "the precise format of `Fn`-family traits' type parameters is subject to change", - ); - // Do not suggest the other syntax if we are in trait impl: - // the desugaring would contain an associated type constraint. - if !is_impl { - err.span_suggestion( - span, - "use parenthetical notation instead", - fn_trait_to_string(self.tcx(), trait_segment, true), - Applicability::MaybeIncorrect, - ); - } - err.emit(); - } - - if is_impl { - let trait_name = self.tcx().def_path_str(trait_def_id); - self.tcx().dcx().emit_err(ManualImplementation { span, trait_name }); - } - } - - pub(super) fn complain_about_assoc_item_not_found( - &self, - all_candidates: impl Fn() -> I, - ty_param_name: &str, - ty_param_def_id: Option, - assoc_kind: ty::AssocKind, - assoc_name: Ident, - span: Span, - binding: Option<&hir::TypeBinding<'tcx>>, - ) -> ErrorGuaranteed - where - I: Iterator>, - { - let tcx = self.tcx(); - - // First and foremost, provide a more user-friendly & “intuitive” error on kind mismatches. - if let Some(assoc_item) = all_candidates().find_map(|r| { - tcx.associated_items(r.def_id()) - .filter_by_name_unhygienic(assoc_name.name) - .find(|item| tcx.hygienic_eq(assoc_name, item.ident(tcx), r.def_id())) - }) { - return self.complain_about_assoc_kind_mismatch( - assoc_item, assoc_kind, assoc_name, span, binding, - ); - } - - let assoc_kind_str = super::assoc_kind_str(assoc_kind); - - // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a - // valid span, so we point at the whole path segment instead. - let is_dummy = assoc_name.span == DUMMY_SP; - - let mut err = errors::AssocItemNotFound { - span: if is_dummy { span } else { assoc_name.span }, - assoc_name, - assoc_kind: assoc_kind_str, - ty_param_name, - label: None, - sugg: None, - }; - - if is_dummy { - err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span }); - return tcx.dcx().emit_err(err); - } - - let all_candidate_names: Vec<_> = all_candidates() - .flat_map(|r| tcx.associated_items(r.def_id()).in_definition_order()) - .filter_map(|item| { - (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name) - }) - .collect(); - - if let Some(suggested_name) = - find_best_match_for_name(&all_candidate_names, assoc_name.name, None) - { - err.sugg = Some(errors::AssocItemNotFoundSugg::Similar { - span: assoc_name.span, - assoc_kind: assoc_kind_str, - suggested_name, - }); - return tcx.dcx().emit_err(err); - } - - // If we didn't find a good item in the supertraits (or couldn't get - // the supertraits), like in ItemCtxt, then look more generally from - // all visible traits. If there's one clear winner, just suggest that. - - let visible_traits: Vec<_> = tcx - .all_traits() - .filter(|trait_def_id| { - let viz = tcx.visibility(*trait_def_id); - let def_id = self.item_def_id(); - viz.is_accessible_from(def_id, tcx) - }) - .collect(); - - let wider_candidate_names: Vec<_> = visible_traits - .iter() - .flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order()) - .filter_map(|item| { - (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name) - }) - .collect(); - - if let Some(suggested_name) = - find_best_match_for_name(&wider_candidate_names, assoc_name.name, None) - { - if let [best_trait] = visible_traits - .iter() - .copied() - .filter(|trait_def_id| { - tcx.associated_items(trait_def_id) - .filter_by_name_unhygienic(suggested_name) - .any(|item| item.kind == assoc_kind) - }) - .collect::>()[..] - { - let trait_name = tcx.def_path_str(best_trait); - err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait { - span: assoc_name.span, - assoc_kind: assoc_kind_str, - trait_name: &trait_name, - suggested_name, - identically_named: suggested_name == assoc_name.name, - }); - let hir = tcx.hir(); - if let Some(def_id) = ty_param_def_id - && let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id)) - && let Some(generics) = hir.get_generics(parent.def_id) - { - if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( - |b| match b { - hir::GenericBound::Trait(t, ..) => { - t.trait_ref.trait_def_id() == Some(best_trait) - } - _ => false, - }, - ) { - // The type param already has a bound for `trait_name`, we just need to - // change the associated item. - err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { - span: assoc_name.span, - assoc_kind: assoc_kind_str, - suggested_name, - }); - return tcx.dcx().emit_err(err); - } - - let mut err = tcx.dcx().create_err(err); - if suggest_constraining_type_param( - tcx, - generics, - &mut err, - &ty_param_name, - &trait_name, - None, - None, - ) && suggested_name != assoc_name.name - { - // We suggested constraining a type parameter, but the associated item on it - // was also not an exact match, so we also suggest changing it. - err.span_suggestion_verbose( - assoc_name.span, - fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, - suggested_name, - Applicability::MaybeIncorrect, - ); - } - return err.emit(); - } - return tcx.dcx().emit_err(err); - } - } - - // If we still couldn't find any associated item, and only one associated item exists, - // suggests using it. - if let [candidate_name] = all_candidate_names.as_slice() { - // This should still compile, except on `#![feature(associated_type_defaults)]` - // where it could suggests `type A = Self::A`, thus recursing infinitely. - let applicability = - if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults { - Applicability::Unspecified - } else { - Applicability::MaybeIncorrect - }; - - err.sugg = Some(errors::AssocItemNotFoundSugg::Other { - span: assoc_name.span, - applicability, - ty_param_name, - assoc_kind: assoc_kind_str, - suggested_name: *candidate_name, - }); - } else { - err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span }); - } - - tcx.dcx().emit_err(err) - } - - fn complain_about_assoc_kind_mismatch( - &self, - assoc_item: &ty::AssocItem, - assoc_kind: ty::AssocKind, - ident: Ident, - span: Span, - binding: Option<&hir::TypeBinding<'tcx>>, - ) -> ErrorGuaranteed { - let tcx = self.tcx(); - - let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind - && let Some(binding) = binding - && let hir::TypeBindingKind::Constraint { .. } = binding.kind - { - let lo = if binding.gen_args.span_ext.is_dummy() { - ident.span - } else { - binding.gen_args.span_ext - }; - Some(lo.between(span.shrink_to_hi())) - } else { - None - }; - - // FIXME(associated_const_equality): This has quite a few false positives and negatives. - let wrap_in_braces_sugg = if let Some(binding) = binding - && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(hir_ty) } = binding.kind - && let ty = self.ast_ty_to_ty(hir_ty) - && (ty.is_enum() || ty.references_error()) - && tcx.features().associated_const_equality - { - Some(errors::AssocKindMismatchWrapInBracesSugg { - lo: hir_ty.span.shrink_to_lo(), - hi: hir_ty.span.shrink_to_hi(), - }) - } else { - None - }; - - // For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since - // one can argue that that's more “intuitive” to the user. - let (span, expected_because_label, expected, got) = if let Some(binding) = binding - && let hir::TypeBindingKind::Equality { term } = binding.kind - { - let span = match term { - hir::Term::Ty(ty) => ty.span, - hir::Term::Const(ct) => tcx.def_span(ct.def_id), - }; - (span, Some(ident.span), assoc_item.kind, assoc_kind) - } else { - (ident.span, None, assoc_kind, assoc_item.kind) - }; - - tcx.dcx().emit_err(errors::AssocKindMismatch { - span, - expected: super::assoc_kind_str(expected), - got: super::assoc_kind_str(got), - expected_because_label, - assoc_kind: super::assoc_kind_str(assoc_item.kind), - def_span: tcx.def_span(assoc_item.def_id), - bound_on_assoc_const_label, - wrap_in_braces_sugg, - }) - } - - pub(super) fn report_ambiguous_associated_type( - &self, - span: Span, - types: &[String], - traits: &[String], - name: Symbol, - ) -> ErrorGuaranteed { - let mut err = - struct_span_code_err!(self.tcx().dcx(), span, E0223, "ambiguous associated type"); - if self - .tcx() - .resolutions(()) - .confused_type_with_std_module - .keys() - .any(|full_span| full_span.contains(span)) - { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "you are looking for the module in `std`, not the primitive type", - "std::", - Applicability::MachineApplicable, - ); - } else { - let mut types = types.to_vec(); - types.sort(); - let mut traits = traits.to_vec(); - traits.sort(); - match (&types[..], &traits[..]) { - ([], []) => { - err.span_suggestion_verbose( - span, - format!( - "if there were a type named `Type` that implements a trait named \ - `Trait` with associated type `{name}`, you could use the \ - fully-qualified path", - ), - format!("::{name}"), - Applicability::HasPlaceholders, - ); - } - ([], [trait_str]) => { - err.span_suggestion_verbose( - span, - format!( - "if there were a type named `Example` that implemented `{trait_str}`, \ - you could use the fully-qualified path", - ), - format!("::{name}"), - Applicability::HasPlaceholders, - ); - } - ([], traits) => { - err.span_suggestions( - span, - format!( - "if there were a type named `Example` that implemented one of the \ - traits with associated type `{name}`, you could use the \ - fully-qualified path", - ), - traits - .iter() - .map(|trait_str| format!("::{name}")) - .collect::>(), - Applicability::HasPlaceholders, - ); - } - ([type_str], []) => { - err.span_suggestion_verbose( - span, - format!( - "if there were a trait named `Example` with associated type `{name}` \ - implemented for `{type_str}`, you could use the fully-qualified path", - ), - format!("<{type_str} as Example>::{name}"), - Applicability::HasPlaceholders, - ); - } - (types, []) => { - err.span_suggestions( - span, - format!( - "if there were a trait named `Example` with associated type `{name}` \ - implemented for one of the types, you could use the fully-qualified \ - path", - ), - types - .into_iter() - .map(|type_str| format!("<{type_str} as Example>::{name}")), - Applicability::HasPlaceholders, - ); - } - (types, traits) => { - let mut suggestions = vec![]; - for type_str in types { - for trait_str in traits { - suggestions.push(format!("<{type_str} as {trait_str}>::{name}")); - } - } - err.span_suggestions( - span, - "use fully-qualified syntax", - suggestions, - Applicability::MachineApplicable, - ); - } - } - } - let reported = err.emit(); - self.set_tainted_by_errors(reported); - reported - } - - pub(crate) fn complain_about_ambiguous_inherent_assoc_type( - &self, - name: Ident, - candidates: Vec, - span: Span, - ) -> ErrorGuaranteed { - let mut err = struct_span_code_err!( - self.tcx().dcx(), - name.span, - E0034, - "multiple applicable items in scope" - ); - err.span_label(name.span, format!("multiple `{name}` found")); - self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span); - let reported = err.emit(); - self.set_tainted_by_errors(reported); - reported - } - - // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. - fn note_ambiguous_inherent_assoc_type( - &self, - err: &mut Diag<'_>, - candidates: Vec, - span: Span, - ) { - let tcx = self.tcx(); - - // Dynamic limit to avoid hiding just one candidate, which is silly. - let limit = if candidates.len() == 5 { 5 } else { 4 }; - - for (index, &item) in candidates.iter().take(limit).enumerate() { - let impl_ = tcx.impl_of_method(item).unwrap(); - - let note_span = if item.is_local() { - Some(tcx.def_span(item)) - } else if impl_.is_local() { - Some(tcx.def_span(impl_)) - } else { - None - }; - - let title = if candidates.len() > 1 { - format!("candidate #{}", index + 1) - } else { - "the candidate".into() - }; - - let impl_ty = tcx.at(span).type_of(impl_).instantiate_identity(); - let note = format!("{title} is defined in an impl for the type `{impl_ty}`"); - - if let Some(span) = note_span { - err.span_note(span, note); - } else { - err.note(note); - } - } - if candidates.len() > limit { - err.note(format!("and {} others", candidates.len() - limit)); - } - } - - // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. - pub(crate) fn complain_about_inherent_assoc_type_not_found( - &self, - name: Ident, - self_ty: Ty<'tcx>, - candidates: Vec<(DefId, (DefId, DefId))>, - fulfillment_errors: Vec>, - span: Span, - ) -> ErrorGuaranteed { - // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`. - // Either - // * update this code by applying changes similar to #106702 or by taking a - // Vec<(DefId, (DefId, DefId), Option>>)> or - // * deduplicate this code across the two crates. - - let tcx = self.tcx(); - - let adt_did = self_ty.ty_adt_def().map(|def| def.did()); - let add_def_label = |err: &mut Diag<'_>| { - if let Some(did) = adt_did { - err.span_label( - tcx.def_span(did), - format!("associated item `{name}` not found for this {}", tcx.def_descr(did)), - ); - } - }; - - if fulfillment_errors.is_empty() { - // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. - - let limit = if candidates.len() == 5 { 5 } else { 4 }; - let type_candidates = candidates - .iter() - .take(limit) - .map(|&(impl_, _)| { - format!("- `{}`", tcx.at(span).type_of(impl_).instantiate_identity()) - }) - .collect::>() - .join("\n"); - let additional_types = if candidates.len() > limit { - format!("\nand {} more types", candidates.len() - limit) - } else { - String::new() - }; - - let mut err = struct_span_code_err!( - tcx.dcx(), - name.span, - E0220, - "associated type `{name}` not found for `{self_ty}` in the current scope" - ); - err.span_label(name.span, format!("associated item not found in `{self_ty}`")); - err.note(format!( - "the associated type was found for\n{type_candidates}{additional_types}", - )); - add_def_label(&mut err); - return err.emit(); - } - - let mut bound_spans: SortedMap> = Default::default(); - - let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { - let msg = format!("`{}`", if obligation.len() > 50 { quiet } else { obligation }); - match &self_ty.kind() { - // Point at the type that couldn't satisfy the bound. - ty::Adt(def, _) => { - bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) - } - // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { - for pred in preds.iter() { - match pred.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => { - bound_spans - .get_mut_or_insert_default(tcx.def_span(tr.def_id)) - .push(msg.clone()); - } - ty::ExistentialPredicate::Projection(_) - | ty::ExistentialPredicate::AutoTrait(_) => {} - } - } - } - // Point at the closure that couldn't satisfy the bound. - ty::Closure(def_id, _) => { - bound_spans - .get_mut_or_insert_default(tcx.def_span(*def_id)) - .push(format!("`{quiet}`")); - } - _ => {} - } - }; - - let format_pred = |pred: ty::Predicate<'tcx>| { - let bound_predicate = pred.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { - let pred = bound_predicate.rebind(pred); - // `::Item = String`. - let projection_ty = pred.skip_binder().projection_ty; - - let args_with_infer_self = tcx.mk_args_from_iter( - std::iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into()) - .chain(projection_ty.args.iter().skip(1)), - ); - - let quiet_projection_ty = - ty::AliasTy::new(tcx, projection_ty.def_id, args_with_infer_self); - - let term = pred.skip_binder().term; - - let obligation = format!("{projection_ty} = {term}"); - let quiet = format!("{quiet_projection_ty} = {term}"); - - bound_span_label(projection_ty.self_ty(), &obligation, &quiet); - Some((obligation, projection_ty.self_ty())) - } - ty::PredicateKind::Clause(ty::ClauseKind::Trait(poly_trait_ref)) => { - let p = poly_trait_ref.trait_ref; - let self_ty = p.self_ty(); - let path = p.print_only_trait_path(); - let obligation = format!("{self_ty}: {path}"); - let quiet = format!("_: {path}"); - bound_span_label(self_ty, &obligation, &quiet); - Some((obligation, self_ty)) - } - _ => None, - } - }; - - // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds. - // I would do the same here if it didn't mean more code duplication. - let mut bounds: Vec<_> = fulfillment_errors - .into_iter() - .map(|error| error.root_obligation.predicate) - .filter_map(format_pred) - .map(|(p, _)| format!("`{p}`")) - .collect(); - bounds.sort(); - bounds.dedup(); - - let mut err = tcx.dcx().struct_span_err( - name.span, - format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") - ); - if !bounds.is_empty() { - err.note(format!( - "the following trait bounds were not satisfied:\n{}", - bounds.join("\n") - )); - } - err.span_label( - name.span, - format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") - ); - - for (span, mut bounds) in bound_spans { - if !tcx.sess.source_map().is_span_accessible(span) { - continue; - } - bounds.sort(); - bounds.dedup(); - let msg = match &bounds[..] { - [bound] => format!("doesn't satisfy {bound}"), - bounds if bounds.len() > 4 => format!("doesn't satisfy {} bounds", bounds.len()), - [bounds @ .., last] => format!("doesn't satisfy {} or {last}", bounds.join(", ")), - [] => unreachable!(), - }; - err.span_label(span, msg); - } - add_def_label(&mut err); - err.emit() - } - - /// When there are any missing associated types, emit an E0191 error and attempt to supply a - /// reasonable suggestion on how to write it. For the case of multiple associated types in the - /// same trait bound have the same name (as they come from different supertraits), we instead - /// emit a generic note suggesting using a `where` clause to constraint instead. - pub(crate) fn complain_about_missing_associated_types( - &self, - associated_types: FxIndexMap>, - potential_assoc_types: Vec, - trait_bounds: &[hir::PolyTraitRef<'_>], - ) { - if associated_types.values().all(|v| v.is_empty()) { - return; - } - - let tcx = self.tcx(); - // FIXME: Marked `mut` so that we can replace the spans further below with a more - // appropriate one, but this should be handled earlier in the span assignment. - let mut associated_types: FxIndexMap> = associated_types - .into_iter() - .map(|(span, def_ids)| { - (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) - }) - .collect(); - let mut names: FxIndexMap> = Default::default(); - let mut names_len = 0; - - // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and - // `issue-22560.rs`. - let mut trait_bound_spans: Vec = vec![]; - let mut object_safety_violations = false; - for (span, items) in &associated_types { - if !items.is_empty() { - trait_bound_spans.push(*span); - } - for assoc_item in items { - let trait_def_id = assoc_item.container_id(tcx); - names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name); - names_len += 1; - - let violations = - object_safety_violations_for_assoc_item(tcx, trait_def_id, *assoc_item); - if !violations.is_empty() { - report_object_safety_error(tcx, *span, None, trait_def_id, &violations).emit(); - object_safety_violations = true; - } - } - } - if object_safety_violations { - return; - } - if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { - match bound.trait_ref.path.segments { - // FIXME: `trait_ref.path.span` can point to a full path with multiple - // segments, even though `trait_ref.path.segments` is of length `1`. Work - // around that bug here, even though it should be fixed elsewhere. - // This would otherwise cause an invalid suggestion. For an example, look at - // `tests/ui/issues/issue-28344.rs` where instead of the following: - // - // error[E0191]: the value of the associated type `Output` - // (from trait `std::ops::BitXor`) must be specified - // --> $DIR/issue-28344.rs:4:17 - // | - // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); - // | ^^^^^^ help: specify the associated type: - // | `BitXor` - // - // we would output: - // - // error[E0191]: the value of the associated type `Output` - // (from trait `std::ops::BitXor`) must be specified - // --> $DIR/issue-28344.rs:4:17 - // | - // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); - // | ^^^^^^^^^^^^^ help: specify the associated type: - // | `BitXor::bitor` - [segment] if segment.args.is_none() => { - trait_bound_spans = vec![segment.ident.span]; - associated_types = associated_types - .into_values() - .map(|items| (segment.ident.span, items)) - .collect(); - } - _ => {} - } - } - - // We get all the associated items that _are_ set, - // so that we can check if any of their names match one of the ones we are missing. - // This would mean that they are shadowing the associated type we are missing, - // and we can then use their span to indicate this to the user. - let bound_names = trait_bounds - .iter() - .filter_map(|poly_trait_ref| { - let path = poly_trait_ref.trait_ref.path.segments.last()?; - let args = path.args?; - - Some(args.bindings.iter().filter_map(|binding| { - let ident = binding.ident; - let trait_def = path.res.def_id(); - let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind( - tcx, - ident, - ty::AssocKind::Type, - trait_def, - ); - - Some((ident.name, assoc_item?)) - })) - }) - .flatten() - .collect::>(); - - let mut names = names - .into_iter() - .map(|(trait_, mut assocs)| { - assocs.sort(); - format!( - "{} in `{trait_}`", - match &assocs[..] { - [] => String::new(), - [only] => format!("`{only}`"), - [assocs @ .., last] => format!( - "{} and `{last}`", - assocs.iter().map(|a| format!("`{a}`")).collect::>().join(", ") - ), - } - ) - }) - .collect::>(); - names.sort(); - let names = names.join(", "); - - trait_bound_spans.sort(); - let mut err = struct_span_code_err!( - tcx.dcx(), - trait_bound_spans, - E0191, - "the value of the associated type{} {} must be specified", - pluralize!(names_len), - names, - ); - let mut suggestions = vec![]; - let mut types_count = 0; - let mut where_constraints = vec![]; - let mut already_has_generics_args_suggestion = false; - for (span, assoc_items) in &associated_types { - let mut names: UnordMap<_, usize> = Default::default(); - for item in assoc_items { - types_count += 1; - *names.entry(item.name).or_insert(0) += 1; - } - let mut dupes = false; - let mut shadows = false; - for item in assoc_items { - let prefix = if names[&item.name] > 1 { - let trait_def_id = item.container_id(tcx); - dupes = true; - format!("{}::", tcx.def_path_str(trait_def_id)) - } else if bound_names.get(&item.name).is_some_and(|x| x != &item) { - let trait_def_id = item.container_id(tcx); - shadows = true; - format!("{}::", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - - let mut is_shadowed = false; - - if let Some(assoc_item) = bound_names.get(&item.name) - && assoc_item != &item - { - is_shadowed = true; - - let rename_message = - if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" }; - err.span_label( - tcx.def_span(assoc_item.def_id), - format!("`{}{}` shadowed here{}", prefix, item.name, rename_message), - ); - } - - let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; - - if let Some(sp) = tcx.hir().span_if_local(item.def_id) { - err.span_label( - sp, - format!("`{}{}` defined here{}", prefix, item.name, rename_message), - ); - } - } - if potential_assoc_types.len() == assoc_items.len() { - // When the amount of missing associated types equals the number of - // extra type arguments present. A suggesting to replace the generic args with - // associated types is already emitted. - already_has_generics_args_suggestion = true; - } else if let (Ok(snippet), false, false) = - (tcx.sess.source_map().span_to_snippet(*span), dupes, shadows) - { - let types: Vec<_> = - assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect(); - let code = if snippet.ends_with('>') { - // The user wrote `Trait<'a>` or similar and we don't have a type we can - // suggest, but at least we can clue them to the correct syntax - // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the - // suggestion. - format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) - } else { - // The user wrote `Iterator`, so we don't have a type we can suggest, but at - // least we can clue them to the correct syntax `Iterator`. - format!("{}<{}>", snippet, types.join(", ")) - }; - suggestions.push((*span, code)); - } else if dupes { - where_constraints.push(*span); - } - } - let where_msg = "consider introducing a new type parameter, adding `where` constraints \ - using the fully-qualified path to the associated types"; - if !where_constraints.is_empty() && suggestions.is_empty() { - // If there are duplicates associated type names and a single trait bound do not - // use structured suggestion, it means that there are multiple supertraits with - // the same associated type name. - err.help(where_msg); - } - if suggestions.len() != 1 || already_has_generics_args_suggestion { - // We don't need this label if there's an inline suggestion, show otherwise. - for (span, assoc_items) in &associated_types { - let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); - for item in assoc_items { - types_count += 1; - *names.entry(item.name).or_insert(0) += 1; - } - let mut label = vec![]; - for item in assoc_items { - let postfix = if names[&item.name] > 1 { - let trait_def_id = item.container_id(tcx); - format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - label.push(format!("`{}`{}", item.name, postfix)); - } - if !label.is_empty() { - err.span_label( - *span, - format!( - "associated type{} {} must be specified", - pluralize!(label.len()), - label.join(", "), - ), - ); - } - } - } - suggestions.sort_by_key(|&(span, _)| span); - // There are cases where one bound points to a span within another bound's span, like when - // you have code like the following (#115019), so we skip providing a suggestion in those - // cases to avoid having a malformed suggestion. - // - // pub struct Flatten { - // inner: >::IntoIterator as Item>::core, - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - // | ^^^^^^^^^^^^^^^^^^^^^ - // | | - // | associated types `Item`, `IntoIter` must be specified - // associated types `Item`, `IntoIter` must be specified - // } - let overlaps = suggestions.windows(2).any(|pair| pair[0].0.overlaps(pair[1].0)); - if !suggestions.is_empty() && !overlaps { - err.multipart_suggestion( - format!("specify the associated type{}", pluralize!(types_count)), - suggestions, - Applicability::HasPlaceholders, - ); - if !where_constraints.is_empty() { - err.span_help(where_constraints, where_msg); - } - } - - self.set_tainted_by_errors(err.emit()); - } - - /// On ambiguous associated type, look for an associated function whose name matches the - /// extended path and, if found, emit an E0223 error with a structured suggestion. - /// e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195) - pub(crate) fn maybe_report_similar_assoc_fn( - &self, - span: Span, - qself_ty: Ty<'tcx>, - qself: &hir::Ty<'_>, - ) -> Result<(), ErrorGuaranteed> { - let tcx = self.tcx(); - if let Some((_, node)) = tcx.hir().parent_iter(qself.hir_id).skip(1).next() - && let hir::Node::Expr(hir::Expr { - kind: - hir::ExprKind::Path(hir::QPath::TypeRelative( - hir::Ty { - kind: - hir::TyKind::Path(hir::QPath::TypeRelative( - _, - hir::PathSegment { ident: ident2, .. }, - )), - .. - }, - hir::PathSegment { ident: ident3, .. }, - )), - .. - }) = node - && let Some(ty_def_id) = qself_ty.ty_def_id() - && let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id) - && let name = format!("{ident2}_{ident3}") - && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx - .associated_items(inherent_impl) - .filter_by_name_unhygienic(Symbol::intern(&name)) - .next() - { - let reported = - struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type") - .with_span_suggestion_verbose( - ident2.span.to(ident3.span), - format!("there is an associated function with a similar name: `{name}`"), - name, - Applicability::MaybeIncorrect, - ) - .emit(); - self.set_tainted_by_errors(reported); - Err(reported) - } else { - Ok(()) - } - } -} - -/// Emits an error regarding forbidden type binding associations -pub fn prohibit_assoc_ty_binding( - tcx: TyCtxt<'_>, - span: Span, - segment: Option<(&hir::PathSegment<'_>, Span)>, -) { - tcx.dcx().emit_err(AssocTypeBindingNotAllowed { - span, - fn_trait_expansion: if let Some((segment, span)) = segment - && segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar - { - Some(ParenthesizedFnTraitExpansion { - span, - expanded_type: fn_trait_to_string(tcx, segment, false), - }) - } else { - None - }, - }); -} - -pub(crate) fn fn_trait_to_string( - tcx: TyCtxt<'_>, - trait_segment: &hir::PathSegment<'_>, - parenthesized: bool, -) -> String { - let args = trait_segment - .args - .as_ref() - .and_then(|args| args.args.get(0)) - .and_then(|arg| match arg { - hir::GenericArg::Type(ty) => match ty.kind { - hir::TyKind::Tup(t) => t - .iter() - .map(|e| tcx.sess.source_map().span_to_snippet(e.span)) - .collect::, _>>() - .map(|a| a.join(", ")), - _ => tcx.sess.source_map().span_to_snippet(ty.span), - } - .map(|s| { - // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma - if parenthesized || s.is_empty() { format!("({s})") } else { format!("({s},)") } - }) - .ok(), - _ => None, - }) - .unwrap_or_else(|| "()".to_string()); - - let ret = trait_segment - .args() - .bindings - .iter() - .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { - (true, hir::TypeBindingKind::Equality { term }) => { - let span = match term { - hir::Term::Ty(ty) => ty.span, - hir::Term::Const(c) => tcx.hir().span(c.hir_id), - }; - - (span != tcx.hir().span(trait_segment.hir_id)) - .then_some(tcx.sess.source_map().span_to_snippet(span).ok()) - .flatten() - } - _ => None, - }) - .unwrap_or_else(|| "()".to_string()); - - if parenthesized { - format!("{}{} -> {}", trait_segment.ident, args, ret) - } else { - format!("{}<{}, Output={}>", trait_segment.ident, args, ret) - } -} diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs deleted file mode 100644 index 428eea2f686e9..0000000000000 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ /dev/null @@ -1,658 +0,0 @@ -use super::IsMethodCall; -use crate::astconv::{ - errors::prohibit_assoc_ty_binding, CreateInstantiationsForGenericArgsCtxt, ExplicitLateBound, - GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, -}; -use crate::structured_errors::{GenericArgsInfo, StructuredDiag, WrongNumberOfGenericArgs}; -use rustc_ast::ast::ParamKindOrd; -use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, -}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::GenericArg; -use rustc_middle::ty::{ - self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt, -}; -use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; -use rustc_span::symbol::kw; -use smallvec::SmallVec; - -/// Report an error that a generic argument did not match the generic parameter that was -/// expected. -fn generic_arg_mismatch_err( - tcx: TyCtxt<'_>, - arg: &GenericArg<'_>, - param: &GenericParamDef, - possible_ordering_error: bool, - help: Option, -) -> ErrorGuaranteed { - let sess = tcx.sess; - let mut err = struct_span_code_err!( - tcx.dcx(), - arg.span(), - E0747, - "{} provided when a {} was expected", - arg.descr(), - param.kind.descr(), - ); - - if let GenericParamDefKind::Const { .. } = param.kind { - if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { - err.help("const arguments cannot yet be inferred with `_`"); - if sess.is_nightly_build() { - err.help("add `#![feature(generic_arg_infer)]` to the crate attributes to enable"); - } - } - } - - let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diag<'_>| { - let suggestions = vec![ - (arg.span().shrink_to_lo(), String::from("{ ")), - (arg.span().shrink_to_hi(), String::from(" }")), - ]; - err.multipart_suggestion( - "if this generic argument was intended as a const parameter, \ - surround it with braces", - suggestions, - Applicability::MaybeIncorrect, - ); - }; - - // Specific suggestion set for diagnostics - match (arg, ¶m.kind) { - ( - GenericArg::Type(hir::Ty { - kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), - .. - }), - GenericParamDefKind::Const { .. }, - ) => match path.res { - Res::Err => { - add_braces_suggestion(arg, &mut err); - return err - .with_primary_message("unresolved item provided when a constant was expected") - .emit(); - } - Res::Def(DefKind::TyParam, src_def_id) => { - if let Some(param_local_id) = param.def_id.as_local() { - let param_name = tcx.hir().ty_param_name(param_local_id); - let param_type = tcx.type_of(param.def_id).instantiate_identity(); - if param_type.is_suggestable(tcx, false) { - err.span_suggestion( - tcx.def_span(src_def_id), - "consider changing this type parameter to a const parameter", - format!("const {param_name}: {param_type}"), - Applicability::MaybeIncorrect, - ); - }; - } - } - _ => add_braces_suggestion(arg, &mut err), - }, - ( - GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), - GenericParamDefKind::Const { .. }, - ) => add_braces_suggestion(arg, &mut err), - ( - GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), - GenericParamDefKind::Const { .. }, - ) if tcx.type_of(param.def_id).skip_binder() == tcx.types.usize => { - let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); - if let Ok(snippet) = snippet { - err.span_suggestion( - arg.span(), - "array type provided where a `usize` was expected, try", - format!("{{ {snippet} }}"), - Applicability::MaybeIncorrect, - ); - } - } - (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { - let body = tcx.hir().body(cnst.value.body); - if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body.value.kind - { - if let Res::Def(DefKind::Fn { .. }, id) = path.res { - err.help(format!("`{}` is a function item, not a type", tcx.item_name(id))); - err.help("function item types cannot be named directly"); - } - } - } - _ => {} - } - - let kind_ord = param.kind.to_ord(); - let arg_ord = arg.to_ord(); - - // This note is only true when generic parameters are strictly ordered by their kind. - if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { - let (first, last) = if kind_ord < arg_ord { - (param.kind.descr(), arg.descr()) - } else { - (arg.descr(), param.kind.descr()) - }; - err.note(format!("{first} arguments must be provided before {last} arguments")); - if let Some(help) = help { - err.help(help); - } - } - - err.emit() -} - -/// Creates the relevant generic arguments -/// corresponding to a set of generic parameters. This is a -/// rather complex function. Let us try to explain the role -/// of each of its parameters: -/// -/// To start, we are given the `def_id` of the thing whose generic -/// parameters we are instantiating, and a partial set of -/// arguments `parent_args`. In general, the generic arguments -/// for an item begin with arguments for all the "parents" of -/// that item -- e.g., for a method it might include the -/// parameters from the impl. -/// -/// Therefore, the method begins by walking down these parents, -/// starting with the outermost parent and proceed inwards until -/// it reaches `def_id`. For each parent `P`, it will check `parent_args` -/// first to see if the parent's arguments are listed in there. If so, -/// we can append those and move on. Otherwise, it invokes the -/// three callback functions: -/// -/// - `args_for_def_id`: given the `DefId` `P`, supplies back the -/// generic arguments that were given to that parent from within -/// the path; so e.g., if you have `::Bar`, the `DefId` -/// might refer to the trait `Foo`, and the arguments might be -/// `[T]`. The boolean value indicates whether to infer values -/// for arguments whose values were not explicitly provided. -/// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, -/// instantiate a `GenericArg`. -/// - `inferred_kind`: if no parameter was provided, and inference is enabled, then -/// creates a suitable inference variable. -pub fn create_args_for_parent_generic_args<'tcx: 'a, 'a>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - parent_args: &[ty::GenericArg<'tcx>], - has_self: bool, - self_ty: Option>, - arg_count: &GenericArgCountResult, - ctx: &mut impl CreateInstantiationsForGenericArgsCtxt<'a, 'tcx>, -) -> GenericArgsRef<'tcx> { - // Collect the segments of the path; we need to instantiate arguments - // for parameters throughout the entire path (wherever there are - // generic parameters). - let mut parent_defs = tcx.generics_of(def_id); - let count = parent_defs.count(); - let mut stack = vec![(def_id, parent_defs)]; - while let Some(def_id) = parent_defs.parent { - parent_defs = tcx.generics_of(def_id); - stack.push((def_id, parent_defs)); - } - - // We manually build up the generic arguments, rather than using convenience - // methods in `rustc_middle/src/ty/generic_args.rs`, so that we can iterate over the arguments and - // parameters in lock-step linearly, instead of trying to match each pair. - let mut args: SmallVec<[ty::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); - // Iterate over each segment of the path. - while let Some((def_id, defs)) = stack.pop() { - let mut params = defs.params.iter().peekable(); - - // If we have already computed the generic arguments for parents, - // we can use those directly. - while let Some(¶m) = params.peek() { - if let Some(&kind) = parent_args.get(param.index as usize) { - args.push(kind); - params.next(); - } else { - break; - } - } - - // `Self` is handled first, unless it's been handled in `parent_args`. - if has_self { - if let Some(¶m) = params.peek() { - if param.index == 0 { - if let GenericParamDefKind::Type { .. } = param.kind { - args.push( - self_ty - .map(|ty| ty.into()) - .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), - ); - params.next(); - } - } - } - } - - // Check whether this segment takes generic arguments and the user has provided any. - let (generic_args, infer_args) = ctx.args_for_def_id(def_id); - - let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()); - let mut args_iter = args_iter.clone().peekable(); - - // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. - // If we later encounter a lifetime, we know that the arguments were provided in the - // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be - // inferred, so we can use it for diagnostics later. - let mut force_infer_lt = None; - - loop { - // We're going to iterate through the generic arguments that the user - // provided, matching them with the generic parameters we expect. - // Mismatches can occur as a result of elided lifetimes, or for malformed - // input. We try to handle both sensibly. - match (args_iter.peek(), params.peek()) { - (Some(&arg), Some(¶m)) => { - match (arg, ¶m.kind, arg_count.explicit_late_bound) { - ( - GenericArg::Const(hir::ConstArg { - is_desugared_from_effects: true, - .. - }), - GenericParamDefKind::Const { is_host_effect: false, .. } - | GenericParamDefKind::Type { .. } - | GenericParamDefKind::Lifetime, - _, - ) => { - // SPECIAL CASE FOR DESUGARED EFFECT PARAMS - // This comes from the following example: - // - // ``` - // #[const_trait] - // pub trait PartialEq {} - // impl const PartialEq for () {} - // ``` - // - // Since this is a const impl, we need to insert a host arg at the end of - // `PartialEq`'s generics, but this errors since `Rhs` isn't specified. - // To work around this, we infer all arguments until we reach the host param. - args.push(ctx.inferred_kind(Some(&args), param, infer_args)); - params.next(); - } - (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) - | ( - GenericArg::Type(_) | GenericArg::Infer(_), - GenericParamDefKind::Type { .. }, - _, - ) - | ( - GenericArg::Const(_) | GenericArg::Infer(_), - GenericParamDefKind::Const { .. }, - _, - ) => { - args.push(ctx.provided_kind(param, arg)); - args_iter.next(); - params.next(); - } - ( - GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_), - GenericParamDefKind::Lifetime, - _, - ) => { - // We expected a lifetime argument, but got a type or const - // argument. That means we're inferring the lifetimes. - args.push(ctx.inferred_kind(None, param, infer_args)); - force_infer_lt = Some((arg, param)); - params.next(); - } - (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { - // We've come across a lifetime when we expected something else in - // the presence of explicit late bounds. This is most likely - // due to the presence of the explicit bound so we're just going to - // ignore it. - args_iter.next(); - } - (_, _, _) => { - // We expected one kind of parameter, but the user provided - // another. This is an error. However, if we already know that - // the arguments don't match up with the parameters, we won't issue - // an additional error, as the user already knows what's wrong. - if arg_count.correct.is_ok() { - // We're going to iterate over the parameters to sort them out, and - // show that order to the user as a possible order for the parameters - let mut param_types_present = defs - .params - .iter() - .map(|param| (param.kind.to_ord(), param.clone())) - .collect::>(); - param_types_present.sort_by_key(|(ord, _)| *ord); - let (mut param_types_present, ordered_params): ( - Vec, - Vec, - ) = param_types_present.into_iter().unzip(); - param_types_present.dedup(); - - generic_arg_mismatch_err( - tcx, - arg, - param, - !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()), - Some(format!( - "reorder the arguments: {}: `<{}>`", - param_types_present - .into_iter() - .map(|ord| format!("{ord}s")) - .collect::>() - .join(", then "), - ordered_params - .into_iter() - .filter_map(|param| { - if param.name == kw::SelfUpper { - None - } else { - Some(param.name.to_string()) - } - }) - .collect::>() - .join(", ") - )), - ); - } - - // We've reported the error, but we want to make sure that this - // problem doesn't bubble down and create additional, irrelevant - // errors. In this case, we're simply going to ignore the argument - // and any following arguments. The rest of the parameters will be - // inferred. - while args_iter.next().is_some() {} - } - } - } - - (Some(&arg), None) => { - // We should never be able to reach this point with well-formed input. - // There are three situations in which we can encounter this issue. - // - // 1. The number of arguments is incorrect. In this case, an error - // will already have been emitted, and we can ignore it. - // 2. There are late-bound lifetime parameters present, yet the - // lifetime arguments have also been explicitly specified by the - // user. - // 3. We've inferred some lifetimes, which have been provided later (i.e. - // after a type or const). We want to throw an error in this case. - - if arg_count.correct.is_ok() - && arg_count.explicit_late_bound == ExplicitLateBound::No - { - let kind = arg.descr(); - assert_eq!(kind, "lifetime"); - let (provided_arg, param) = - force_infer_lt.expect("lifetimes ought to have been inferred"); - generic_arg_mismatch_err(tcx, provided_arg, param, false, None); - } - - break; - } - - (None, Some(¶m)) => { - // If there are fewer arguments than parameters, it means - // we're inferring the remaining arguments. - args.push(ctx.inferred_kind(Some(&args), param, infer_args)); - params.next(); - } - - (None, None) => break, - } - } - } - - tcx.mk_args(&args) -} - -/// Checks that the correct number of generic arguments have been provided. -/// Used specifically for function calls. -pub fn check_generic_arg_count_for_call( - tcx: TyCtxt<'_>, - def_id: DefId, - generics: &ty::Generics, - seg: &hir::PathSegment<'_>, - is_method_call: IsMethodCall, -) -> GenericArgCountResult { - let gen_pos = match is_method_call { - IsMethodCall::Yes => GenericArgPosition::MethodCall, - IsMethodCall::No => GenericArgPosition::Value, - }; - let has_self = generics.parent.is_none() && generics.has_self; - check_generic_arg_count(tcx, def_id, seg, generics, gen_pos, has_self) -} - -/// Checks that the correct number of generic arguments have been provided. -/// This is used both for datatypes and function calls. -#[instrument(skip(tcx, gen_pos), level = "debug")] -pub(crate) fn check_generic_arg_count( - tcx: TyCtxt<'_>, - def_id: DefId, - seg: &hir::PathSegment<'_>, - gen_params: &ty::Generics, - gen_pos: GenericArgPosition, - has_self: bool, -) -> GenericArgCountResult { - let gen_args = seg.args(); - let default_counts = gen_params.own_defaults(); - let param_counts = gen_params.own_counts(); - - // Subtracting from param count to ensure type params synthesized from `impl Trait` - // cannot be explicitly specified. - let synth_type_param_count = gen_params - .params - .iter() - .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. })) - .count(); - let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; - let synth_const_param_count = gen_params - .params - .iter() - .filter(|param| { - matches!(param.kind, ty::GenericParamDefKind::Const { is_host_effect: true, .. }) - }) - .count(); - let named_const_param_count = param_counts.consts - synth_const_param_count; - let infer_lifetimes = - (gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_params(); - - if gen_pos != GenericArgPosition::Type - && let Some(b) = gen_args.bindings.first() - { - prohibit_assoc_ty_binding(tcx, b.span, None); - } - - let explicit_late_bound = - prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); - - let mut invalid_args = vec![]; - - let mut check_lifetime_args = |min_expected_args: usize, - max_expected_args: usize, - provided_args: usize, - late_bounds_ignore: bool| { - if (min_expected_args..=max_expected_args).contains(&provided_args) { - return Ok(()); - } - - if late_bounds_ignore { - return Ok(()); - } - - if provided_args > max_expected_args { - invalid_args.extend( - gen_args.args[max_expected_args..provided_args].iter().map(|arg| arg.span()), - ); - }; - - let gen_args_info = if provided_args > min_expected_args { - invalid_args.extend( - gen_args.args[min_expected_args..provided_args].iter().map(|arg| arg.span()), - ); - let num_redundant_args = provided_args - min_expected_args; - GenericArgsInfo::ExcessLifetimes { num_redundant_args } - } else { - let num_missing_args = min_expected_args - provided_args; - GenericArgsInfo::MissingLifetimes { num_missing_args } - }; - - let reported = WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - has_self as usize, - gen_args, - def_id, - ) - .diagnostic() - .emit(); - - Err(reported) - }; - - let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; - let max_expected_lifetime_args = param_counts.lifetimes; - let num_provided_lifetime_args = gen_args.num_lifetime_params(); - - let lifetimes_correct = check_lifetime_args( - min_expected_lifetime_args, - max_expected_lifetime_args, - num_provided_lifetime_args, - explicit_late_bound == ExplicitLateBound::Yes, - ); - - let mut check_types_and_consts = |expected_min, - expected_max, - expected_max_with_synth, - provided, - params_offset, - args_offset| { - debug!( - ?expected_min, - ?expected_max, - ?provided, - ?params_offset, - ?args_offset, - "check_types_and_consts" - ); - if (expected_min..=expected_max).contains(&provided) { - return Ok(()); - } - - let num_default_params = expected_max - expected_min; - - let gen_args_info = if provided > expected_max { - invalid_args.extend( - gen_args.args[args_offset + expected_max..args_offset + provided] - .iter() - .map(|arg| arg.span()), - ); - let num_redundant_args = provided - expected_max; - - // Provide extra note if synthetic arguments like `impl Trait` are specified. - let synth_provided = provided <= expected_max_with_synth; - - GenericArgsInfo::ExcessTypesOrConsts { - num_redundant_args, - num_default_params, - args_offset, - synth_provided, - } - } else { - let num_missing_args = expected_max - provided; - - GenericArgsInfo::MissingTypesOrConsts { - num_missing_args, - num_default_params, - args_offset, - } - }; - - debug!(?gen_args_info); - - let reported = WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - params_offset, - gen_args, - def_id, - ) - .diagnostic() - .emit_unless(gen_args.has_err()); - - Err(reported) - }; - - let args_correct = { - let expected_min = if seg.infer_args { - 0 - } else { - param_counts.consts + named_type_param_count - - default_counts.types - - default_counts.consts - }; - debug!(?expected_min); - debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); - - let provided = gen_args.num_generic_params(); - - check_types_and_consts( - expected_min, - named_const_param_count + named_type_param_count, - named_const_param_count + named_type_param_count + synth_type_param_count, - provided, - param_counts.lifetimes + has_self as usize, - gen_args.num_lifetime_params(), - ) - }; - - GenericArgCountResult { - explicit_late_bound, - correct: lifetimes_correct - .and(args_correct) - .map_err(|reported| GenericArgCountMismatch { reported: Some(reported), invalid_args }), - } -} - -/// Prohibits explicit lifetime arguments if late-bound lifetime parameters -/// are present. This is used both for datatypes and function calls. -pub(crate) fn prohibit_explicit_late_bound_lifetimes( - tcx: TyCtxt<'_>, - def: &ty::Generics, - args: &hir::GenericArgs<'_>, - position: GenericArgPosition, -) -> ExplicitLateBound { - let param_counts = def.own_counts(); - let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params(); - - if infer_lifetimes { - return ExplicitLateBound::No; - } - - if let Some(span_late) = def.has_late_bound_regions { - let msg = "cannot specify lifetime arguments explicitly \ - if late bound lifetime parameters are present"; - let note = "the late bound lifetime parameter is introduced here"; - let span = args.args[0].span(); - - if position == GenericArgPosition::Value - && args.num_lifetime_params() != param_counts.lifetimes - { - struct_span_code_err!(tcx.dcx(), span, E0794, "{}", msg) - .with_span_note(span_late, note) - .emit(); - } else { - let mut multispan = MultiSpan::from_span(span); - multispan.push_span_label(span_late, note); - tcx.node_span_lint( - LATE_BOUND_LIFETIME_ARGUMENTS, - args.args[0].hir_id(), - multispan, - msg, - |_| {}, - ); - } - - ExplicitLateBound::Yes - } else { - ExplicitLateBound::No - } -} diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs deleted file mode 100644 index b421a33ba294b..0000000000000 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ /dev/null @@ -1,256 +0,0 @@ -use rustc_ast::TraitObjectSyntax; -use rustc_errors::{codes::*, Diag, EmissionGuarantee, StashKey}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability}; -use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; - -use super::AstConv; - -impl<'tcx> dyn AstConv<'tcx> + '_ { - /// Make sure that we are in the condition to suggest the blanket implementation. - pub(super) fn maybe_lint_blanket_trait_impl( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_, G>, - ) { - let tcx = self.tcx(); - let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; - if let hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Impl(hir::Impl { - self_ty: impl_self_ty, - of_trait: Some(of_trait_ref), - generics, - .. - }), - .. - }) = tcx.hir_node_by_def_id(parent_id) - && self_ty.hir_id == impl_self_ty.hir_id - { - if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { - return; - } - let of_trait_span = of_trait_ref.path.span; - // make sure that we are not calling unwrap to abort during the compilation - let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { - return; - }; - - let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span) - else { - return; - }; - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); - if sugg.is_empty() { - return; - }; - diag.multipart_suggestion( - format!( - "alternatively use a blanket implementation to implement `{of_trait_name}` for \ - all types that also implement `{impl_trait_name}`" - ), - sugg, - Applicability::MaybeIncorrect, - ); - } - } - - fn add_generic_param_suggestion( - &self, - generics: &hir::Generics<'_>, - self_ty_span: Span, - impl_trait_name: &str, - ) -> Vec<(Span, String)> { - // check if the trait has generics, to make a correct suggestion - let param_name = generics.params.next_type_param_name(None); - - let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { - (span, format!(", {param_name}: {impl_trait_name}")) - } else { - (generics.span, format!("<{param_name}: {impl_trait_name}>")) - }; - vec![(self_ty_span, param_name), add_generic_sugg] - } - - /// Make sure that we are in the condition to suggest `impl Trait`. - fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool { - let tcx = self.tcx(); - let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; - let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => { - (sig, generics, None) - } - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(sig, _), - generics, - owner_id, - .. - }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), - _ => return false, - }; - let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; - let mut is_downgradable = true; - let is_object_safe = match self_ty.kind { - hir::TyKind::TraitObject(objects, ..) => { - objects.iter().all(|o| match o.trait_ref.path.res { - Res::Def(DefKind::Trait, id) => { - if Some(id) == owner { - // For recursive traits, don't downgrade the error. (#119652) - is_downgradable = false; - } - tcx.check_is_object_safe(id) - } - _ => false, - }) - } - _ => false, - }; - if let hir::FnRetTy::Return(ty) = sig.decl.output - && ty.hir_id == self_ty.hir_id - { - let pre = if !is_object_safe { - format!("`{trait_name}` is not object safe, ") - } else { - String::new() - }; - let msg = format!( - "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ - single underlying type", - ); - diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); - if is_object_safe { - diag.multipart_suggestion_verbose( - "alternatively, you can return an owned trait object", - vec![ - (ty.span.shrink_to_lo(), "Box".to_string()), - ], - Applicability::MachineApplicable, - ); - } else if is_downgradable { - // We'll emit the object safety error already, with a structured suggestion. - diag.downgrade_to_delayed_bug(); - } - return true; - } - for ty in sig.decl.inputs { - if ty.hir_id != self_ty.hir_id { - continue; - } - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name); - if !sugg.is_empty() { - diag.multipart_suggestion_verbose( - format!("use a new generic type parameter, constrained by `{trait_name}`"), - sugg, - Applicability::MachineApplicable, - ); - diag.multipart_suggestion_verbose( - "you can also use an opaque type, but users won't be able to specify the type \ - parameter when calling the `fn`, having to rely exclusively on type inference", - impl_sugg, - Applicability::MachineApplicable, - ); - } - if !is_object_safe { - diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`")); - if is_downgradable { - // We'll emit the object safety error already, with a structured suggestion. - diag.downgrade_to_delayed_bug(); - } - } else { - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { - // There are more than one trait bound, we need surrounding parentheses. - vec![ - (self_ty.span.shrink_to_lo(), "&(dyn ".to_string()), - (self_ty.span.shrink_to_hi(), ")".to_string()), - ] - } else { - vec![(self_ty.span.shrink_to_lo(), "&dyn ".to_string())] - }; - diag.multipart_suggestion_verbose( - format!( - "alternatively, use a trait object to accept any type that implements \ - `{trait_name}`, accessing its methods at runtime using dynamic dispatch", - ), - sugg, - Applicability::MachineApplicable, - ); - } - return true; - } - false - } - - pub(super) fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) { - let tcx = self.tcx(); - if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = - self_ty.kind - { - let needs_bracket = in_path - && !tcx - .sess - .source_map() - .span_to_prev_source(self_ty.span) - .ok() - .is_some_and(|s| s.trim_end().ends_with('<')); - - let is_global = poly_trait_ref.trait_ref.path.is_global(); - - let mut sugg = Vec::from_iter([( - self_ty.span.shrink_to_lo(), - format!( - "{}dyn {}", - if needs_bracket { "<" } else { "" }, - if is_global { "(" } else { "" }, - ), - )]); - - if is_global || needs_bracket { - sugg.push(( - self_ty.span.shrink_to_hi(), - format!( - "{}{}", - if is_global { ")" } else { "" }, - if needs_bracket { ">" } else { "" }, - ), - )); - } - - if self_ty.span.edition().at_least_rust_2021() { - let msg = "trait objects must include the `dyn` keyword"; - let label = "add `dyn` keyword before this trait"; - let mut diag = - rustc_errors::struct_span_code_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg); - if self_ty.span.can_be_used_for_suggestions() - && !self.maybe_lint_impl_trait(self_ty, &mut diag) - { - diag.multipart_suggestion_verbose( - label, - sugg, - Applicability::MachineApplicable, - ); - } - // check if the impl trait that we are considering is a impl of a local trait - self.maybe_lint_blanket_trait_impl(self_ty, &mut diag); - diag.stash(self_ty.span, StashKey::TraitMissingMethod); - } else { - let msg = "trait objects without an explicit `dyn` are deprecated"; - tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| { - if self_ty.span.can_be_used_for_suggestions() { - lint.multipart_suggestion_verbose( - "if this is an object-safe trait, use `dyn`", - sugg, - Applicability::MachineApplicable, - ); - } - self.maybe_lint_blanket_trait_impl(self_ty, lint); - }); - } - } - } -} diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs deleted file mode 100644 index a912d7f578dd0..0000000000000 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ /dev/null @@ -1,2669 +0,0 @@ -//! Conversion from AST representation of types to the `ty.rs` representation. -//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an -//! instance of `AstConv`. - -mod bounds; -mod errors; -pub mod generics; -mod lint; -mod object_safety; - -use crate::astconv::errors::prohibit_assoc_ty_binding; -use crate::astconv::generics::{check_generic_arg_count, create_args_for_parent_generic_args}; -use crate::bounds::Bounds; -use crate::collect::HirPlaceholderCollector; -use crate::errors::AmbiguousLifetimeBound; -use crate::middle::resolve_bound_vars as rbv; -use crate::require_c_abi_if_c_variadic; -use rustc_ast::TraitObjectSyntax; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, MultiSpan, -}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{walk_generics, Visitor as _}; -use rustc_hir::{GenericArg, GenericArgs}; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCause; -use rustc_middle::middle::stability::AllowUnstable; -use rustc_middle::ty::{ - self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, - TypeVisitableExt, -}; -use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; -use rustc_target::spec::abi; -use rustc_trait_selection::traits::wf::object_region_bounds; -use rustc_trait_selection::traits::{self, ObligationCtxt}; - -use std::fmt::Display; -use std::slice; - -#[derive(Debug)] -pub struct PathSeg(pub DefId, pub usize); - -#[derive(Copy, Clone, Debug)] -pub struct OnlySelfBounds(pub bool); - -#[derive(Copy, Clone, Debug)] -pub enum PredicateFilter { - /// All predicates may be implied by the trait. - All, - - /// Only traits that reference `Self: ..` are implied by the trait. - SelfOnly, - - /// Only traits that reference `Self: ..` and define an associated type - /// with the given ident are implied by the trait. - SelfThatDefines(Ident), - - /// Only traits that reference `Self: ..` and their associated type bounds. - /// For example, given `Self: Tr`, this would expand to `Self: Tr` - /// and `::A: B`. - SelfAndAssociatedTypeBounds, -} - -pub trait AstConv<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx>; - - fn item_def_id(&self) -> DefId; - - /// Returns predicates in scope of the form `X: Foo`, where `X` - /// is a type parameter `X` with the given id `def_id` and T - /// matches `assoc_name`. This is a subset of the full set of - /// predicates. - /// - /// This is used for one specific purpose: resolving "short-hand" - /// associated type references like `T::Item`. In principle, we - /// would do that by first getting the full set of predicates in - /// scope and then filtering down to find those that apply to `T`, - /// but this can lead to cycle errors. The problem is that we have - /// to do this resolution *in order to create the predicates in - /// the first place*. Hence, we have this "special pass". - fn get_type_parameter_bounds( - &self, - span: Span, - def_id: LocalDefId, - assoc_name: Ident, - ) -> ty::GenericPredicates<'tcx>; - - /// Returns the lifetime to use when a lifetime is omitted (and not elided). - fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) - -> Option>; - - /// Returns the type to use when a type is omitted. - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; - - /// Returns `true` if `_` is allowed in type signatures in the current context. - fn allow_ty_infer(&self) -> bool; - - /// Returns the const to use when a const is omitted. - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> Const<'tcx>; - - /// Projecting an associated type from a (potentially) - /// higher-ranked trait reference is more complicated, because of - /// the possibility of late-bound regions appearing in the - /// associated type binding. This is not legal in function - /// signatures for that reason. In a function body, we can always - /// handle it because we can use inference variables to remove the - /// late-bound regions. - fn projected_ty_from_poly_trait_ref( - &self, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'tcx>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx>; - - /// Returns `AdtDef` if `ty` is an ADT. - /// Note that `ty` might be a projection type that needs normalization. - /// This used to get the enum variants in scope of the type. - /// For example, `Self::A` could refer to an associated type - /// or to an enum variant depending on the result of this function. - fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option>; - - /// Invoked when we encounter an error from some prior pass - /// (e.g., resolve) that is translated into a ty-error. This is - /// used to help suppress derived errors typeck might otherwise - /// report. - fn set_tainted_by_errors(&self, e: ErrorGuaranteed); - - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span); - - fn astconv(&self) -> &dyn AstConv<'tcx> - where - Self: Sized, - { - self - } - - fn infcx(&self) -> Option<&InferCtxt<'tcx>>; -} - -/// New-typed boolean indicating whether explicit late-bound lifetimes -/// are present in a set of generic arguments. -/// -/// For example if we have some method `fn f<'a>(&'a self)` implemented -/// for some type `T`, although `f` is generic in the lifetime `'a`, `'a` -/// is late-bound so should not be provided explicitly. Thus, if `f` is -/// instantiated with some generic arguments providing `'a` explicitly, -/// we taint those arguments with `ExplicitLateBound::Yes` so that we -/// can provide an appropriate diagnostic later. -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum ExplicitLateBound { - Yes, - No, -} - -#[derive(Copy, Clone, PartialEq)] -pub enum IsMethodCall { - Yes, - No, -} - -/// Denotes the "position" of a generic argument, indicating if it is a generic type, -/// generic function or generic method call. -#[derive(Copy, Clone, PartialEq)] -pub(crate) enum GenericArgPosition { - Type, - Value, // e.g., functions - MethodCall, -} - -/// A marker denoting that the generic arguments that were -/// provided did not match the respective generic parameters. -#[derive(Clone, Default, Debug)] -pub struct GenericArgCountMismatch { - /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). - pub reported: Option, - /// A list of spans of arguments provided that were not valid. - pub invalid_args: Vec, -} - -/// Decorates the result of a generic argument count mismatch -/// check with whether explicit late bounds were provided. -#[derive(Clone, Debug)] -pub struct GenericArgCountResult { - pub explicit_late_bound: ExplicitLateBound, - pub correct: Result<(), GenericArgCountMismatch>, -} - -pub trait CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> { - fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'tcx>>, bool); - - fn provided_kind( - &mut self, - param: &ty::GenericParamDef, - arg: &GenericArg<'tcx>, - ) -> ty::GenericArg<'tcx>; - - fn inferred_kind( - &mut self, - args: Option<&[ty::GenericArg<'tcx>]>, - param: &ty::GenericParamDef, - infer_args: bool, - ) -> ty::GenericArg<'tcx>; -} - -impl<'tcx> dyn AstConv<'tcx> + '_ { - #[instrument(level = "debug", skip(self), ret)] - pub fn ast_region_to_region( - &self, - lifetime: &hir::Lifetime, - def: Option<&ty::GenericParamDef>, - ) -> ty::Region<'tcx> { - let tcx = self.tcx(); - let lifetime_name = |def_id| tcx.hir().name(tcx.local_def_id_to_hir_id(def_id)); - - match tcx.named_bound_var(lifetime.hir_id) { - Some(rbv::ResolvedArg::StaticLifetime) => tcx.lifetimes.re_static, - - Some(rbv::ResolvedArg::LateBound(debruijn, index, def_id)) => { - let name = lifetime_name(def_id.expect_local()); - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(index), - kind: ty::BrNamed(def_id, name), - }; - ty::Region::new_bound(tcx, debruijn, br) - } - - Some(rbv::ResolvedArg::EarlyBound(def_id)) => { - let name = tcx.hir().ty_param_name(def_id.expect_local()); - let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - ty::Region::new_early_param(tcx, ty::EarlyParamRegion { def_id, index, name }) - } - - Some(rbv::ResolvedArg::Free(scope, id)) => { - let name = lifetime_name(id.expect_local()); - ty::Region::new_late_param(tcx, scope, ty::BrNamed(id, name)) - - // (*) -- not late-bound, won't change - } - - Some(rbv::ResolvedArg::Error(guar)) => ty::Region::new_error(tcx, guar), - - None => { - self.re_infer(def, lifetime.ident.span).unwrap_or_else(|| { - debug!(?lifetime, "unelided lifetime in signature"); - - // This indicates an illegal lifetime - // elision. `resolve_lifetime` should have - // reported an error in this case -- but if - // not, let's error out. - ty::Region::new_error_with_message( - tcx, - lifetime.ident.span, - "unelided lifetime in signature", - ) - }) - } - } - } - - /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, - /// returns an appropriate set of generic arguments for this particular reference to `I`. - pub fn ast_path_args_for_ty( - &self, - span: Span, - def_id: DefId, - item_segment: &hir::PathSegment<'tcx>, - ) -> GenericArgsRef<'tcx> { - let (args, _) = self.create_args_for_ast_path( - span, - def_id, - &[], - item_segment, - None, - ty::BoundConstness::NotConst, - ); - if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span))); - } - - args - } - - /// Given the type/lifetime/const arguments provided to some path (along with - /// an implicit `Self`, if this is a trait reference), returns the complete - /// set of generic arguments. This may involve applying defaulted type parameters. - /// - /// Constraints on associated types are not converted here but - /// separately in `add_predicates_for_ast_type_binding`. - /// - /// Example: - /// - /// ```ignore (illustrative) - /// T: std::ops::Index - /// // ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 - /// ``` - /// - /// 1. The `self_ty` here would refer to the type `T`. - /// 2. The path in question is the path to the trait `std::ops::Index`, - /// which will have been resolved to a `def_id` - /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type - /// parameters are returned in the `GenericArgsRef` - /// 4. Associated type bindings like `Output = u32` are contained in `generic_args.bindings`. - /// - /// Note that the type listing given here is *exactly* what the user provided. - /// - /// For (generic) associated types - /// - /// ```ignore (illustrative) - /// as Iterable>::Iter::<'a> - /// ``` - /// - /// We have the parent args are the args for the parent trait: - /// `[Vec, u8]` and `generic_args` are the arguments for the associated - /// type itself: `['a]`. The returned `GenericArgsRef` concatenates these two - /// lists: `[Vec, u8, 'a]`. - #[instrument(level = "debug", skip(self, span), ret)] - fn create_args_for_ast_path( - &self, - span: Span, - def_id: DefId, - parent_args: &[ty::GenericArg<'tcx>], - segment: &hir::PathSegment<'tcx>, - self_ty: Option>, - constness: ty::BoundConstness, - ) -> (GenericArgsRef<'tcx>, GenericArgCountResult) { - // If the type is parameterized by this region, then replace this - // region with the current anon region binding (in other words, - // whatever & would get replaced with). - - let tcx = self.tcx(); - let generics = tcx.generics_of(def_id); - debug!("generics: {:?}", generics); - - if generics.has_self { - if generics.parent.is_some() { - // The parent is a trait so it should have at least one - // generic parameter for the `Self` type. - assert!(!parent_args.is_empty()) - } else { - // This item (presumably a trait) needs a self-type. - assert!(self_ty.is_some()); - } - } else { - assert!(self_ty.is_none()); - } - - let mut arg_count = check_generic_arg_count( - tcx, - def_id, - segment, - generics, - GenericArgPosition::Type, - self_ty.is_some(), - ); - - if let Err(err) = &arg_count.correct - && let Some(reported) = err.reported - { - self.set_tainted_by_errors(reported); - } - - // Skip processing if type has no generic parameters. - // Traits always have `Self` as a generic parameter, which means they will not return early - // here and so associated type bindings will be handled regardless of whether there are any - // non-`Self` generic parameters. - if generics.params.is_empty() { - return (tcx.mk_args(parent_args), arg_count); - } - - struct InstantiationsForAstPathCtxt<'a, 'tcx> { - astconv: &'a dyn AstConv<'tcx>, - def_id: DefId, - generic_args: &'a GenericArgs<'tcx>, - span: Span, - inferred_params: Vec, - infer_args: bool, - } - - impl<'a, 'tcx> CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> - for InstantiationsForAstPathCtxt<'a, 'tcx> - { - fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'tcx>>, bool) { - if did == self.def_id { - (Some(self.generic_args), self.infer_args) - } else { - // The last component of this tuple is unimportant. - (None, false) - } - } - - fn provided_kind( - &mut self, - param: &ty::GenericParamDef, - arg: &GenericArg<'tcx>, - ) -> ty::GenericArg<'tcx> { - let tcx = self.astconv.tcx(); - - let mut handle_ty_args = |has_default, ty: &hir::Ty<'tcx>| { - if has_default { - tcx.check_optional_stability( - param.def_id, - Some(arg.hir_id()), - arg.span(), - None, - AllowUnstable::No, - |_, _| { - // Default generic parameters may not be marked - // with stability attributes, i.e. when the - // default parameter was defined at the same time - // as the rest of the type. As such, we ignore missing - // stability attributes. - }, - ); - } - if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) { - self.inferred_params.push(ty.span); - Ty::new_misc_error(tcx).into() - } else { - self.astconv.ast_ty_to_ty(ty).into() - } - }; - - match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.astconv.ast_region_to_region(lt, Some(param)).into() - } - (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { - handle_ty_args(has_default, ty) - } - (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => { - handle_ty_args(has_default, &inf.to_ty()) - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - let did = ct.value.def_id; - tcx.feed_anon_const_type(did, tcx.type_of(param.def_id)); - ty::Const::from_anon_const(tcx, did).into() - } - (&GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => { - let ty = tcx - .at(self.span) - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - if self.astconv.allow_ty_infer() { - self.astconv.ct_infer(ty, Some(param), inf.span).into() - } else { - self.inferred_params.push(inf.span); - ty::Const::new_misc_error(tcx, ty).into() - } - } - (kind, arg) => span_bug!( - self.span, - "mismatched path argument for kind {kind:?}: found arg {arg:?}" - ), - } - } - - fn inferred_kind( - &mut self, - args: Option<&[ty::GenericArg<'tcx>]>, - param: &ty::GenericParamDef, - infer_args: bool, - ) -> ty::GenericArg<'tcx> { - let tcx = self.astconv.tcx(); - match param.kind { - GenericParamDefKind::Lifetime => self - .astconv - .re_infer(Some(param), self.span) - .unwrap_or_else(|| { - debug!(?param, "unelided lifetime in signature"); - - // This indicates an illegal lifetime in a non-assoc-trait position - ty::Region::new_error_with_message( - tcx, - self.span, - "unelided lifetime in signature", - ) - }) - .into(), - GenericParamDefKind::Type { has_default, .. } => { - if !infer_args && has_default { - // No type parameter provided, but a default exists. - let args = args.unwrap(); - if args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Type(ty) => ty.references_error(), - _ => false, - }) { - // Avoid ICE #86756 when type error recovery goes awry. - return Ty::new_misc_error(tcx).into(); - } - tcx.at(self.span).type_of(param.def_id).instantiate(tcx, args).into() - } else if infer_args { - self.astconv.ty_infer(Some(param), self.span).into() - } else { - // We've already errored above about the mismatch. - Ty::new_misc_error(tcx).into() - } - } - GenericParamDefKind::Const { has_default, .. } => { - let ty = tcx - .at(self.span) - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - if let Err(guar) = ty.error_reported() { - return ty::Const::new_error(tcx, guar, ty).into(); - } - // FIXME(effects) see if we should special case effect params here - if !infer_args && has_default { - tcx.const_param_default(param.def_id) - .instantiate(tcx, args.unwrap()) - .into() - } else { - if infer_args { - self.astconv.ct_infer(ty, Some(param), self.span).into() - } else { - // We've already errored above about the mismatch. - ty::Const::new_misc_error(tcx, ty).into() - } - } - } - } - } - } - - let mut args_ctx = InstantiationsForAstPathCtxt { - astconv: self, - def_id, - span, - generic_args: segment.args(), - inferred_params: vec![], - infer_args: segment.infer_args, - }; - if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness - && generics.has_self - && !tcx.has_attr(def_id, sym::const_trait) - { - let e = tcx.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { - span, - modifier: constness.as_str(), - }); - self.set_tainted_by_errors(e); - arg_count.correct = - Err(GenericArgCountMismatch { reported: Some(e), invalid_args: vec![] }); - } - let args = create_args_for_parent_generic_args( - tcx, - def_id, - parent_args, - self_ty.is_some(), - self_ty, - &arg_count, - &mut args_ctx, - ); - - (args, arg_count) - } - - pub fn create_args_for_associated_item( - &self, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'tcx>, - parent_args: GenericArgsRef<'tcx>, - ) -> GenericArgsRef<'tcx> { - debug!( - "create_args_for_associated_item(span: {:?}, item_def_id: {:?}, item_segment: {:?}", - span, item_def_id, item_segment - ); - let (args, _) = self.create_args_for_ast_path( - span, - item_def_id, - parent_args, - item_segment, - None, - ty::BoundConstness::NotConst, - ); - - if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span))); - } - - args - } - - /// Instantiates the path for the given trait reference, assuming that it's - /// bound to a valid trait type. Returns the `DefId` of the defining trait. - /// The type _cannot_ be a type other than a trait type. - /// - /// If the `projections` argument is `None`, then assoc type bindings like `Foo` - /// are disallowed. Otherwise, they are pushed onto the vector given. - pub fn instantiate_mono_trait_ref( - &self, - trait_ref: &hir::TraitRef<'tcx>, - self_ty: Ty<'tcx>, - ) -> ty::TraitRef<'tcx> { - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); - - self.ast_path_to_mono_trait_ref( - trait_ref.path.span, - trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), - self_ty, - trait_ref.path.segments.last().unwrap(), - true, - ty::BoundConstness::NotConst, - ) - } - - /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct - /// a full trait reference. The resulting trait reference is returned. This may also generate - /// auxiliary bounds, which are added to `bounds`. - /// - /// Example: - /// - /// ```ignore (illustrative) - /// poly_trait_ref = Iterator - /// self_ty = Foo - /// ``` - /// - /// this would return `Foo: Iterator` and add `::Item = u32` into `bounds`. - /// - /// **A note on binders:** against our usual convention, there is an implied binder around - /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions. - /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>` - /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be - /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly, - /// however. - #[instrument(level = "debug", skip(self, span, constness, bounds))] - pub(crate) fn instantiate_poly_trait_ref( - &self, - trait_ref: &hir::TraitRef<'tcx>, - span: Span, - constness: ty::BoundConstness, - polarity: ty::ImplPolarity, - self_ty: Ty<'tcx>, - bounds: &mut Bounds<'tcx>, - only_self_bounds: OnlySelfBounds, - ) -> GenericArgCountResult { - let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); - let trait_segment = trait_ref.path.segments.last().unwrap(); - - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); - self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); - - let (generic_args, arg_count) = self.create_args_for_ast_path( - trait_ref.path.span, - trait_def_id, - &[], - trait_segment, - Some(self_ty), - constness, - ); - - let tcx = self.tcx(); - let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id); - debug!(?bound_vars); - - let poly_trait_ref = ty::Binder::bind_with_vars( - ty::TraitRef::new(tcx, trait_def_id, generic_args), - bound_vars, - ); - - debug!(?poly_trait_ref); - bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity); - - let mut dup_bindings = FxIndexMap::default(); - for binding in trait_segment.args().bindings { - // Don't register additional associated type bounds for negative bounds, - // since we should have emitten an error for them earlier, and they will - // not be well-formed! - if polarity == ty::ImplPolarity::Negative { - assert!( - self.tcx().dcx().has_errors().is_some(), - "negative trait bounds should not have bindings", - ); - continue; - } - - // Specify type to assert that error was already reported in `Err` case. - let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding( - trait_ref.hir_ref_id, - poly_trait_ref, - binding, - bounds, - &mut dup_bindings, - binding.span, - only_self_bounds, - ); - // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). - } - - arg_count - } - - fn ast_path_to_mono_trait_ref( - &self, - span: Span, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - trait_segment: &hir::PathSegment<'tcx>, - is_impl: bool, - // FIXME(effects) move all host param things in astconv to hir lowering - constness: ty::BoundConstness, - ) -> ty::TraitRef<'tcx> { - self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); - - let (generic_args, _) = self.create_args_for_ast_path( - span, - trait_def_id, - &[], - trait_segment, - Some(self_ty), - constness, - ); - if let Some(b) = trait_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span, Some((trait_segment, span))); - } - ty::TraitRef::new(self.tcx(), trait_def_id, generic_args) - } - - fn trait_defines_associated_item_named( - &self, - trait_def_id: DefId, - assoc_kind: ty::AssocKind, - assoc_name: Ident, - ) -> bool { - self.tcx() - .associated_items(trait_def_id) - .find_by_name_and_kind(self.tcx(), assoc_name, assoc_kind, trait_def_id) - .is_some() - } - - fn ast_path_to_ty( - &self, - span: Span, - did: DefId, - item_segment: &hir::PathSegment<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx(); - let args = self.ast_path_args_for_ty(span, did, item_segment); - - if let DefKind::TyAlias = tcx.def_kind(did) - && tcx.type_alias_is_lazy(did) - { - // Type aliases defined in crates that have the - // feature `lazy_type_alias` enabled get encoded as a type alias that normalization will - // then actually instantiate the where bounds of. - let alias_ty = ty::AliasTy::new(tcx, did, args); - Ty::new_alias(tcx, ty::Weak, alias_ty) - } else { - tcx.at(span).type_of(did).instantiate(tcx, args) - } - } - - // Search for a bound on a type parameter which includes the associated item - // given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter - // This function will fail if there are no suitable bounds or there is - // any ambiguity. - fn find_bound_for_assoc_item( - &self, - ty_param_def_id: LocalDefId, - assoc_name: Ident, - span: Span, - ) -> Result, ErrorGuaranteed> { - let tcx = self.tcx(); - - debug!( - "find_bound_for_assoc_item(ty_param_def_id={:?}, assoc_name={:?}, span={:?})", - ty_param_def_id, assoc_name, span, - ); - - let predicates = - &self.get_type_parameter_bounds(span, ty_param_def_id, assoc_name).predicates; - - debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); - - let param_name = tcx.hir().ty_param_name(ty_param_def_id); - self.one_bound_for_assoc_item( - || { - traits::transitive_bounds_that_define_assoc_item( - tcx, - predicates - .iter() - .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))), - assoc_name, - ) - }, - param_name, - Some(ty_param_def_id), - ty::AssocKind::Type, - assoc_name, - span, - None, - ) - } - - // Checks that `bounds` contains exactly one element and reports appropriate - // errors otherwise. - #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, binding), ret)] - fn one_bound_for_assoc_item( - &self, - all_candidates: impl Fn() -> I, - ty_param_name: impl Display, - ty_param_def_id: Option, - assoc_kind: ty::AssocKind, - assoc_name: Ident, - span: Span, - binding: Option<&hir::TypeBinding<'tcx>>, - ) -> Result, ErrorGuaranteed> - where - I: Iterator>, - { - let tcx = self.tcx(); - - let mut matching_candidates = all_candidates().filter(|r| { - self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name) - }); - - let Some(bound) = matching_candidates.next() else { - let reported = self.complain_about_assoc_item_not_found( - all_candidates, - &ty_param_name.to_string(), - ty_param_def_id, - assoc_kind, - assoc_name, - span, - binding, - ); - self.set_tainted_by_errors(reported); - return Err(reported); - }; - debug!(?bound); - - if let Some(bound2) = matching_candidates.next() { - debug!(?bound2); - - let assoc_kind_str = assoc_kind_str(assoc_kind); - let ty_param_name = &ty_param_name.to_string(); - let mut err = tcx.dcx().create_err(crate::errors::AmbiguousAssocItem { - span, - assoc_kind: assoc_kind_str, - assoc_name, - ty_param_name, - }); - // Provide a more specific error code index entry for equality bindings. - err.code( - if let Some(binding) = binding - && let hir::TypeBindingKind::Equality { .. } = binding.kind - { - E0222 - } else { - E0221 - }, - ); - - // FIXME(#97583): Resugar equality bounds to type/const bindings. - // FIXME: Turn this into a structured, translateable & more actionable suggestion. - let mut where_bounds = vec![]; - for bound in [bound, bound2].into_iter().chain(matching_candidates) { - let bound_id = bound.def_id(); - let bound_span = tcx - .associated_items(bound_id) - .find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id) - .and_then(|item| tcx.hir().span_if_local(item.def_id)); - - if let Some(bound_span) = bound_span { - err.span_label( - bound_span, - format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),), - ); - if let Some(binding) = binding { - match binding.kind { - hir::TypeBindingKind::Equality { term } => { - let term: ty::Term<'_> = match term { - hir::Term::Ty(ty) => self.ast_ty_to_ty(ty).into(), - hir::Term::Const(ct) => { - ty::Const::from_anon_const(tcx, ct.def_id).into() - } - }; - // FIXME(#97583): This isn't syntactically well-formed! - where_bounds.push(format!( - " T: {trait}::{assoc_name} = {term}", - trait = bound.print_only_trait_path(), - )); - } - // FIXME: Provide a suggestion. - hir::TypeBindingKind::Constraint { bounds: _ } => {} - } - } else { - err.span_suggestion_verbose( - span.with_hi(assoc_name.span.lo()), - "use fully-qualified syntax to disambiguate", - format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()), - Applicability::MaybeIncorrect, - ); - } - } else { - err.note(format!( - "associated {assoc_kind_str} `{assoc_name}` could derive from `{}`", - bound.print_only_trait_path(), - )); - } - } - if !where_bounds.is_empty() { - err.help(format!( - "consider introducing a new type parameter `T` and adding `where` constraints:\ - \n where\n T: {ty_param_name},\n{}", - where_bounds.join(",\n"), - )); - } - let reported = err.emit(); - self.set_tainted_by_errors(reported); - if !where_bounds.is_empty() { - return Err(reported); - } - } - - Ok(bound) - } - - // Create a type from a path to an associated type or to an enum variant. - // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C` - // and item_segment is the path segment for `D`. We return a type and a def for - // the whole path. - // Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type - // parameter or `Self`. - // NOTE: When this function starts resolving `Trait::AssocTy` successfully - // it should also start reporting the `BARE_TRAIT_OBJECTS` lint. - #[instrument(level = "debug", skip(self, hir_ref_id, span, qself, assoc_segment), fields(assoc_ident=?assoc_segment.ident), ret)] - pub fn associated_path_to_ty( - &self, - hir_ref_id: hir::HirId, - span: Span, - qself_ty: Ty<'tcx>, - qself: &hir::Ty<'_>, - assoc_segment: &hir::PathSegment<'tcx>, - permit_variants: bool, - ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { - let tcx = self.tcx(); - let assoc_ident = assoc_segment.ident; - let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { - path.res - } else { - Res::Err - }; - - // Check if we have an enum variant or an inherent associated type. - let mut variant_resolution = None; - if let Some(adt_def) = self.probe_adt(span, qself_ty) { - if adt_def.is_enum() { - let variant_def = adt_def - .variants() - .iter() - .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did())); - if let Some(variant_def) = variant_def { - if permit_variants { - tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); - self.prohibit_generics(slice::from_ref(assoc_segment).iter(), |err| { - err.note("enum variants can't have type parameters"); - let type_name = tcx.item_name(adt_def.did()); - let msg = format!( - "you might have meant to specify type parameters on enum \ - `{type_name}`" - ); - let Some(args) = assoc_segment.args else { - return; - }; - // Get the span of the generics args *including* the leading `::`. - // We do so by stretching args.span_ext to the left by 2. Earlier - // it was done based on the end of assoc segment but that sometimes - // led to impossible spans and caused issues like #116473 - let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); - if tcx.generics_of(adt_def.did()).count() == 0 { - // FIXME(estebank): we could also verify that the arguments being - // work for the `enum`, instead of just looking if it takes *any*. - err.span_suggestion_verbose( - args_span, - format!("{type_name} doesn't have generic parameters"), - "", - Applicability::MachineApplicable, - ); - return; - } - let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) - else { - err.note(msg); - return; - }; - let (qself_sugg_span, is_self) = - if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = - &qself.kind - { - // If the path segment already has type params, we want to overwrite - // them. - match &path.segments { - // `segment` is the previous to last element on the path, - // which would normally be the `enum` itself, while the last - // `_` `PathSegment` corresponds to the variant. - [ - .., - hir::PathSegment { - ident, - args, - res: Res::Def(DefKind::Enum, _), - .. - }, - _, - ] => ( - // We need to include the `::` in `Type::Variant::` - // to point the span to `::`, not just ``. - ident.span.shrink_to_hi().to(args - .map_or(ident.span.shrink_to_hi(), |a| a.span_ext)), - false, - ), - [segment] => ( - // We need to include the `::` in `Type::Variant::` - // to point the span to `::`, not just ``. - segment.ident.span.shrink_to_hi().to(segment - .args - .map_or(segment.ident.span.shrink_to_hi(), |a| { - a.span_ext - })), - kw::SelfUpper == segment.ident.name, - ), - _ => { - err.note(msg); - return; - } - } - } else { - err.note(msg); - return; - }; - let suggestion = vec![ - if is_self { - // Account for people writing `Self::Variant::`, where - // `Self` is the enum, and suggest replacing `Self` with the - // appropriate type: `Type::::Variant`. - (qself.span, format!("{type_name}{snippet}")) - } else { - (qself_sugg_span, snippet) - }, - (args_span, String::new()), - ]; - err.multipart_suggestion_verbose( - msg, - suggestion, - Applicability::MaybeIncorrect, - ); - }); - return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); - } else { - variant_resolution = Some(variant_def.def_id); - } - } - } - - if let Some((ty, did)) = self.lookup_inherent_assoc_ty( - assoc_ident, - assoc_segment, - adt_def.did(), - qself_ty, - hir_ref_id, - span, - )? { - return Ok((ty, DefKind::AssocTy, did)); - } - } - - // Find the type of the associated item, and the trait where the associated - // item is declared. - let bound = match (&qself_ty.kind(), qself_res) { - (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => { - // `Self` in an impl of a trait -- we have a concrete self type and a - // trait reference. - let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else { - // A cycle error occurred, most likely. - tcx.dcx().span_bug(span, "expected cycle error"); - }; - - self.one_bound_for_assoc_item( - || { - traits::supertraits( - tcx, - ty::Binder::dummy(trait_ref.instantiate_identity()), - ) - }, - kw::SelfUpper, - None, - ty::AssocKind::Type, - assoc_ident, - span, - None, - )? - } - ( - &ty::Param(_), - Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), - ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?, - _ => { - let reported = if variant_resolution.is_some() { - // Variant in type position - let msg = format!("expected type, found variant `{assoc_ident}`"); - tcx.dcx().span_err(span, msg) - } else if qself_ty.is_enum() { - let mut err = struct_span_code_err!( - tcx.dcx(), - assoc_ident.span, - E0599, - "no variant named `{}` found for enum `{}`", - assoc_ident, - qself_ty, - ); - - let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggested_name) = find_best_match_for_name( - &adt_def - .variants() - .iter() - .map(|variant| variant.name) - .collect::>(), - assoc_ident.name, - None, - ) { - err.span_suggestion( - assoc_ident.span, - "there is a variant with a similar name", - suggested_name, - Applicability::MaybeIncorrect, - ); - } else { - err.span_label( - assoc_ident.span, - format!("variant not found in `{qself_ty}`"), - ); - } - - if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) { - err.span_label(sp, format!("variant `{assoc_ident}` not found here")); - } - - err.emit() - } else if let Err(reported) = qself_ty.error_reported() { - reported - } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() { - // `::Assoc` makes no sense. - struct_span_code_err!( - tcx.dcx(), - tcx.def_span(alias_ty.def_id), - E0667, - "`impl Trait` is not allowed in path parameters" - ) - .emit() // Already reported in an earlier stage. - } else { - self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?; - - let traits: Vec<_> = - self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); - - // Don't print `ty::Error` to the user. - self.report_ambiguous_associated_type( - span, - &[qself_ty.to_string()], - &traits, - assoc_ident.name, - ) - }; - self.set_tainted_by_errors(reported); - return Err(reported); - } - }; - - let trait_did = bound.def_id(); - let assoc_ty_did = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap(); - let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound); - - if let Some(variant_def_id) = variant_resolution { - tcx.node_span_lint( - AMBIGUOUS_ASSOCIATED_ITEMS, - hir_ref_id, - span, - "ambiguous associated item", - |lint| { - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - assoc_ident, - also, - tcx.def_kind_descr(kind, def_id) - ); - lint.span_note(tcx.def_span(def_id), note_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(DefKind::AssocTy, assoc_ty_did, " also"); - - lint.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), - Applicability::MachineApplicable, - ); - }, - ); - } - Ok((ty, DefKind::AssocTy, assoc_ty_did)) - } - - fn lookup_inherent_assoc_ty( - &self, - name: Ident, - segment: &hir::PathSegment<'tcx>, - adt_did: DefId, - self_ty: Ty<'tcx>, - block: hir::HirId, - span: Span, - ) -> Result, DefId)>, ErrorGuaranteed> { - let tcx = self.tcx(); - - // Don't attempt to look up inherent associated types when the feature is not enabled. - // Theoretically it'd be fine to do so since we feature-gate their definition site. - // However, due to current limitations of the implementation (caused by us performing - // selection in AstConv), IATs can lead to cycle errors (#108491, #110106) which mask the - // feature-gate error, needlessly confusing users that use IATs by accident (#113265). - if !tcx.features().inherent_associated_types { - return Ok(None); - } - - let candidates: Vec<_> = tcx - .inherent_impls(adt_did)? - .iter() - .filter_map(|&impl_| Some((impl_, self.lookup_assoc_ty_unchecked(name, block, impl_)?))) - .collect(); - - if candidates.is_empty() { - return Ok(None); - } - - // - // Select applicable inherent associated type candidates modulo regions. - // - - // In contexts that have no inference context, just make a new one. - // We do need a local variable to store it, though. - let infcx_; - let infcx = match self.infcx() { - Some(infcx) => infcx, - None => { - assert!(!self_ty.has_infer()); - infcx_ = tcx.infer_ctxt().ignoring_regions().build(); - &infcx_ - } - }; - - // FIXME(inherent_associated_types): Acquiring the ParamEnv this early leads to cycle errors - // when inside of an ADT (#108491) or where clause. - let param_env = tcx.param_env(block.owner); - - let mut universes = if self_ty.has_escaping_bound_vars() { - vec![None; self_ty.outer_exclusive_binder().as_usize()] - } else { - vec![] - }; - - let (impl_, (assoc_item, def_scope)) = crate::traits::with_replaced_escaping_bound_vars( - infcx, - &mut universes, - self_ty, - |self_ty| { - self.select_inherent_assoc_type_candidates( - infcx, name, span, self_ty, param_env, candidates, - ) - }, - )?; - - self.check_assoc_ty(assoc_item, name, def_scope, block, span); - - // FIXME(fmease): Currently creating throwaway `parent_args` to please - // `create_args_for_associated_item`. Modify the latter instead (or sth. similar) to - // not require the parent args logic. - let parent_args = ty::GenericArgs::identity_for_item(tcx, impl_); - let args = self.create_args_for_associated_item(span, assoc_item, segment, parent_args); - let args = tcx.mk_args_from_iter( - std::iter::once(ty::GenericArg::from(self_ty)) - .chain(args.into_iter().skip(parent_args.len())), - ); - - let ty = Ty::new_alias(tcx, ty::Inherent, ty::AliasTy::new(tcx, assoc_item, args)); - - Ok(Some((ty, assoc_item))) - } - - fn select_inherent_assoc_type_candidates( - &self, - infcx: &InferCtxt<'tcx>, - name: Ident, - span: Span, - self_ty: Ty<'tcx>, - param_env: ParamEnv<'tcx>, - candidates: Vec<(DefId, (DefId, DefId))>, - ) -> Result<(DefId, (DefId, DefId)), ErrorGuaranteed> { - let tcx = self.tcx(); - let mut fulfillment_errors = Vec::new(); - - let applicable_candidates: Vec<_> = candidates - .iter() - .copied() - .filter(|&(impl_, _)| { - infcx.probe(|_| { - let ocx = ObligationCtxt::new(infcx); - let self_ty = ocx.normalize(&ObligationCause::dummy(), param_env, self_ty); - - let impl_args = infcx.fresh_args_for_item(span, impl_); - let impl_ty = tcx.type_of(impl_).instantiate(tcx, impl_args); - let impl_ty = ocx.normalize(&ObligationCause::dummy(), param_env, impl_ty); - - // Check that the self types can be related. - if ocx.eq(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() { - return false; - } - - // Check whether the impl imposes obligations we have to worry about. - let impl_bounds = tcx.predicates_of(impl_).instantiate(tcx, impl_args); - let impl_bounds = - ocx.normalize(&ObligationCause::dummy(), param_env, impl_bounds); - let impl_obligations = traits::predicates_for_generics( - |_, _| ObligationCause::dummy(), - param_env, - impl_bounds, - ); - ocx.register_obligations(impl_obligations); - - let mut errors = ocx.select_where_possible(); - if !errors.is_empty() { - fulfillment_errors.append(&mut errors); - return false; - } - - true - }) - }) - .collect(); - - match &applicable_candidates[..] { - &[] => Err(self.complain_about_inherent_assoc_type_not_found( - name, - self_ty, - candidates, - fulfillment_errors, - span, - )), - - &[applicable_candidate] => Ok(applicable_candidate), - - &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc_type( - name, - applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(), - span, - )), - } - } - - fn lookup_assoc_ty( - &self, - name: Ident, - block: hir::HirId, - span: Span, - scope: DefId, - ) -> Option { - let (item, def_scope) = self.lookup_assoc_ty_unchecked(name, block, scope)?; - self.check_assoc_ty(item, name, def_scope, block, span); - Some(item) - } - - fn lookup_assoc_ty_unchecked( - &self, - name: Ident, - block: hir::HirId, - scope: DefId, - ) -> Option<(DefId, DefId)> { - let tcx = self.tcx(); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block); - - // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` - // instead of calling `filter_by_name_and_kind` which would needlessly normalize the - // `ident` again and again. - let item = tcx.associated_items(scope).in_definition_order().find(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident(tcx).normalize_to_macros_2_0() == ident - })?; - - Some((item.def_id, def_scope)) - } - - fn check_assoc_ty( - &self, - item: DefId, - name: Ident, - def_scope: DefId, - block: hir::HirId, - span: Span, - ) { - let tcx = self.tcx(); - let kind = DefKind::AssocTy; - - if !tcx.visibility(item).is_accessible_from(def_scope, tcx) { - let kind = tcx.def_kind_descr(kind, item); - let msg = format!("{kind} `{name}` is private"); - let def_span = tcx.def_span(item); - let reported = tcx - .dcx() - .struct_span_err(span, msg) - .with_code(E0624) - .with_span_label(span, format!("private {kind}")) - .with_span_label(def_span, format!("{kind} defined here")) - .emit(); - self.set_tainted_by_errors(reported); - } - tcx.check_stability(item, Some(block), span, None); - } - - fn probe_traits_that_match_assoc_ty( - &self, - qself_ty: Ty<'tcx>, - assoc_ident: Ident, - ) -> Vec { - let tcx = self.tcx(); - - // In contexts that have no inference context, just make a new one. - // We do need a local variable to store it, though. - let infcx_; - let infcx = if let Some(infcx) = self.infcx() { - infcx - } else { - assert!(!qself_ty.has_infer()); - infcx_ = tcx.infer_ctxt().build(); - &infcx_ - }; - - tcx.all_traits() - .filter(|trait_def_id| { - // Consider only traits with the associated type - tcx.associated_items(*trait_def_id) - .in_definition_order() - .any(|i| { - i.kind.namespace() == Namespace::TypeNS - && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident - && matches!(i.kind, ty::AssocKind::Type) - }) - // Consider only accessible traits - && tcx.visibility(*trait_def_id) - .is_accessible_from(self.item_def_id(), tcx) - && tcx.all_impls(*trait_def_id) - .any(|impl_def_id| { - let impl_header = tcx.impl_trait_header(impl_def_id); - impl_header.is_some_and(|header| { - let trait_ref = header.trait_ref.instantiate( - tcx, - infcx.fresh_args_for_item(DUMMY_SP, impl_def_id), - ); - - let value = tcx.fold_regions(qself_ty, |_, _| tcx.lifetimes.re_erased); - // FIXME: Don't bother dealing with non-lifetime binders here... - if value.has_escaping_bound_vars() { - return false; - } - infcx - .can_eq( - ty::ParamEnv::empty(), - trait_ref.self_ty(), - value, - ) && header.polarity != ty::ImplPolarity::Negative - }) - }) - }) - .map(|trait_def_id| tcx.def_path_str(trait_def_id)) - .collect() - } - - fn qpath_to_ty( - &self, - span: Span, - opt_self_ty: Option>, - item_def_id: DefId, - trait_segment: &hir::PathSegment<'tcx>, - item_segment: &hir::PathSegment<'tcx>, - constness: ty::BoundConstness, - ) -> Ty<'tcx> { - let tcx = self.tcx(); - - let trait_def_id = tcx.parent(item_def_id); - - debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id); - - let Some(self_ty) = opt_self_ty else { - let path_str = tcx.def_path_str(trait_def_id); - - let def_id = self.item_def_id(); - - debug!("qpath_to_ty: self.item_def_id()={:?}", def_id); - - let parent_def_id = def_id - .as_local() - .map(|def_id| tcx.local_def_id_to_hir_id(def_id)) - .map(|hir_id| tcx.hir().get_parent_item(hir_id).to_def_id()); - - debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id); - - // If the trait in segment is the same as the trait defining the item, - // use the `` syntax in the error. - let is_part_of_self_trait_constraints = def_id == trait_def_id; - let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id); - - let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { - vec!["Self".to_string()] - } else { - // Find all the types that have an `impl` for the trait. - tcx.all_impls(trait_def_id) - .filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id)) - .filter(|header| { - // Consider only accessible traits - tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx) - && header.polarity != ty::ImplPolarity::Negative - }) - .map(|header| header.trait_ref.instantiate_identity().self_ty()) - // We don't care about blanket impls. - .filter(|self_ty| !self_ty.has_non_region_param()) - .map(|self_ty| tcx.erase_regions(self_ty).to_string()) - .collect() - }; - // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that - // references the trait. Relevant for the first case in - // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` - let reported = self.report_ambiguous_associated_type( - span, - &type_names, - &[path_str], - item_segment.ident.name, - ); - return Ty::new_error(tcx, reported); - }; - - debug!("qpath_to_ty: self_type={:?}", self_ty); - - let trait_ref = self.ast_path_to_mono_trait_ref( - span, - trait_def_id, - self_ty, - trait_segment, - false, - constness, - ); - - let item_args = - self.create_args_for_associated_item(span, item_def_id, item_segment, trait_ref.args); - - debug!("qpath_to_ty: trait_ref={:?}", trait_ref); - - Ty::new_projection(tcx, item_def_id, item_args) - } - - pub fn prohibit_generics<'a>( - &self, - segments: impl Iterator> + Clone, - extend: impl Fn(&mut Diag<'_>), - ) -> bool { - let args = segments.clone().flat_map(|segment| segment.args().args); - - let (lt, ty, ct, inf) = - args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg { - hir::GenericArg::Lifetime(_) => (true, ty, ct, inf), - hir::GenericArg::Type(_) => (lt, true, ct, inf), - hir::GenericArg::Const(_) => (lt, ty, true, inf), - hir::GenericArg::Infer(_) => (lt, ty, ct, true), - }); - let mut emitted = false; - if lt || ty || ct || inf { - let types_and_spans: Vec<_> = segments - .clone() - .flat_map(|segment| { - if segment.args().args.is_empty() { - None - } else { - Some(( - match segment.res { - Res::PrimTy(ty) => { - format!("{} `{}`", segment.res.descr(), ty.name()) - } - Res::Def(_, def_id) - if let Some(name) = self.tcx().opt_item_name(def_id) => - { - format!("{} `{name}`", segment.res.descr()) - } - Res::Err => "this type".to_string(), - _ => segment.res.descr().to_string(), - }, - segment.ident.span, - )) - } - }) - .collect(); - let this_type = match &types_and_spans[..] { - [.., _, (last, _)] => format!( - "{} and {last}", - types_and_spans[..types_and_spans.len() - 1] - .iter() - .map(|(x, _)| x.as_str()) - .intersperse(", ") - .collect::() - ), - [(only, _)] => only.to_string(), - [] => "this type".to_string(), - }; - - let arg_spans: Vec = args.map(|arg| arg.span()).collect(); - - let mut kinds = Vec::with_capacity(4); - if lt { - kinds.push("lifetime"); - } - if ty { - kinds.push("type"); - } - if ct { - kinds.push("const"); - } - if inf { - kinds.push("generic"); - } - let (kind, s) = match kinds[..] { - [.., _, last] => ( - format!( - "{} and {last}", - kinds[..kinds.len() - 1] - .iter() - .map(|&x| x) - .intersperse(", ") - .collect::() - ), - "s", - ), - [only] => (only.to_string(), ""), - [] => unreachable!("expected at least one generic to prohibit"), - }; - let last_span = *arg_spans.last().unwrap(); - let span: MultiSpan = arg_spans.into(); - let mut err = struct_span_code_err!( - self.tcx().dcx(), - span, - E0109, - "{kind} arguments are not allowed on {this_type}", - ); - err.span_label(last_span, format!("{kind} argument{s} not allowed")); - for (what, span) in types_and_spans { - err.span_label(span, format!("not allowed on {what}")); - } - extend(&mut err); - self.set_tainted_by_errors(err.emit()); - emitted = true; - } - - for segment in segments { - // Only emit the first error to avoid overloading the user with error messages. - if let Some(b) = segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span, None); - return true; - } - } - emitted - } - - // FIXME(eddyb, varkor) handle type paths here too, not just value ones. - pub fn def_ids_for_value_path_segments( - &self, - segments: &[hir::PathSegment<'_>], - self_ty: Option>, - kind: DefKind, - def_id: DefId, - span: Span, - ) -> Vec { - // We need to extract the type parameters supplied by the user in - // the path `path`. Due to the current setup, this is a bit of a - // tricky-process; the problem is that resolve only tells us the - // end-point of the path resolution, and not the intermediate steps. - // Luckily, we can (at least for now) deduce the intermediate steps - // just from the end-point. - // - // There are basically five cases to consider: - // - // 1. Reference to a constructor of a struct: - // - // struct Foo(...) - // - // In this case, the parameters are declared in the type space. - // - // 2. Reference to a constructor of an enum variant: - // - // enum E { Foo(...) } - // - // In this case, the parameters are defined in the type space, - // but may be specified either on the type or the variant. - // - // 3. Reference to a fn item or a free constant: - // - // fn foo() { } - // - // In this case, the path will again always have the form - // `a::b::foo::` where only the final segment should have - // type parameters. However, in this case, those parameters are - // declared on a value, and hence are in the `FnSpace`. - // - // 4. Reference to a method or an associated constant: - // - // impl SomeStruct { - // fn foo(...) - // } - // - // Here we can have a path like - // `a::b::SomeStruct::::foo::`, in which case parameters - // may appear in two places. The penultimate segment, - // `SomeStruct::`, contains parameters in TypeSpace, and the - // final segment, `foo::` contains parameters in fn space. - // - // The first step then is to categorize the segments appropriately. - - let tcx = self.tcx(); - - assert!(!segments.is_empty()); - let last = segments.len() - 1; - - let mut path_segs = vec![]; - - match kind { - // Case 1. Reference to a struct constructor. - DefKind::Ctor(CtorOf::Struct, ..) => { - // Everything but the final segment should have no - // parameters at all. - let generics = tcx.generics_of(def_id); - // Variant and struct constructors use the - // generics of their parent type definition. - let generics_def_id = generics.parent.unwrap_or(def_id); - path_segs.push(PathSeg(generics_def_id, last)); - } - - // Case 2. Reference to a variant constructor. - DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => { - let (generics_def_id, index) = if let Some(self_ty) = self_ty { - let adt_def = self.probe_adt(span, self_ty).unwrap(); - debug_assert!(adt_def.is_enum()); - (adt_def.did(), last) - } else if last >= 1 && segments[last - 1].args.is_some() { - // Everything but the penultimate segment should have no - // parameters at all. - let mut def_id = def_id; - - // `DefKind::Ctor` -> `DefKind::Variant` - if let DefKind::Ctor(..) = kind { - def_id = tcx.parent(def_id); - } - - // `DefKind::Variant` -> `DefKind::Enum` - let enum_def_id = tcx.parent(def_id); - (enum_def_id, last - 1) - } else { - // FIXME: lint here recommending `Enum::<...>::Variant` form - // instead of `Enum::Variant::<...>` form. - - // Everything but the final segment should have no - // parameters at all. - let generics = tcx.generics_of(def_id); - // Variant and struct constructors use the - // generics of their parent type definition. - (generics.parent.unwrap_or(def_id), last) - }; - path_segs.push(PathSeg(generics_def_id, index)); - } - - // Case 3. Reference to a top-level value. - DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } => { - path_segs.push(PathSeg(def_id, last)); - } - - // Case 4. Reference to a method or associated const. - DefKind::AssocFn | DefKind::AssocConst => { - if segments.len() >= 2 { - let generics = tcx.generics_of(def_id); - path_segs.push(PathSeg(generics.parent.unwrap(), last - 1)); - } - path_segs.push(PathSeg(def_id, last)); - } - - kind => bug!("unexpected definition kind {:?} for {:?}", kind, def_id), - } - - debug!("path_segs = {:?}", path_segs); - - path_segs - } - - /// Check a type `Path` and convert it to a `Ty`. - pub fn res_to_ty( - &self, - opt_self_ty: Option>, - path: &hir::Path<'tcx>, - hir_id: hir::HirId, - permit_variants: bool, - ) -> Ty<'tcx> { - let tcx = self.tcx(); - - debug!( - "res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?})", - path.res, opt_self_ty, path.segments - ); - - let span = path.span; - match path.res { - Res::Def(DefKind::OpaqueTy, did) => { - // Check for desugared `impl Trait`. - assert!(tcx.is_type_alias_impl_trait(did)); - let item_segment = path.segments.split_last().unwrap(); - self.prohibit_generics(item_segment.1.iter(), |err| { - err.note("`impl Trait` types can't have type parameters"); - }); - let args = self.ast_path_args_for_ty(span, did, item_segment.0); - Ty::new_opaque(tcx, did, args) - } - Res::Def( - DefKind::Enum - | DefKind::TyAlias - | DefKind::Struct - | DefKind::Union - | DefKind::ForeignTy, - did, - ) => { - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.split_last().unwrap().1.iter(), |_| {}); - self.ast_path_to_ty(span, did, path.segments.last().unwrap()) - } - Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { - // Convert "variant type" as if it were a real type. - // The resulting `Ty` is type of the variant's enum for now. - assert_eq!(opt_self_ty, None); - - let path_segs = - self.def_ids_for_value_path_segments(path.segments, None, kind, def_id, span); - let generic_segs: FxHashSet<_> = - path_segs.iter().map(|PathSeg(_, index)| index).collect(); - self.prohibit_generics( - path.segments.iter().enumerate().filter_map(|(index, seg)| { - if !generic_segs.contains(&index) { Some(seg) } else { None } - }), - |err| { - err.note("enum variants can't have type parameters"); - }, - ); - - let PathSeg(def_id, index) = path_segs.last().unwrap(); - self.ast_path_to_ty(span, *def_id, &path.segments[*index]) - } - Res::Def(DefKind::TyParam, def_id) => { - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.iter(), |err| { - if let Some(span) = tcx.def_ident_span(def_id) { - let name = tcx.item_name(def_id); - err.span_note(span, format!("type parameter `{name}` defined here")); - } - }); - self.hir_id_to_bound_ty(hir_id) - } - Res::SelfTyParam { .. } => { - // `Self` in trait or type alias. - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.iter(), |err| { - if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments { - err.span_suggestion_verbose( - ident.span.shrink_to_hi().to(args.span_ext), - "the `Self` type doesn't accept type parameters", - "", - Applicability::MaybeIncorrect, - ); - } - }); - tcx.types.self_param - } - Res::SelfTyAlias { alias_to: def_id, forbid_generic, .. } => { - // `Self` in impl (we know the concrete type). - assert_eq!(opt_self_ty, None); - // Try to evaluate any array length constants. - let ty = tcx.at(span).type_of(def_id).instantiate_identity(); - let span_of_impl = tcx.span_of_impl(def_id); - self.prohibit_generics(path.segments.iter(), |err| { - let def_id = match *ty.kind() { - ty::Adt(self_def, _) => self_def.did(), - _ => return, - }; - - let type_name = tcx.item_name(def_id); - let span_of_ty = tcx.def_ident_span(def_id); - let generics = tcx.generics_of(def_id).count(); - - let msg = format!("`Self` is of type `{ty}`"); - if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { - let mut span: MultiSpan = vec![t_sp].into(); - span.push_span_label( - i_sp, - format!("`Self` is on type `{type_name}` in this `impl`"), - ); - let mut postfix = ""; - if generics == 0 { - postfix = ", which doesn't have generic parameters"; - } - span.push_span_label( - t_sp, - format!("`Self` corresponds to this type{postfix}"), - ); - err.span_note(span, msg); - } else { - err.note(msg); - } - for segment in path.segments { - if let Some(args) = segment.args - && segment.ident.name == kw::SelfUpper - { - if generics == 0 { - // FIXME(estebank): we could also verify that the arguments being - // work for the `enum`, instead of just looking if it takes *any*. - err.span_suggestion_verbose( - segment.ident.span.shrink_to_hi().to(args.span_ext), - "the `Self` type doesn't accept type parameters", - "", - Applicability::MachineApplicable, - ); - return; - } else { - err.span_suggestion_verbose( - segment.ident.span, - format!( - "the `Self` type doesn't accept type parameters, use the \ - concrete type's name `{type_name}` instead if you want to \ - specify its type parameters" - ), - type_name, - Applicability::MaybeIncorrect, - ); - } - } - } - }); - // HACK(min_const_generics): Forbid generic `Self` types - // here as we can't easily do that during nameres. - // - // We do this before normalization as we otherwise allow - // ```rust - // trait AlwaysApplicable { type Assoc; } - // impl AlwaysApplicable for T { type Assoc = usize; } - // - // trait BindsParam { - // type ArrayTy; - // } - // impl BindsParam for ::Assoc { - // type ArrayTy = [u8; Self::MAX]; - // } - // ``` - // Note that the normalization happens in the param env of - // the anon const, which is empty. This is why the - // `AlwaysApplicable` impl needs a `T: ?Sized` bound for - // this to compile if we were to normalize here. - if forbid_generic && ty.has_param() { - let mut err = tcx.dcx().struct_span_err( - path.span, - "generic `Self` types are currently not permitted in anonymous constants", - ); - if let Some(hir::Node::Item(&hir::Item { - kind: hir::ItemKind::Impl(impl_), - .. - })) = tcx.hir().get_if_local(def_id) - { - err.span_note(impl_.self_ty.span, "not a concrete type"); - } - let reported = err.emit(); - self.set_tainted_by_errors(reported); - Ty::new_error(tcx, reported) - } else { - ty - } - } - Res::Def(DefKind::AssocTy, def_id) => { - debug_assert!(path.segments.len() >= 2); - self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {}); - // HACK: until we support ``, assume all of them are. - let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) { - ty::BoundConstness::ConstIfConst - } else { - ty::BoundConstness::NotConst - }; - self.qpath_to_ty( - span, - opt_self_ty, - def_id, - &path.segments[path.segments.len() - 2], - path.segments.last().unwrap(), - constness, - ) - } - Res::PrimTy(prim_ty) => { - assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.iter(), |err| { - let name = prim_ty.name_str(); - for segment in path.segments { - if let Some(args) = segment.args { - err.span_suggestion_verbose( - segment.ident.span.shrink_to_hi().to(args.span_ext), - format!("primitive type `{name}` doesn't have generic parameters"), - "", - Applicability::MaybeIncorrect, - ); - } - } - }); - match prim_ty { - hir::PrimTy::Bool => tcx.types.bool, - hir::PrimTy::Char => tcx.types.char, - hir::PrimTy::Int(it) => Ty::new_int(tcx, ty::int_ty(it)), - hir::PrimTy::Uint(uit) => Ty::new_uint(tcx, ty::uint_ty(uit)), - hir::PrimTy::Float(ft) => Ty::new_float(tcx, ty::float_ty(ft)), - hir::PrimTy::Str => tcx.types.str_, - } - } - Res::Err => { - let e = self - .tcx() - .dcx() - .span_delayed_bug(path.span, "path with `Res::Err` but no error emitted"); - self.set_tainted_by_errors(e); - Ty::new_error(self.tcx(), e) - } - _ => span_bug!(span, "unexpected resolution: {:?}", path.res), - } - } - - // Converts a hir id corresponding to a type parameter to - // a early-bound `ty::Param` or late-bound `ty::Bound`. - pub(crate) fn hir_id_to_bound_ty(&self, hir_id: hir::HirId) -> Ty<'tcx> { - let tcx = self.tcx(); - match tcx.named_bound_var(hir_id) { - Some(rbv::ResolvedArg::LateBound(debruijn, index, def_id)) => { - let name = tcx.item_name(def_id); - let br = ty::BoundTy { - var: ty::BoundVar::from_u32(index), - kind: ty::BoundTyKind::Param(def_id, name), - }; - Ty::new_bound(tcx, debruijn, br) - } - Some(rbv::ResolvedArg::EarlyBound(def_id)) => { - let def_id = def_id.expect_local(); - let item_def_id = tcx.hir().ty_param_owner(def_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id.to_def_id()]; - Ty::new_param(tcx, index, tcx.hir().ty_param_name(def_id)) - } - Some(rbv::ResolvedArg::Error(guar)) => Ty::new_error(tcx, guar), - arg => bug!("unexpected bound var resolution for {hir_id:?}: {arg:?}"), - } - } - - // Converts a hir id corresponding to a const parameter to - // a early-bound `ConstKind::Param` or late-bound `ConstKind::Bound`. - pub(crate) fn hir_id_to_bound_const( - &self, - hir_id: hir::HirId, - param_ty: Ty<'tcx>, - ) -> Const<'tcx> { - let tcx = self.tcx(); - match tcx.named_bound_var(hir_id) { - Some(rbv::ResolvedArg::EarlyBound(def_id)) => { - // Find the name and index of the const parameter by indexing the generics of - // the parent item and construct a `ParamConst`. - 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), param_ty) - } - Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => { - ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index), param_ty) - } - Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar, param_ty), - arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", hir_id), - } - } - - /// Parses the programmer's textual representation of a type into our - /// internal notion of a type. - pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - self.ast_ty_to_ty_inner(ast_ty, false, false) - } - - /// Parses the programmer's textual representation of a type into our - /// internal notion of a type. This is meant to be used within a path. - pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - self.ast_ty_to_ty_inner(ast_ty, false, true) - } - - fn check_delegation_constraints(&self, sig_id: DefId, span: Span, emit: bool) -> bool { - let mut error_occured = false; - let sig_span = self.tcx().def_span(sig_id); - let mut try_emit = |descr| { - if emit { - self.tcx().dcx().emit_err(crate::errors::NotSupportedDelegation { - span, - descr, - callee_span: sig_span, - }); - } - error_occured = true; - }; - - if let Some(node) = self.tcx().hir().get_if_local(sig_id) - && let Some(decl) = node.fn_decl() - && let hir::FnRetTy::Return(ty) = decl.output - && let hir::TyKind::InferDelegation(_, _) = ty.kind - { - try_emit("recursive delegation"); - } - - let sig = self.tcx().fn_sig(sig_id).instantiate_identity(); - if sig.output().has_opaque_types() { - try_emit("delegation to a function with opaque type"); - } - - let sig_generics = self.tcx().generics_of(sig_id); - let parent = self.tcx().parent(self.item_def_id()); - let parent_generics = self.tcx().generics_of(parent); - - let parent_is_trait = (self.tcx().def_kind(parent) == DefKind::Trait) as usize; - let sig_has_self = sig_generics.has_self as usize; - - if sig_generics.count() > sig_has_self || parent_generics.count() > parent_is_trait { - try_emit("delegation with early bound generics"); - } - - if self.tcx().asyncness(sig_id) == ty::Asyncness::Yes { - try_emit("delegation to async functions"); - } - - if self.tcx().constness(sig_id) == hir::Constness::Const { - try_emit("delegation to const functions"); - } - - if sig.c_variadic() { - try_emit("delegation to variadic functions"); - // variadic functions are also `unsafe` and `extern "C"`. - // Do not emit same error multiple times. - return error_occured; - } - - if let hir::Unsafety::Unsafe = sig.unsafety() { - try_emit("delegation to unsafe functions"); - } - - if abi::Abi::Rust != sig.abi() { - try_emit("delegation to non Rust ABI functions"); - } - - error_occured - } - - fn ty_from_delegation( - &self, - sig_id: DefId, - idx: hir::InferDelegationKind, - span: Span, - ) -> Ty<'tcx> { - if self.check_delegation_constraints(sig_id, span, idx == hir::InferDelegationKind::Output) - { - let e = self.tcx().dcx().span_delayed_bug(span, "not supported delegation case"); - self.set_tainted_by_errors(e); - return Ty::new_error(self.tcx(), e); - }; - let sig = self.tcx().fn_sig(sig_id); - let sig_generics = self.tcx().generics_of(sig_id); - - let parent = self.tcx().parent(self.item_def_id()); - let parent_def_kind = self.tcx().def_kind(parent); - - let sig = if let DefKind::Impl { .. } = parent_def_kind - && sig_generics.has_self - { - // Generic params can't be here except the trait self type. - // They are not supported yet. - assert_eq!(sig_generics.count(), 1); - assert_eq!(self.tcx().generics_of(parent).count(), 0); - - let self_ty = self.tcx().type_of(parent).instantiate_identity(); - let generic_self_ty = ty::GenericArg::from(self_ty); - let args = self.tcx().mk_args_from_iter(std::iter::once(generic_self_ty)); - sig.instantiate(self.tcx(), args) - } else { - sig.instantiate_identity() - }; - - // Bound vars are also inherited from `sig_id`. They will be - // rebinded later in `ty_of_fn`. - let sig = sig.skip_binder(); - - match idx { - hir::InferDelegationKind::Input(id) => sig.inputs()[id], - hir::InferDelegationKind::Output => sig.output(), - } - } - - /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait - /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. - #[instrument(level = "debug", skip(self), ret)] - fn ast_ty_to_ty_inner( - &self, - ast_ty: &hir::Ty<'tcx>, - borrowed: bool, - in_path: bool, - ) -> Ty<'tcx> { - let tcx = self.tcx(); - - let result_ty = match &ast_ty.kind { - hir::TyKind::InferDelegation(sig_id, idx) => { - self.ty_from_delegation(*sig_id, *idx, ast_ty.span) - } - hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.ast_ty_to_ty(ty)), - hir::TyKind::Ptr(mt) => { - Ty::new_ptr(tcx, ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl }) - } - hir::TyKind::Ref(region, mt) => { - let r = self.ast_region_to_region(region, None); - debug!(?r); - let t = self.ast_ty_to_ty_inner(mt.ty, true, false); - Ty::new_ref(tcx, r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl }) - } - hir::TyKind::Never => tcx.types.never, - hir::TyKind::Tup(fields) => { - Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.ast_ty_to_ty(t))) - } - hir::TyKind::AnonAdt(item_id) => { - let did = item_id.owner_id.def_id; - let adt_def = tcx.adt_def(did); - let generics = tcx.generics_of(did); - - debug!("ast_ty_to_ty_inner(AnonAdt): generics={:?}", generics); - let args = ty::GenericArgs::for_item(tcx, did.to_def_id(), |param, _| { - tcx.mk_param_from_def(param) - }); - debug!("ast_ty_to_ty_inner(AnonAdt): args={:?}", args); - - Ty::new_adt(tcx, adt_def, tcx.mk_args(args)) - } - hir::TyKind::BareFn(bf) => { - require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span); - - Ty::new_fn_ptr( - tcx, - self.ty_of_fn(ast_ty.hir_id, bf.unsafety, bf.abi, bf.decl, None, Some(ast_ty)), - ) - } - hir::TyKind::TraitObject(bounds, lifetime, repr) => { - self.maybe_lint_bare_trait(ast_ty, in_path); - let repr = match repr { - TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, - TraitObjectSyntax::DynStar => ty::DynStar, - }; - - self.conv_object_ty_poly_trait_ref( - ast_ty.span, - ast_ty.hir_id, - bounds, - lifetime, - borrowed, - repr, - ) - } - hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => { - debug!(?maybe_qself, ?path); - let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself)); - self.res_to_ty(opt_self_ty, path, ast_ty.hir_id, false) - } - &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => { - let opaque_ty = tcx.hir().item(item_id); - - match opaque_ty.kind { - hir::ItemKind::OpaqueTy(&hir::OpaqueTy { .. }) => { - let local_def_id = item_id.owner_id.def_id; - // If this is an RPITIT and we are using the new RPITIT lowering scheme, we - // generate the def_id of an associated type for the trait and return as - // type a projection. - let def_id = if in_trait { - tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id() - } else { - local_def_id.to_def_id() - }; - self.impl_trait_ty_to_ty(def_id, lifetimes, in_trait) - } - ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), - } - } - hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => { - debug!(?qself, ?segment); - let ty = self.ast_ty_to_ty_inner(qself, false, true); - self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false) - .map(|(ty, _, _)| ty) - .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) - } - &hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => { - let def_id = tcx.require_lang_item(lang_item, Some(span)); - let (args, _) = self.create_args_for_ast_path( - span, - def_id, - &[], - &hir::PathSegment::invalid(), - None, - ty::BoundConstness::NotConst, - ); - tcx.at(span).type_of(def_id).instantiate(tcx, args) - } - hir::TyKind::Array(ty, length) => { - let length = match length { - hir::ArrayLen::Infer(inf) => self.ct_infer(tcx.types.usize, None, inf.span), - hir::ArrayLen::Body(constant) => { - ty::Const::from_anon_const(tcx, constant.def_id) - } - }; - - Ty::new_array_with_const_len(tcx, self.ast_ty_to_ty(ty), length) - } - hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(), - hir::TyKind::Infer => { - // Infer also appears as the type of arguments or return - // values in an ExprKind::Closure, or as - // the type of local variables. Both of these cases are - // handled specially and will not descend into this routine. - self.ty_infer(None, ast_ty.span) - } - hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), - }; - - self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span); - result_ty - } - - #[instrument(level = "debug", skip(self), ret)] - fn impl_trait_ty_to_ty( - &self, - def_id: DefId, - lifetimes: &[hir::GenericArg<'_>], - in_trait: bool, - ) -> Ty<'tcx> { - debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes); - let tcx = self.tcx(); - - let generics = tcx.generics_of(def_id); - - debug!("impl_trait_ty_to_ty: generics={:?}", generics); - let args = ty::GenericArgs::for_item(tcx, def_id, |param, _| { - // We use `generics.count() - lifetimes.len()` here instead of `generics.parent_count` - // since return-position impl trait in trait squashes all of the generics from its source fn - // into its own generics, so the opaque's "own" params isn't always just lifetimes. - if let Some(i) = (param.index as usize).checked_sub(generics.count() - lifetimes.len()) - { - // Resolve our own lifetime parameters. - let GenericParamDefKind::Lifetime { .. } = param.kind else { - span_bug!( - tcx.def_span(param.def_id), - "only expected lifetime for opaque's own generics, got {:?}", - param.kind - ); - }; - let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else { - bug!( - "expected lifetime argument for param {param:?}, found {:?}", - &lifetimes[i] - ) - }; - self.ast_region_to_region(lifetime, None).into() - } else { - tcx.mk_param_from_def(param) - } - }); - debug!("impl_trait_ty_to_ty: args={:?}", args); - - if in_trait { - Ty::new_projection(tcx, def_id, args) - } else { - Ty::new_opaque(tcx, def_id, args) - } - } - - pub fn ty_of_arg(&self, ty: &hir::Ty<'tcx>, expected_ty: Option>) -> Ty<'tcx> { - match ty.kind { - hir::TyKind::Infer if let Some(expected_ty) = expected_ty => { - self.record_ty(ty.hir_id, expected_ty, ty.span); - expected_ty - } - _ => self.ast_ty_to_ty(ty), - } - } - - #[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)] - pub fn ty_of_fn( - &self, - hir_id: hir::HirId, - unsafety: hir::Unsafety, - abi: abi::Abi, - decl: &hir::FnDecl<'tcx>, - generics: Option<&hir::Generics<'_>>, - hir_ty: Option<&hir::Ty<'_>>, - ) -> ty::PolyFnSig<'tcx> { - let tcx = self.tcx(); - let bound_vars = if let hir::FnRetTy::Return(ret_ty) = decl.output - && let hir::TyKind::InferDelegation(sig_id, _) = ret_ty.kind - { - tcx.fn_sig(sig_id).skip_binder().bound_vars() - } else { - tcx.late_bound_vars(hir_id) - }; - debug!(?bound_vars); - - // We proactively collect all the inferred type params to emit a single error per fn def. - let mut visitor = HirPlaceholderCollector::default(); - let mut infer_replacements = vec![]; - - if let Some(generics) = generics { - walk_generics(&mut visitor, generics); - } - - let input_tys: Vec<_> = decl - .inputs - .iter() - .enumerate() - .map(|(i, a)| { - if let hir::TyKind::Infer = a.kind - && !self.allow_ty_infer() - { - if let Some(suggested_ty) = - self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) - { - infer_replacements.push((a.span, suggested_ty.to_string())); - return Ty::new_error_with_message( - self.tcx(), - a.span, - suggested_ty.to_string(), - ); - } - } - - // Only visit the type looking for `_` if we didn't fix the type above - visitor.visit_ty(a); - self.ty_of_arg(a, None) - }) - .collect(); - - let output_ty = match decl.output { - hir::FnRetTy::Return(output) => { - if let hir::TyKind::Infer = output.kind - && !self.allow_ty_infer() - && let Some(suggested_ty) = - self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) - { - infer_replacements.push((output.span, suggested_ty.to_string())); - Ty::new_error_with_message(self.tcx(), output.span, suggested_ty.to_string()) - } else { - visitor.visit_ty(output); - self.ast_ty_to_ty(output) - } - } - hir::FnRetTy::DefaultReturn(..) => Ty::new_unit(tcx), - }; - - debug!(?output_ty); - - let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi); - let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); - - if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) { - // We always collect the spans for placeholder types when evaluating `fn`s, but we - // only want to emit an error complaining about them if infer types (`_`) are not - // allowed. `allow_ty_infer` gates this behavior. We check for the presence of - // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. - - let mut diag = crate::collect::placeholder_type_error_diag( - tcx, - generics, - visitor.0, - infer_replacements.iter().map(|(s, _)| *s).collect(), - true, - hir_ty, - "function", - ); - - if !infer_replacements.is_empty() { - diag.multipart_suggestion( - format!( - "try replacing `_` with the type{} in the corresponding trait method signature", - rustc_errors::pluralize!(infer_replacements.len()), - ), - infer_replacements, - Applicability::MachineApplicable, - ); - } - - self.set_tainted_by_errors(diag.emit()); - } - - // Find any late-bound regions declared in return type that do - // not appear in the arguments. These are not well-formed. - // - // Example: - // for<'a> fn() -> &'a str <-- 'a is bad - // for<'a> fn(&'a String) -> &'a str <-- 'a is ok - let inputs = bare_fn_ty.inputs(); - let late_bound_in_args = - tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned())); - let output = bare_fn_ty.output(); - let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output); - - self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { - struct_span_code_err!( - tcx.dcx(), - decl.output.span(), - E0581, - "return type references {}, which is not constrained by the fn input types", - br_name - ) - }); - - bare_fn_ty - } - - /// Given a fn_hir_id for a impl function, suggest the type that is found on the - /// corresponding function in the trait that the impl implements, if it exists. - /// If arg_idx is Some, then it corresponds to an input type index, otherwise it - /// corresponds to the return type. - fn suggest_trait_fn_ty_for_impl_fn_infer( - &self, - fn_hir_id: hir::HirId, - arg_idx: Option, - ) -> Option> { - let tcx = self.tcx(); - let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) = - tcx.hir_node(fn_hir_id) - else { - return None; - }; - let i = tcx.parent_hir_node(fn_hir_id).expect_item().expect_impl(); - - let trait_ref = - self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty)); - - let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( - tcx, - *ident, - ty::AssocKind::Fn, - trait_ref.def_id, - )?; - - let fn_sig = tcx.fn_sig(assoc.def_id).instantiate( - tcx, - trait_ref.args.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)), - ); - let fn_sig = tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), fn_sig); - - Some(if let Some(arg_idx) = arg_idx { - *fn_sig.inputs().get(arg_idx)? - } else { - fn_sig.output() - }) - } - - #[instrument(level = "trace", skip(self, generate_err))] - fn validate_late_bound_regions( - &self, - constrained_regions: FxHashSet, - referenced_regions: FxHashSet, - generate_err: impl Fn(&str) -> Diag<'tcx>, - ) { - for br in referenced_regions.difference(&constrained_regions) { - let br_name = match *br { - ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon | ty::BrEnv => { - "an anonymous lifetime".to_string() - } - ty::BrNamed(_, name) => format!("lifetime `{name}`"), - }; - - let mut err = generate_err(&br_name); - - if let ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon = *br { - // The only way for an anonymous lifetime to wind up - // in the return type but **also** be unconstrained is - // if it only appears in "associated types" in the - // input. See #47511 and #62200 for examples. In this case, - // though we can easily give a hint that ought to be - // relevant. - err.note( - "lifetimes appearing in an associated or opaque type are not considered constrained", - ); - err.note("consider introducing a named lifetime parameter"); - } - - self.set_tainted_by_errors(err.emit()); - } - } - - /// Given the bounds on an object, determines what single region bound (if any) we can - /// use to summarize this type. The basic idea is that we will use the bound the user - /// provided, if they provided one, and otherwise search the supertypes of trait bounds - /// for region bounds. It may be that we can derive no bound at all, in which case - /// we return `None`. - #[instrument(level = "debug", skip(self, span), ret)] - fn compute_object_lifetime_bound( - &self, - span: Span, - existential_predicates: &'tcx ty::List>, - ) -> Option> // if None, use the default - { - let tcx = self.tcx(); - - // No explicit region bound specified. Therefore, examine trait - // bounds and see if we can derive region bounds from those. - let derived_region_bounds = object_region_bounds(tcx, existential_predicates); - - // If there are no derived region bounds, then report back that we - // can find no region bound. The caller will use the default. - if derived_region_bounds.is_empty() { - return None; - } - - // If any of the derived region bounds are 'static, that is always - // the best choice. - if derived_region_bounds.iter().any(|r| r.is_static()) { - return Some(tcx.lifetimes.re_static); - } - - // Determine whether there is exactly one unique region in the set - // of derived region bounds. If so, use that. Otherwise, report an - // error. - let r = derived_region_bounds[0]; - if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - self.set_tainted_by_errors(tcx.dcx().emit_err(AmbiguousLifetimeBound { span })); - } - Some(r) - } -} - -fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { - match kind { - ty::AssocKind::Fn => "function", - ty::AssocKind::Const => "constant", - ty::AssocKind::Type => "type", - } -} diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index b69f679880dbf..aafb5c1c0b4bc 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -1,5 +1,5 @@ -//! Bounds are restrictions applied to some types after they've been converted into the -//! `ty` form from the HIR. +//! Bounds are restrictions applied to some types after they've been lowered from the HIR to the +//! [`rustc_middle::ty`] form. use rustc_hir::LangItem; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -42,26 +42,22 @@ impl<'tcx> Bounds<'tcx> { tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, span: Span, - polarity: ty::ImplPolarity, + polarity: ty::PredicatePolarity, ) { - self.push_trait_bound_inner(tcx, trait_ref, span, polarity); - } - - fn push_trait_bound_inner( - &mut self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - span: Span, - polarity: ty::ImplPolarity, - ) { - self.clauses.push(( + let clause = ( trait_ref .map_bound(|trait_ref| { ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity }) }) .to_predicate(tcx), span, - )); + ); + // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands. + if tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { + self.clauses.insert(0, clause); + } else { + self.clauses.push(clause); + } } pub fn push_projection_bound( diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d1fed13ee9feb..32e1b19eaaeb9 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1,19 +1,19 @@ use crate::check::intrinsicck::InlineAsmCtxt; -use crate::errors::LinkageType; use super::compare_impl_item::check_type_bounds; use super::compare_impl_item::{compare_impl_method, compare_impl_ty}; use super::*; use rustc_attr as attr; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{codes::*, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::Node; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; -use rustc_infer::traits::{Obligation, TraitEngineExt as _}; +use rustc_infer::traits::Obligation; use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; +use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; -use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt}; @@ -22,12 +22,11 @@ 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; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; -use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _}; use rustc_type_ir::fold::TypeFoldable; use std::cell::LazyCell; @@ -345,10 +344,7 @@ fn check_opaque_meets_bounds<'tcx>( }; let param_env = tcx.param_env(defining_use_anchor); - let infcx = tcx - .infer_ctxt() - .with_opaque_type_inference(DefiningAnchor::bind(tcx, defining_use_anchor)) - .build(); + let infcx = tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).build(); let ocx = ObligationCtxt::new(&infcx); let args = match *origin { @@ -382,8 +378,8 @@ fn check_opaque_meets_bounds<'tcx>( Ok(()) => {} Err(ty_err) => { // Some types may be left "stranded" if they can't be reached - // from an astconv'd bound but they're mentioned in the HIR. This - // will happen, e.g., when a nested opaque is inside of a non- + // from a lowered rustc_middle bound but they're mentioned in the HIR. + // This will happen, e.g., when a nested opaque is inside of a non- // existent associated type, like `impl Trait`. // See . let ty_err = ty_err.to_string(tcx); @@ -478,6 +474,171 @@ fn sanity_check_found_hidden_type<'tcx>( } } +/// Check that the opaque's precise captures list is valid (if present). +/// We check this for regular `impl Trait`s and also RPITITs, even though the latter +/// are technically GATs. +/// +/// This function is responsible for: +/// 1. Checking that all type/const params are mention in the captures list. +/// 2. Checking that all lifetimes that are implicitly captured are mentioned. +/// 3. Asserting that all parameters mentioned in the captures list are invariant. +fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) { + let hir::OpaqueTy { precise_capturing_args, .. } = + *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let Some(precise_capturing_args) = precise_capturing_args else { + // No precise capturing args; nothing to validate + return; + }; + + let mut expected_captures = UnordSet::default(); + let mut shadowed_captures = UnordSet::default(); + let mut seen_params = UnordMap::default(); + let mut prev_non_lifetime_param = None; + for arg in precise_capturing_args { + let (hir_id, ident) = match *arg { + hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { + hir_id, + ident, + .. + }) => { + if prev_non_lifetime_param.is_none() { + prev_non_lifetime_param = Some(ident); + } + (hir_id, ident) + } + hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, ident, .. }) => { + if let Some(prev_non_lifetime_param) = prev_non_lifetime_param { + tcx.dcx().emit_err(errors::LifetimesMustBeFirst { + lifetime_span: ident.span, + name: ident.name, + other_span: prev_non_lifetime_param.span, + }); + } + (hir_id, ident) + } + }; + + let ident = ident.normalize_to_macros_2_0(); + if let Some(span) = seen_params.insert(ident, ident.span) { + tcx.dcx().emit_err(errors::DuplicatePreciseCapture { + name: ident.name, + first_span: span, + second_span: ident.span, + }); + } + + match tcx.named_bound_var(hir_id) { + Some(ResolvedArg::EarlyBound(def_id)) => { + expected_captures.insert(def_id); + + // Make sure we allow capturing these lifetimes through `Self` and + // `T::Assoc` projection syntax, too. These will occur when we only + // see lifetimes are captured after hir-lowering -- this aligns with + // the cases that were stabilized with the `impl_trait_projection` + // feature -- see . + if let DefKind::LifetimeParam = tcx.def_kind(def_id) + && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + }) = *tcx.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local()) + { + shadowed_captures.insert(def_id); + } + } + _ => { + tcx.dcx().span_delayed_bug( + tcx.hir().span(hir_id), + "parameter should have been resolved", + ); + } + } + } + + let variances = tcx.variances_of(opaque_def_id); + let mut def_id = Some(opaque_def_id.to_def_id()); + while let Some(generics) = def_id { + let generics = tcx.generics_of(generics); + def_id = generics.parent; + + for param in &generics.params { + if expected_captures.contains(¶m.def_id) { + assert_eq!( + variances[param.index as usize], + ty::Invariant, + "precise captured param should be invariant" + ); + continue; + } + // If a param is shadowed by a early-bound (duplicated) lifetime, then + // it may or may not be captured as invariant, depending on if it shows + // up through `Self` or `T::Assoc` syntax. + if shadowed_captures.contains(¶m.def_id) { + continue; + } + + match param.kind { + ty::GenericParamDefKind::Lifetime => { + let use_span = tcx.def_span(param.def_id); + let opaque_span = tcx.def_span(opaque_def_id); + // Check if the lifetime param was captured but isn't named in the precise captures list. + if variances[param.index as usize] == ty::Invariant { + if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id)) + && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + }) = *tcx + .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) + { + tcx.dcx().emit_err(errors::LifetimeNotCaptured { + opaque_span, + use_span, + param_span: tcx.def_span(def_id), + }); + } else { + // If the `use_span` is actually just the param itself, then we must + // have not duplicated the lifetime but captured the original. + // The "effective" `use_span` will be the span of the opaque itself, + // and the param span will be the def span of the param. + tcx.dcx().emit_err(errors::LifetimeNotCaptured { + opaque_span, + use_span: opaque_span, + param_span: use_span, + }); + } + continue; + } + } + ty::GenericParamDefKind::Type { .. } => { + if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::SelfTyNotCaptured { + trait_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + }); + } else { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::ParamNotCaptured { + param_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + kind: "type", + }); + } + } + ty::GenericParamDefKind::Const { .. } => { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::ParamNotCaptured { + param_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + kind: "const", + }); + } + } + } + } +} + fn is_enum_of_nonnullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, adt_def: AdtDef<'tcx>, @@ -499,17 +660,16 @@ fn is_enum_of_nonnullable_ptr<'tcx>( fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) { if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() { if match tcx.type_of(def_id).instantiate_identity().kind() { - ty::RawPtr(_) => false, + ty::RawPtr(_, _) => false, ty::Adt(adt_def, args) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *args), _ => true, } { - tcx.dcx().emit_err(LinkageType { span: tcx.def_span(def_id) }); + tcx.dcx().emit_err(errors::LinkageType { span: tcx.def_span(def_id) }); } } } 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); @@ -537,8 +697,14 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { } DefKind::Impl { of_trait } => { if of_trait && let Some(impl_trait_header) = tcx.impl_trait_header(def_id) { - check_impl_items_against_trait(tcx, def_id, impl_trait_header); - check_on_unimplemented(tcx, def_id); + if tcx + .ensure() + .coherent_trait(impl_trait_header.trait_ref.instantiate_identity().def_id) + .is_ok() + { + check_impl_items_against_trait(tcx, def_id, impl_trait_header); + check_on_unimplemented(tcx, def_id); + } } } DefKind::Trait => { @@ -571,6 +737,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_union(tcx, def_id); } DefKind::OpaqueTy => { + check_opaque_precise_captures(tcx, def_id); + let origin = tcx.opaque_type_origin(def_id); if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin @@ -899,7 +1067,7 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit(); return; } - let e = fields[FieldIdx::from_u32(0)].ty(tcx, args); + let e = fields[FieldIdx::ZERO].ty(tcx, args); if !fields.iter().all(|f| f.ty(tcx, args) == e) { struct_span_code_err!(tcx.dcx(), sp, E0076, "SIMD vector should be homogeneous") .with_span_label(sp, "SIMD elements must have the same type") @@ -934,10 +1102,13 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { // No: char, "fat" pointers, compound types match e.kind() { ty::Param(_) => (), // pass struct(T, T, T, T) through, let monomorphization catch errors - ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) => (), // struct(u8, u8, u8, u8) is ok + ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct(u8, u8, u8, u8) is ok ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct([T; N]) through, let monomorphization catch errors ty::Array(t, _clen) - if matches!(t.kind(), ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_)) => + if matches!( + t.kind(), + ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) + ) => { /* struct([f32; 4]) is ok */ } _ => { struct_span_code_err!( @@ -961,7 +1132,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { for r in attr::parse_repr_attr(tcx.sess, attr) { if let attr::ReprPacked(pack) = r && let Some(repr_pack) = repr.pack - && pack as u64 != repr_pack.bytes() + && pack != repr_pack { struct_span_code_err!( tcx.dcx(), @@ -1543,55 +1714,31 @@ fn opaque_type_cycle_error( err.emit() } -// FIXME(@lcnr): This should not be computed per coroutine, but instead once for -// each typeck root. pub(super) fn check_coroutine_obligations( tcx: TyCtxt<'_>, def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { - debug_assert!(tcx.is_coroutine(def_id.to_def_id())); + debug_assert!(!tcx.is_typeck_child(def_id.to_def_id())); - let typeck = tcx.typeck(def_id); - let param_env = tcx.param_env(typeck.hir_owner.def_id); + let typeck_results = tcx.typeck(def_id); + let param_env = tcx.param_env(def_id); - let coroutine_interior_predicates = &typeck.coroutine_interior_predicates[&def_id]; - debug!(?coroutine_interior_predicates); + debug!(?typeck_results.coroutine_stalled_predicates); let infcx = tcx .infer_ctxt() // typeck writeback gives us predicates with their regions erased. // As borrowck already has checked lifetimes, we do not need to do it again. .ignoring_regions() - // Bind opaque types to type checking root, as they should have been checked by borrowck, - // but may show up in some cases, like when (root) obligations are stalled in the new solver. - .with_opaque_type_inference(DefiningAnchor::bind(tcx, typeck.hir_owner.def_id)) + .with_opaque_type_inference(def_id) .build(); - let mut fulfillment_cx = >::new(&infcx); - for (predicate, cause) in coroutine_interior_predicates { - let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate); - fulfillment_cx.register_predicate_obligation(&infcx, obligation); - } - - if (tcx.features().unsized_locals || tcx.features().unsized_fn_params) - && let Some(coroutine) = tcx.mir_coroutine_witnesses(def_id) - { - for field_ty in coroutine.field_tys.iter() { - fulfillment_cx.register_bound( - &infcx, - param_env, - field_ty.ty, - tcx.require_lang_item(hir::LangItem::Sized, Some(field_ty.source_info.span)), - ObligationCause::new( - field_ty.source_info.span, - def_id, - ObligationCauseCode::SizedCoroutineInterior(def_id), - ), - ); - } + let ocx = ObligationCtxt::new(&infcx); + for (predicate, cause) in &typeck_results.coroutine_stalled_predicates { + ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, *predicate)); } - let errors = fulfillment_cx.select_all_or_error(&infcx); + let errors = ocx.select_all_or_error(); debug!(?errors); if !errors.is_empty() { return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index ec9e928ce593d..d2759087cb471 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -9,7 +9,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, FulfillmentError}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -639,10 +639,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( } } - if !unnormalized_trait_sig.output().references_error() { - debug_assert!( - !collector.types.is_empty(), - "expect >0 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`" + if !unnormalized_trait_sig.output().references_error() && collector.types.is_empty() { + tcx.dcx().delayed_bug( + "expect >0 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`", ); } @@ -746,7 +745,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // We may not collect all RPITITs that we see in the HIR for a trait signature // because an RPITIT was located within a missing item. Like if we have a sig - // returning `-> Missing`, that gets converted to `-> [type error]`, + // returning `-> Missing`, that gets converted to `-> {type error}`, // and when walking through the signature we end up never collecting the def id // of the `impl Sized`. Insert that here, so we don't ICE later. for assoc_item in tcx.associated_types_for_impl_traits_in_associated_fn(trait_m.def_id) { @@ -801,10 +800,10 @@ impl<'tcx> TypeFolder> for ImplTraitInTraitCollector<'_, 'tcx> { bug!("FIXME(RPITIT): error here"); } // Replace with infer var - let infer_ty = self.ocx.infcx.next_ty_var(TypeVariableOrigin { - span: self.span, - kind: TypeVariableOriginKind::MiscVariable, - }); + let infer_ty = self + .ocx + .infcx + .next_ty_var(TypeVariableOrigin { span: self.span, param_def_id: None }); self.types.insert(proj.def_id, (infer_ty, proj.args)); // Recurse into bounds for (pred, pred_span) in self @@ -1305,7 +1304,7 @@ fn compare_number_of_generics<'tcx>( .iter() .filter(|p| match p.kind { hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided, + kind: hir::LifetimeParamKind::Elided(_), } => { // A fn can have an arbitrary number of extra elided lifetimes for the // same signature. @@ -1724,6 +1723,7 @@ pub(super) fn compare_impl_const_raw( compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?; compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?; + check_region_bounds_on_impl_item(tcx, impl_const_item, trait_const_item, false)?; compare_const_predicate_entailment(tcx, impl_const_item, trait_const_item, impl_trait_ref) } @@ -1764,8 +1764,6 @@ fn compare_const_predicate_entailment<'tcx>( let impl_ct_predicates = tcx.predicates_of(impl_ct.def_id); let trait_ct_predicates = tcx.predicates_of(trait_ct.def_id); - check_region_bounds_on_impl_item(tcx, impl_ct, trait_ct, false)?; - // The predicates declared by the impl definition, the trait and the // associated const in the trait are assumed. let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap()); @@ -1867,6 +1865,7 @@ pub(super) fn compare_impl_ty<'tcx>( let _: Result<(), ErrorGuaranteed> = try { compare_number_of_generics(tcx, impl_ty, trait_ty, false)?; compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?; + check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?; compare_type_predicate_entailment(tcx, impl_ty, trait_ty, impl_trait_ref)?; check_type_bounds(tcx, trait_ty, impl_ty, impl_trait_ref)?; }; @@ -1887,8 +1886,6 @@ fn compare_type_predicate_entailment<'tcx>( let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id); let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id); - check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?; - let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_args); if impl_ty_own_bounds.len() == 0 { // Nothing to check. diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs index f0c15a070b48a..a49626eed35af 100644 --- a/compiler/rustc_hir_analysis/src/check/errs.rs +++ b/compiler/rustc_hir_analysis/src/check/errs.rs @@ -12,16 +12,9 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) { let hir_id = expr.hir_id; if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind && matches!(borrow_kind, hir::BorrowKind::Ref) - && let Some(var) = is_path_static_mut(*expr) + && let Some(var) = path_if_static_mut(tcx, expr) { - handle_static_mut_ref( - tcx, - span, - var, - span.edition().at_least_rust_2024(), - matches!(m, Mutability::Mut), - hir_id, - ); + handle_static_mut_ref(tcx, span, var, span.edition().at_least_rust_2024(), m, hir_id); } } @@ -29,28 +22,28 @@ pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) { pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) { if let hir::StmtKind::Let(loc) = stmt.kind && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind - && matches!(ba.0, rustc_ast::ByRef::Yes) + && let hir::ByRef::Yes(rmutbl) = ba.0 && let Some(init) = loc.init - && let Some(var) = is_path_static_mut(*init) + && let Some(var) = path_if_static_mut(tcx, init) { handle_static_mut_ref( tcx, init.span, var, loc.span.edition().at_least_rust_2024(), - matches!(ba.1, Mutability::Mut), + rmutbl, stmt.hir_id, ); } } -fn is_path_static_mut(expr: hir::Expr<'_>) -> Option { +fn path_if_static_mut(tcx: TyCtxt<'_>, expr: &hir::Expr<'_>) -> Option { if let hir::ExprKind::Path(qpath) = expr.kind && let hir::QPath::Resolved(_, path) = qpath && let hir::def::Res::Def(def_kind, _) = path.res && let hir::def::DefKind::Static { mutability: Mutability::Mut, nested: false } = def_kind { - return Some(qpath_to_string(&qpath)); + return Some(qpath_to_string(&tcx, &qpath)); } None } @@ -60,28 +53,27 @@ fn handle_static_mut_ref( span: Span, var: String, e2024: bool, - mutable: bool, + mutable: Mutability, hir_id: hir::HirId, ) { if e2024 { - let (sugg, shared) = if mutable { + let (sugg, shared) = if mutable == Mutability::Mut { (errors::StaticMutRefSugg::Mut { span, var }, "mutable") } else { (errors::StaticMutRefSugg::Shared { span, var }, "shared") }; tcx.sess.psess.dcx.emit_err(errors::StaticMutRef { span, sugg, shared }); - return; - } - - let (sugg, shared) = if mutable { - (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable") } else { - (errors::RefOfMutStaticSugg::Shared { span, var }, "shared") - }; - tcx.emit_node_span_lint( - STATIC_MUT_REFS, - hir_id, - span, - errors::RefOfMutStatic { span, sugg, shared }, - ); + let (sugg, shared) = if mutable == Mutability::Mut { + (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable") + } else { + (errors::RefOfMutStaticSugg::Shared { span, var }, "shared") + }; + tcx.emit_node_span_lint( + STATIC_MUT_REFS, + hir_id, + span, + errors::RefOfMutStatic { span, sugg, shared }, + ); + } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index bf5838143d945..eb1fa1baecca0 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -107,6 +107,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::cttz | sym::bswap | sym::bitreverse + | sym::three_way_compare | sym::discriminant_value | sym::type_id | sym::likely @@ -127,8 +128,8 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::variant_count | sym::is_val_statically_known | sym::ptr_mask - | sym::check_language_ub - | sym::check_library_ub + | sym::aggregate_raw_ptr + | sym::ub_checks | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -183,7 +184,7 @@ pub fn check_intrinsic_type( let region = ty::Region::new_bound( tcx, ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }, ); let env_region = ty::Region::new_bound( tcx, @@ -191,7 +192,7 @@ pub fn check_intrinsic_type( ty::BoundRegion { var: ty::BoundVar::from_u32(2), kind: ty::BrEnv }, ); let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]); - (Ty::new_ref(tcx, env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty) + (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) }) }; @@ -207,11 +208,11 @@ pub fn check_intrinsic_type( Ty::new_tup(tcx, &[param(0), tcx.types.bool]), ), "load" => (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)), - "store" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx)), + "store" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit), "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax" | "umin" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)), - "fence" | "singlethreadfence" => (0, Vec::new(), Ty::new_unit(tcx)), + "fence" | "singlethreadfence" => (0, Vec::new(), tcx.types.unit), op => { tcx.dcx().emit_err(UnrecognizedAtomicOperation { span, op }); return; @@ -223,7 +224,7 @@ pub fn check_intrinsic_type( let (n_tps, n_cts, inputs, output) = match intrinsic_name { sym::abort => (0, 0, vec![], tcx.types.never), sym::unreachable => (0, 0, vec![], tcx.types.never), - sym::breakpoint => (0, 0, vec![], Ty::new_unit(tcx)), + sym::breakpoint => (0, 0, vec![], tcx.types.unit), sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => { (1, 0, vec![], tcx.types.usize) } @@ -234,21 +235,15 @@ pub fn check_intrinsic_type( sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), sym::assert_inhabited | sym::assert_zero_valid - | sym::assert_mem_uninitialized_valid => (1, 0, vec![], Ty::new_unit(tcx)), - sym::forget => (1, 0, vec![param(0)], Ty::new_unit(tcx)), + | sym::assert_mem_uninitialized_valid => (1, 0, vec![], tcx.types.unit), + sym::forget => (1, 0, vec![param(0)], tcx.types.unit), sym::transmute | sym::transmute_unchecked => (2, 0, vec![param(0)], param(1)), sym::prefetch_read_data | sym::prefetch_write_data | sym::prefetch_read_instruction - | sym::prefetch_write_instruction => ( - 1, - 0, - vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.i32, - ], - Ty::new_unit(tcx), - ), + | sym::prefetch_write_instruction => { + (1, 0, vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.i32], tcx.types.unit) + } sym::needs_drop => (1, 0, vec![], tcx.types.bool), sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), @@ -257,41 +252,35 @@ pub fn check_intrinsic_type( sym::arith_offset => ( 1, 0, - vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.isize, - ], - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.isize], + Ty::new_imm_ptr(tcx, param(0)), ), sym::ptr_mask => ( 1, 0, - vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.usize, - ], - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.usize], + Ty::new_imm_ptr(tcx, param(0)), ), sym::copy | sym::copy_nonoverlapping => ( 1, 0, vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), + Ty::new_imm_ptr(tcx, param(0)), + Ty::new_mut_ptr(tcx, param(0)), tcx.types.usize, ], - Ty::new_unit(tcx), + tcx.types.unit, ), sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => ( 1, 0, vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + Ty::new_mut_ptr(tcx, param(0)), + Ty::new_imm_ptr(tcx, param(0)), tcx.types.usize, ], - Ty::new_unit(tcx), + tcx.types.unit, ), sym::compare_bytes => { let byte_ptr = Ty::new_imm_ptr(tcx, tcx.types.u8); @@ -300,12 +289,8 @@ pub fn check_intrinsic_type( sym::write_bytes | sym::volatile_set_memory => ( 1, 0, - vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - tcx.types.u8, - tcx.types.usize, - ], - Ty::new_unit(tcx), + vec![Ty::new_mut_ptr(tcx, param(0)), tcx.types.u8, tcx.types.usize], + tcx.types.unit, ), sym::sqrtf16 => (0, 0, vec![tcx.types.f16], tcx.types.f16), @@ -424,16 +409,18 @@ pub fn check_intrinsic_type( (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)) } sym::volatile_store | sym::unaligned_volatile_store => { - (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx)) + (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit) + } + + sym::ctpop | sym::ctlz | sym::ctlz_nonzero | sym::cttz | sym::cttz_nonzero => { + (1, 0, vec![param(0)], tcx.types.u32) } - sym::ctpop - | sym::ctlz - | sym::ctlz_nonzero - | sym::cttz - | sym::cttz_nonzero - | sym::bswap - | sym::bitreverse => (1, 0, vec![param(0)], param(0)), + sym::bswap | sym::bitreverse => (1, 0, vec![param(0)], param(0)), + + sym::three_way_compare => { + (1, 0, vec![param(0), param(0)], tcx.ty_ordering_enum(Some(span))) + } sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { (1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool])) @@ -441,7 +428,7 @@ pub fn check_intrinsic_type( sym::ptr_guaranteed_cmp => ( 1, - 0, + 1, vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))], tcx.types.u8, ), @@ -453,7 +440,7 @@ pub fn check_intrinsic_type( 0, 1, vec![Ty::new_mut_ptr(tcx, tcx.types.u8), tcx.types.usize, tcx.types.usize], - Ty::new_unit(tcx), + tcx.types.unit, ), sym::ptr_offset_from => ( @@ -471,9 +458,8 @@ pub fn check_intrinsic_type( sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { (1, 0, vec![param(0), param(0)], param(0)) } - sym::unchecked_shl | sym::unchecked_shr | sym::rotate_left | sym::rotate_right => { - (1, 0, vec![param(0), param(0)], param(0)) - } + sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)), + sym::rotate_left | sym::rotate_right => (1, 0, vec![param(0), tcx.types.u32], param(0)), sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => { (1, 0, vec![param(0), param(0)], param(0)) } @@ -491,22 +477,24 @@ pub fn check_intrinsic_type( | sym::frem_algebraic => (1, 0, vec![param(0), param(0)], param(0)), sym::float_to_int_unchecked => (2, 0, vec![param(0)], param(1)), - sym::assume => (0, 1, vec![tcx.types.bool], Ty::new_unit(tcx)), + sym::assume => (0, 1, vec![tcx.types.bool], tcx.types.unit), sym::likely => (0, 1, vec![tcx.types.bool], tcx.types.bool), sym::unlikely => (0, 1, vec![tcx.types.bool], tcx.types.bool), sym::read_via_copy => (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)), sym::write_via_move => { - (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx)) + (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit) } + sym::typed_swap => (1, 1, vec![Ty::new_mut_ptr(tcx, param(0)); 2], tcx.types.unit), + sym::discriminant_value => { let assoc_items = tcx.associated_item_def_ids( tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), ); let discriminant_def_id = assoc_items[0]; - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }; + let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }; ( 1, 0, @@ -523,14 +511,14 @@ pub fn check_intrinsic_type( let mut_u8 = Ty::new_mut_ptr(tcx, tcx.types.u8); let try_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig( [mut_u8], - Ty::new_unit(tcx), + tcx.types.unit, false, hir::Unsafety::Normal, Abi::Rust, )); let catch_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig( [mut_u8, mut_u8], - Ty::new_unit(tcx), + tcx.types.unit, false, hir::Unsafety::Normal, Abi::Rust, @@ -544,29 +532,29 @@ pub fn check_intrinsic_type( } sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (0, 0, vec![va_list_ref_ty], Ty::new_unit(tcx)), - None => bug!("`va_list` language item needed for C-variadic intrinsics"), + Some((va_list_ref_ty, _)) => (0, 0, vec![va_list_ref_ty], tcx.types.unit), + None => bug!("`va_list` lang item needed for C-variadic intrinsics"), }, sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) { Some((va_list_ref_ty, va_list_ty)) => { let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty); - (0, 0, vec![va_list_ptr_ty, va_list_ref_ty], Ty::new_unit(tcx)) + (0, 0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.types.unit) } - None => bug!("`va_list` language item needed for C-variadic intrinsics"), + None => bug!("`va_list` lang item needed for C-variadic intrinsics"), }, sym::va_arg => match mk_va_list_ty(hir::Mutability::Mut) { Some((va_list_ref_ty, _)) => (1, 0, vec![va_list_ref_ty], param(0)), - None => bug!("`va_list` language item needed for C-variadic intrinsics"), + None => bug!("`va_list` lang item needed for C-variadic intrinsics"), }, sym::nontemporal_store => { - (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx)) + (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit) } sym::raw_eq => { - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }; + let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }; let param_ty_lhs = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0)); let br = ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrAnon }; @@ -579,13 +567,17 @@ pub fn check_intrinsic_type( sym::is_val_statically_known => (1, 1, vec![param(0)], tcx.types.bool), - sym::const_eval_select => (4, 0, vec![param(0), param(1), param(2)], param(3)), + sym::const_eval_select => (4, 1, vec![param(0), param(1), param(2)], param(3)), sym::vtable_size | sym::vtable_align => { - (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize) + (0, 0, vec![Ty::new_imm_ptr(tcx, tcx.types.unit)], tcx.types.usize) } - sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool), + // This type check is not particularly useful, but the `where` bounds + // on the definition in `core` do the heavy lifting for checking it. + sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)), + + sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool), sym::simd_eq | sym::simd_ne @@ -631,15 +623,15 @@ pub fn check_intrinsic_type( sym::simd_fma => (1, 0, vec![param(0), param(0), param(0)], param(0)), sym::simd_gather => (3, 0, vec![param(0), param(1), param(2)], param(0)), sym::simd_masked_load => (3, 0, vec![param(0), param(1), param(2)], param(2)), - sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], Ty::new_unit(tcx)), - sym::simd_scatter => (3, 0, vec![param(0), param(1), param(2)], Ty::new_unit(tcx)), + sym::simd_masked_store => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit), + sym::simd_scatter => (3, 0, vec![param(0), param(1), param(2)], tcx.types.unit), sym::simd_insert => (2, 0, vec![param(0), tcx.types.u32, param(1)], param(0)), sym::simd_extract => (2, 0, vec![param(0), tcx.types.u32], param(1)), sym::simd_cast | sym::simd_as | sym::simd_cast_ptr - | sym::simd_expose_addr - | sym::simd_from_exposed_addr => (2, 0, vec![param(0)], param(1)), + | sym::simd_expose_provenance + | sym::simd_with_exposed_provenance => (2, 0, vec![param(0)], param(1)), sym::simd_bitmask => (2, 0, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, 0, vec![param(0), param(1), param(1)], param(1)) @@ -658,10 +650,6 @@ pub fn check_intrinsic_type( sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)), sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)), - sym::retag_box_to_raw => { - (2, 0, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_mut_ptr(tcx, param(0))) - } - other => { tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other }); return; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 9de660407d7aa..45ccd0fa2e068 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -6,7 +6,9 @@ use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::Symbol; use rustc_target::abi::FieldIdx; -use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; +use rustc_target::asm::{ + InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, +}; pub struct InlineAsmCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -62,12 +64,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), ty::FnPtr(_) => Some(asm_ty_isize), - ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => { - Some(asm_ty_isize) - } + ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), ty::Adt(adt, args) if adt.repr().simd() => { let fields = &adt.non_enum_variant().fields; - let elem_ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args); + let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args); let (size, ty) = match elem_ty.kind() { ty::Array(ty, len) => { @@ -143,10 +143,10 @@ 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::from_u32(0)].ty(self.tcx, args); + let ty = fields[FieldIdx::ZERO].ty(self.tcx, args); self.get_asm_ty(ty) } _ => self.get_asm_ty(ty), @@ -253,8 +253,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } // Check whether a modifier is suggested for using this type. - if let Some((suggested_modifier, suggested_result)) = - reg_class.suggest_modifier(asm_arch, asm_ty) + if let Some(ModifierInfo { + modifier: suggested_modifier, + result: suggested_result, + size: suggested_size, + }) = reg_class.suggest_modifier(asm_arch, asm_ty) { // Search for any use of this operand without a modifier and emit // the suggestion for them. @@ -268,8 +271,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } } if !spans.is_empty() { - let (default_modifier, default_result) = - reg_class.default_modifier(asm_arch).unwrap(); + let ModifierInfo { + modifier: default_modifier, + result: default_result, + size: default_size, + } = reg_class.default_modifier(asm_arch).unwrap(); self.tcx.node_span_lint( lint::builtin::ASM_SUB_REGISTER, expr.hir_id, @@ -278,10 +284,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { |lint| { lint.span_label(expr.span, "for this argument"); lint.help(format!( - "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`", + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", )); lint.help(format!( - "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`", + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", )); }, ); diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 22afddad6336d..eb0ffc19d4540 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -91,17 +91,18 @@ 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::traits::error_reporting::suggestions::ReturnsVisitor; +use rustc_trait_selection::traits::error_reporting::suggestions::{ + ReturnsVisitor, TypeErrCtxtExt as _, +}; 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; @@ -291,12 +292,16 @@ fn default_body_is_unstable( reason: reason_str, }); + let inject_span = item_did + .as_local() + .and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id))); rustc_session::parse::add_feature_diagnostics_for_issue( &mut err, &tcx.sess, feature, rustc_feature::GateIssue::Library(issue), false, + inject_span, ); err.emit(); @@ -427,7 +432,7 @@ fn fn_sig_suggestion<'tcx>( let asyncness = if tcx.asyncness(assoc.def_id).is_async() { output = if let ty::Alias(_, alias_ty) = *output.kind() { - tcx.explicit_item_bounds(alias_ty.def_id) + tcx.explicit_item_super_predicates(alias_ty.def_id) .iter_instantiated_copied(tcx, alias_ty.args) .find_map(|(bound, _)| bound.as_projection_clause()?.no_bound_vars()?.term.ty()) .unwrap_or_else(|| { @@ -462,17 +467,6 @@ fn fn_sig_suggestion<'tcx>( ) } -pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { - 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", - }) -} - /// Return placeholder code for the given associated item. /// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a /// structured suggestion. @@ -507,7 +501,12 @@ 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 = tcx + .infer_ctxt() + .build() + .err_ctxt() + .ty_kind_suggestion(tcx.param_env(assoc.def_id), ty) + .unwrap_or_else(|| "value".to_string()); format!("const {}: {} = {};", assoc.name, ty, val) } } diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 3c26729eff8af..d2ea51f65f98a 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Arm, Block, Expr, Local, Pat, PatKind, Stmt}; +use rustc_hir::{Arm, Block, Expr, LetStmt, Pat, PatKind, Stmt}; use rustc_index::Idx; use rustc_middle::middle::region::*; use rustc_middle::ty::TyCtxt; @@ -123,7 +123,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h for (i, statement) in blk.stmts.iter().enumerate() { match statement.kind { - hir::StmtKind::Let(hir::Local { els: Some(els), .. }) => { + hir::StmtKind::Let(LetStmt { els: Some(els), .. }) => { // Let-else has a special lexical structure for variables. // First we take a checkpoint of the current scope context here. let mut prev_cx = visitor.cx; @@ -654,7 +654,7 @@ fn resolve_local<'tcx>( // & expression, and its lifetime would be extended to the end of the block (due // to a different rule, not the below code). match pat.kind { - PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true, + PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(_), _), ..) => true, PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)), @@ -668,10 +668,10 @@ fn resolve_local<'tcx>( | PatKind::TupleStruct(_, subpats, _) | PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)), - PatKind::Box(subpat) => is_binding_pat(subpat), + PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat), PatKind::Ref(_, _) - | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..) + | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) | PatKind::Wild | PatKind::Never | PatKind::Path(_) @@ -689,6 +689,8 @@ fn resolve_local<'tcx>( /// | [ ..., E&, ... ] /// | ( ..., E&, ... ) /// | {...; E&} + /// | if _ { ...; E& } else { ...; E& } + /// | match _ { ..., _ => E&, ... } /// | box E& /// | E& as ... /// | ( E& ) @@ -727,6 +729,17 @@ fn resolve_local<'tcx>( record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); } } + hir::ExprKind::If(_, then_block, else_block) => { + record_rvalue_scope_if_borrow_expr(visitor, then_block, blk_id); + if let Some(else_block) = else_block { + record_rvalue_scope_if_borrow_expr(visitor, else_block, blk_id); + } + } + hir::ExprKind::Match(_, arms, _) => { + for arm in arms { + record_rvalue_scope_if_borrow_expr(visitor, arm.body, blk_id); + } + } hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) => { // FIXME(@dingxiangfei2009): choose call arguments here // for candidacy for extended parameter rule application @@ -760,7 +773,7 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { fn enter_node_scope_with_dtor(&mut self, id: hir::ItemLocalId) { // If node was previously marked as a terminating scope during the - // recursive visit of its parent node in the AST, then we need to + // recursive visit of its parent node in the HIR, then we need to // account for the destruction scope representing the scope of // the destructors that run immediately after it completes. if self.terminating_scopes.contains(&id) { @@ -855,7 +868,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { resolve_expr(self, ex); } - fn visit_local(&mut self, l: &'tcx Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) { resolve_local(self, Some(l.pat), l.init) } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f525004534408..5b127e0bf496a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2,17 +2,20 @@ use crate::autoderef::Autoderef; use crate::collect::CollectItemTypesVisitor; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; use crate::errors; +use crate::fluent_generated as fluent; use hir::intravisit::Visitor; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; +use rustc_macros::LintDiagnostic; use rustc_middle::query::Providers; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -136,6 +139,8 @@ where infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); + let errors = infcx.resolve_regions(&outlives_env); if errors.is_empty() { return Ok(()); @@ -196,7 +201,7 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorG hir::OwnerNode::TraitItem(item) => check_trait_item(tcx, item), hir::OwnerNode::ImplItem(item) => check_impl_item(tcx, item), hir::OwnerNode::ForeignItem(item) => check_foreign_item(tcx, item), - hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), + hir::OwnerNode::Synthetic => unreachable!(), }; if let Some(generics) = node.generics() { @@ -272,7 +277,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() } Some(ty::ImplPolarity::Negative) => { let ast::ImplPolarity::Negative(span) = impl_.polarity else { - bug!("impl_polarity query disagrees with impl's polarity in AST"); + bug!("impl_polarity query disagrees with impl's polarity in HIR"); }; // FIXME(#27579): what amount of WF checking do we need for neg impls? if let hir::Defaultness::Default { .. } = impl_.defaultness { @@ -954,7 +959,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), hir_ty.span, "using function pointers as const generic parameters is forbidden", ), - ty::RawPtr(_) => tcx.dcx().struct_span_err( + ty::RawPtr(_, _) => tcx.dcx().struct_span_err( hir_ty.span, "using raw pointers as const generic parameters is forbidden", ), @@ -999,9 +1004,14 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), // Implments `ConstParamTy`, suggest adding the feature to enable. Ok(..) => true, }; - if may_suggest_feature && tcx.sess.is_nightly_build() { - diag.help( - "add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types", + if may_suggest_feature { + tcx.disabled_nightly_features( + &mut diag, + Some(param.hir_id), + [( + " more complex and user defined types".to_string(), + sym::adt_const_params, + )], ); } @@ -1317,7 +1327,7 @@ fn check_impl<'tcx>( trait_ref, ); let trait_pred = - ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Positive }; + ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive }; let mut obligations = traits::wf::trait_obligations( wfcx.infcx, wfcx.param_env, @@ -1627,10 +1637,6 @@ fn check_fn_or_method<'tcx>( } } -const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box`, \ - `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one \ - of the previous types except `Self`)"; - #[instrument(level = "debug", skip(wfcx))] fn check_method_receiver<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, @@ -1666,7 +1672,7 @@ fn check_method_receiver<'tcx>( if tcx.features().arbitrary_self_types { if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { // Report error; `arbitrary_self_types` was enabled. - return Err(e0307(tcx, span, receiver_ty)); + return Err(tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty })); } } else { if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, false) { @@ -1681,24 +1687,17 @@ fn check_method_receiver<'tcx>( the `arbitrary_self_types` feature", ), ) - .with_help(HELP_FOR_SELF_TYPE) + .with_help(fluent::hir_analysis_invalid_receiver_ty_help) .emit() } else { // Report error; would not have worked with `arbitrary_self_types`. - e0307(tcx, span, receiver_ty) + tcx.dcx().emit_err(errors::InvalidReceiverTy { span, receiver_ty }) }); } } Ok(()) } -fn e0307(tcx: TyCtxt<'_>, span: Span, receiver_ty: Ty<'_>) -> ErrorGuaranteed { - struct_span_code_err!(tcx.dcx(), span, E0307, "invalid `self` parameter type: {receiver_ty}") - .with_note("type of `self` must be `Self` or a type that dereferences to it") - .with_help(HELP_FOR_SELF_TYPE) - .emit() -} - /// Returns whether `receiver_ty` would be considered a valid receiver type for `self_ty`. If /// `arbitrary_self_types` is enabled, `receiver_ty` must transitively deref to `self_ty`, possibly /// through a `*const/mut T` raw pointer. If the feature is not enabled, the requirements are more @@ -1861,7 +1860,7 @@ fn check_variances_for_type_defn<'tcx>( .iter() .filter_map(|predicate| match predicate { hir::WherePredicate::BoundPredicate(predicate) => { - match icx.to_ty(predicate.bounded_ty).kind() { + match icx.lower_ty(predicate.bounded_ty).kind() { ty::Param(data) => Some(Parameter(data.index)), _ => None, } @@ -2005,6 +2004,137 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error res } +fn lint_redundant_lifetimes<'tcx>( + tcx: TyCtxt<'tcx>, + owner_id: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, +) { + let def_kind = tcx.def_kind(owner_id); + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::Fn + | DefKind::Const + | DefKind::Impl { of_trait: _ } => { + // Proceed + } + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { + let parent_def_id = tcx.local_parent(owner_id); + if matches!(tcx.def_kind(parent_def_id), DefKind::Impl { of_trait: true }) { + // Don't check for redundant lifetimes for associated items of trait + // implementations, since the signature is required to be compatible + // with the trait, even if the implementation implies some lifetimes + // are redundant. + return; + } + } + DefKind::Mod + | DefKind::Variant + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Closure => return, + } + + // The ordering of this lifetime map is a bit subtle. + // + // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime, + // where we can prove that `'candidate = 'victim`. + // + // `'static` must come first in this list because we can never replace `'static` with + // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to + // suggest replacing `'a` with `'static`. + let mut lifetimes = vec![tcx.lifetimes.re_static]; + lifetimes.extend( + ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()), + ); + // If we are in a function, add its late-bound lifetimes too. + if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { + for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() { + let ty::BoundVariableKind::Region(kind) = var else { continue }; + lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind)); + } + } + lifetimes.retain(|candidate| candidate.has_name()); + + // Keep track of lifetimes which have already been replaced with other lifetimes. + // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by + // both `'a` and `'b`. + let mut shadowed = FxHashSet::default(); + + for (idx, &candidate) in lifetimes.iter().enumerate() { + // Don't suggest removing a lifetime twice. We only need to check this + // here and not up in the `victim` loop because equality is transitive, + // so if A = C and B = C, then A must = B, so it'll be shadowed too in + // A's victim loop. + if shadowed.contains(&candidate) { + continue; + } + + for &victim in &lifetimes[(idx + 1)..] { + // We should only have late-bound lifetimes of the `BrNamed` variety, + // since we get these signatures straight from `hir_lowering`. And any + // other regions (ReError/ReStatic/etc.) shouldn't matter, since we + // can't really suggest to remove them. + let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + })) = victim.kind() + else { + continue; + }; + + // Do not rename lifetimes not local to this item since they'll overlap + // with the lint running on the parent. We still want to consider parent + // lifetimes which make child lifetimes redundant, otherwise we would + // have truncated the `identity_for_item` args above. + if tcx.parent(def_id) != owner_id.to_def_id() { + continue; + } + + // If `candidate <: victim` and `victim <: candidate`, then they're equal. + if outlives_env.free_region_map().sub_free_regions(tcx, candidate, victim) + && outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate) + { + shadowed.insert(victim); + tcx.emit_node_span_lint( + rustc_lint_defs::builtin::REDUNDANT_LIFETIMES, + tcx.local_def_id_to_hir_id(def_id.expect_local()), + tcx.def_span(def_id), + RedundantLifetimeArgsLint { candidate, victim }, + ); + } + } + } +} + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_redundant_lifetime_args)] +#[note] +struct RedundantLifetimeArgsLint<'tcx> { + /// The lifetime we have found to be redundant. + victim: ty::Region<'tcx>, + // The lifetime we can replace the victim with. + candidate: ty::Region<'tcx>, +} + pub fn provide(providers: &mut Providers) { *providers = Providers { check_mod_type_wf, check_well_formed, ..*providers }; } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8d8b13d6cb397..0b6c60e4ba296 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -10,8 +10,8 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::{self, RegionResolutionError}; -use rustc_infer::infer::{DefineOpaqueTypes, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt}; @@ -189,13 +189,10 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() // even if they do not carry that attribute. use rustc_type_ir::TyKind::*; match (source.kind(), target.kind()) { - (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) - if infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, r_a, *r_b).is_ok() - && mutbl_a == *mutbl_b => - { + (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) if r_a == *r_b && mutbl_a == *mutbl_b => { Ok(()) } - (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => Ok(()), + (&RawPtr(_, a_mutbl), &RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()), (&Adt(def_a, args_a), &Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => { if def_a != def_b { let source_path = tcx.def_path_str(def_a.did()); @@ -230,18 +227,14 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } } - if let Ok(ok) = - infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, ty_a, ty_b) - { - if ok.obligations.is_empty() { - res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST { - span, - name: field.name, - ty: ty_a, - })); + if ty_a == ty_b { + res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST { + span, + name: field.name, + ty: ty_a, + })); - return false; - } + return false; } return true; @@ -351,14 +344,17 @@ pub fn coerce_unsized_info<'tcx>( check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty)) } - (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => { - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) - } + (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => check_mutbl( + ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }, + ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }, + &|ty| Ty::new_imm_ptr(tcx, ty), + ), - (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => { - check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) - } + (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => check_mutbl( + ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }, + ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }, + &|ty| Ty::new_imm_ptr(tcx, ty), + ), (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => @@ -430,14 +426,12 @@ pub fn coerce_unsized_info<'tcx>( // something more accepting, but we use // equality because we want to be able to // perform this check without computing - // variance where possible. (This is because - // we may have to evaluate constraint + // variance or constraining opaque types' hidden types. + // (This is because we may have to evaluate constraint // expressions in the course of execution.) // See e.g., #41936. - if let Ok(ok) = infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, a, b) { - if ok.obligations.is_empty() { - return None; - } + if a == b { + return None; } // Collect up all fields that were significantly changed @@ -551,7 +545,7 @@ fn infringing_fields_error( } if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, .. })) = error_predicate.kind().skip_binder() { diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 32f312012548a..4a85e9983f471 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -144,7 +144,12 @@ impl<'tcx> InherentCollect<'tcx> { let id = id.owner_id.def_id; let item_span = self.tcx.def_span(id); let self_ty = self.tcx.type_of(id).instantiate_identity(); - let self_ty = self.tcx.peel_off_weak_alias_tys(self_ty); + let mut self_ty = self.tcx.peel_off_weak_alias_tys(self_ty); + // We allow impls on pattern types exactly when we allow impls on the base type. + // FIXME(pattern_types): Figure out the exact coherence rules we want here. + while let ty::Pat(base, _) = *self_ty.kind() { + self_ty = base; + } match *self_ty.kind() { ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()), ty::Foreign(did) => self.check_def_id(id, self_ty, did), @@ -154,6 +159,7 @@ impl<'tcx> InherentCollect<'tcx> { ty::Dynamic(..) => { Err(self.tcx.dcx().emit_err(errors::InherentDyn { span: item_span })) } + ty::Pat(_, _) => unreachable!(), ty::Bool | ty::Char | ty::Int(_) @@ -162,7 +168,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Never | ty::FnPtr(_) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index b46a67d08eb03..819e0925f68df 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -2,14 +2,20 @@ //! crate or pertains to a type defined in this crate. use crate::errors; + +use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::def_id::LocalDefId; -use rustc_span::Span; -use rustc_trait_selection::traits::{self, IsFirstInputType}; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_lint_defs::builtin::UNCOVERED_PARAM_IN_PROJECTION; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_trait_selection::traits::{self, IsFirstInputType, UncoveredTyParams}; +use rustc_trait_selection::traits::{OrphanCheckErr, OrphanCheckMode}; +use rustc_trait_selection::traits::{StructurallyNormalizeExt, TraitEngineExt}; -#[instrument(skip(tcx), level = "debug")] +#[instrument(level = "debug", skip(tcx))] pub(crate) fn orphan_check_impl( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, @@ -17,31 +23,23 @@ pub(crate) fn orphan_check_impl( let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); trait_ref.error_reported()?; - let trait_def_id = trait_ref.def_id; - - match traits::orphan_check(tcx, impl_def_id.to_def_id()) { + match orphan_check(tcx, impl_def_id, OrphanCheckMode::Proper) { Ok(()) => {} - Err(err) => { - let item = tcx.hir().expect_item(impl_def_id); - let hir::ItemKind::Impl(impl_) = item.kind else { - bug!("{:?} is not an impl: {:?}", impl_def_id, item); - }; - let tr = impl_.of_trait.as_ref().unwrap(); - let sp = tcx.def_span(impl_def_id); - - emit_orphan_check_error( - tcx, - sp, - item.span, - tr.path.span, - trait_ref, - impl_.self_ty.span, - impl_.generics, - err, - )? - } + Err(err) => match orphan_check(tcx, impl_def_id, OrphanCheckMode::Compat) { + Ok(()) => match err { + OrphanCheckErr::UncoveredTyParams(uncovered_ty_params) => { + lint_uncovered_ty_params(tcx, uncovered_ty_params, impl_def_id) + } + OrphanCheckErr::NonLocalInputType(_) => { + bug!("orphanck: shouldn't've gotten non-local input tys in compat mode") + } + }, + Err(err) => return Err(emit_orphan_check_error(tcx, trait_ref, impl_def_id, err)), + }, } + let trait_def_id = trait_ref.def_id; + // In addition to the above rules, we restrict impls of auto traits // so that they can only be implemented on nominal types, such as structs, // enums or foreign types. To see why this restriction exists, consider the @@ -186,13 +184,13 @@ pub(crate) fn orphan_check_impl( // type This = T; // } // impl AutoTrait for ::This {} - AliasKind::Projection => "associated type", + ty::Projection => "associated type", // type Foo = (impl Sized, bool) // impl AutoTrait for Foo {} - AliasKind::Weak => "type alias", + ty::Weak => "type alias", // type Opaque = impl Trait; // impl AutoTrait for Opaque {} - AliasKind::Opaque => "opaque type", + ty::Opaque => "opaque type", // ``` // struct S(T); // impl S { @@ -201,11 +199,16 @@ pub(crate) fn orphan_check_impl( // impl AutoTrait for S::This {} // ``` // FIXME(inherent_associated_types): The example code above currently leads to a cycle - AliasKind::Inherent => "associated type", + ty::Inherent => "associated type", }; (LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther) } + ty::Pat(..) => ( + LocalImpl::Disallow { problematic_kind: "pattern type" }, + NonlocalImpl::DisallowOther, + ), + ty::Bool | ty::Char | ty::Int(..) @@ -270,157 +273,316 @@ pub(crate) fn orphan_check_impl( Ok(()) } +/// Checks the coherence orphan rules. +/// +/// `impl_def_id` should be the `DefId` of a trait impl. +/// +/// To pass, either the trait must be local, or else two conditions must be satisfied: +/// +/// 1. All type parameters in `Self` must be "covered" by some local type constructor. +/// 2. Some local type must appear in `Self`. +#[instrument(level = "debug", skip(tcx), ret)] +fn orphan_check<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: LocalDefId, + mode: OrphanCheckMode, +) -> Result<(), OrphanCheckErr<'tcx, FxIndexSet>> { + // We only accept this routine to be invoked on implementations + // of a trait, not inherent implementations. + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + debug!(trait_ref = ?trait_ref.skip_binder()); + + // If the *trait* is local to the crate, ok. + if let Some(def_id) = trait_ref.skip_binder().def_id.as_local() { + debug!("trait {def_id:?} is local to current crate"); + return Ok(()); + } + + // (1) Instantiate all generic params with fresh inference vars. + let infcx = tcx.infer_ctxt().intercrate(true).build(); + let cause = traits::ObligationCause::dummy(); + let args = infcx.fresh_args_for_item(cause.span, impl_def_id.to_def_id()); + let trait_ref = trait_ref.instantiate(tcx, args); + + let lazily_normalize_ty = |user_ty: Ty<'tcx>| { + let ty::Alias(..) = user_ty.kind() else { return Ok(user_ty) }; + + let ocx = traits::ObligationCtxt::new(&infcx); + let ty = ocx.normalize(&cause, ty::ParamEnv::empty(), user_ty); + let ty = infcx.resolve_vars_if_possible(ty); + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + return Ok(user_ty); + } + + let ty = if infcx.next_trait_solver() { + let mut fulfill_cx = >::new(&infcx); + infcx + .at(&cause, ty::ParamEnv::empty()) + .structurally_normalize(ty, &mut *fulfill_cx) + .map(|ty| infcx.resolve_vars_if_possible(ty)) + .unwrap_or(ty) + } else { + ty + }; + + Ok(ty) + }; + + let Ok(result) = traits::orphan_check_trait_ref::( + &infcx, + trait_ref, + traits::InCrate::Local { mode }, + lazily_normalize_ty, + ) else { + unreachable!() + }; + + // (2) Try to map the remaining inference vars back to generic params. + result.map_err(|err| match err { + OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => { + let mut collector = + UncoveredTyParamCollector { infcx: &infcx, uncovered_params: Default::default() }; + uncovered.visit_with(&mut collector); + // FIXME(fmease): This is very likely reachable. + debug_assert!(!collector.uncovered_params.is_empty()); + + OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { + uncovered: collector.uncovered_params, + local_ty, + }) + } + OrphanCheckErr::NonLocalInputType(tys) => { + let generics = tcx.generics_of(impl_def_id); + let tys = tys + .into_iter() + .map(|(ty, is_target_ty)| { + (ty.fold_with(&mut TyVarReplacer { infcx: &infcx, generics }), is_target_ty) + }) + .collect(); + OrphanCheckErr::NonLocalInputType(tys) + } + }) +} + fn emit_orphan_check_error<'tcx>( tcx: TyCtxt<'tcx>, - sp: Span, - full_impl_span: Span, - trait_span: Span, trait_ref: ty::TraitRef<'tcx>, - self_ty_span: Span, - generics: &hir::Generics<'tcx>, - err: traits::OrphanCheckErr<'tcx>, -) -> Result { - let self_ty = trait_ref.self_ty(); - Err(match err { + impl_def_id: LocalDefId, + err: traits::OrphanCheckErr<'tcx, FxIndexSet>, +) -> ErrorGuaranteed { + match err { traits::OrphanCheckErr::NonLocalInputType(tys) => { - let (mut opaque, mut foreign, mut name, mut pointer, mut ty_diag) = - (Vec::new(), Vec::new(), Vec::new(), Vec::new(), Vec::new()); - let mut sugg = None; + let item = tcx.hir().expect_item(impl_def_id); + let impl_ = item.expect_impl(); + let hir_trait_ref = impl_.of_trait.as_ref().unwrap(); + + let span = tcx.def_span(impl_def_id); + let mut diag = tcx.dcx().create_err(match trait_ref.self_ty().kind() { + ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span, note: () }, + _ if trait_ref.self_ty().is_primitive() => { + errors::OnlyCurrentTraits::Primitive { span, note: () } + } + _ => errors::OnlyCurrentTraits::Arbitrary { span, note: () }, + }); + for &(mut ty, is_target_ty) in &tys { let span = if matches!(is_target_ty, IsFirstInputType::Yes) { // Point at `D` in `impl for C in D` - self_ty_span + impl_.self_ty.span } else { // Point at `C` in `impl for C in D` - trait_span + hir_trait_ref.path.span }; ty = tcx.erase_regions(ty); - ty = match ty.kind() { - // Remove the type arguments from the output, as they are not relevant. - // You can think of this as the reverse of `resolve_vars_if_possible`. - // That way if we had `Vec`, we will properly attribute the - // problem to `Vec` and avoid confusing the user if they were to see - // `MyType` in the error. - ty::Adt(def, _) => Ty::new_adt(tcx, *def, ty::List::empty()), - _ => ty, - }; - - fn push_to_foreign_or_name<'tcx>( - is_foreign: bool, - foreign: &mut Vec, - name: &mut Vec>, - span: Span, - sname: &'tcx str, - ) { - if is_foreign { - foreign.push(errors::OnlyCurrentTraitsForeign { span }) - } else { - name.push(errors::OnlyCurrentTraitsName { span, name: sname }); - } - } let is_foreign = !trait_ref.def_id.is_local() && matches!(is_target_ty, IsFirstInputType::No); - match &ty.kind() { + match *ty.kind() { ty::Slice(_) => { - push_to_foreign_or_name( - is_foreign, - &mut foreign, - &mut name, - span, - "slices", - ); + if is_foreign { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsForeign { span }, + ); + } else { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsName { span, name: "slices" }, + ); + } } ty::Array(..) => { - push_to_foreign_or_name( - is_foreign, - &mut foreign, - &mut name, - span, - "arrays", - ); + if is_foreign { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsForeign { span }, + ); + } else { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsName { span, name: "arrays" }, + ); + } } ty::Tuple(..) => { - push_to_foreign_or_name( - is_foreign, - &mut foreign, - &mut name, - span, - "tuples", - ); + if is_foreign { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsForeign { span }, + ); + } else { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsName { span, name: "tuples" }, + ); + } } ty::Alias(ty::Opaque, ..) => { - opaque.push(errors::OnlyCurrentTraitsOpaque { span }) + diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span }); } - ty::RawPtr(ptr_ty) => { - if !self_ty.has_param() { - let mut_key = ptr_ty.mutbl.prefix_str(); - sugg = Some(errors::OnlyCurrentTraitsPointerSugg { - wrapper_span: self_ty_span, - struct_span: full_impl_span.shrink_to_lo(), - mut_key, - ptr_ty: ptr_ty.ty, - }); + ty::RawPtr(ptr_ty, mutbl) => { + if !trait_ref.self_ty().has_param() { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsPointerSugg { + wrapper_span: impl_.self_ty.span, + struct_span: item.span.shrink_to_lo(), + mut_key: mutbl.prefix_str(), + ptr_ty, + }, + ); } - pointer.push(errors::OnlyCurrentTraitsPointer { span, pointer: ty }); + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsPointer { span, pointer: ty }, + ); + } + ty::Adt(adt_def, _) => { + diag.subdiagnostic( + tcx.dcx(), + errors::OnlyCurrentTraitsAdt { + span, + name: tcx.def_path_str(adt_def.did()), + }, + ); + } + _ => { + diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsTy { span, ty }); } - _ => ty_diag.push(errors::OnlyCurrentTraitsTy { span, ty }), } } - let err_struct = match self_ty.kind() { - ty::Adt(..) => errors::OnlyCurrentTraits::Outside { - span: sp, - note: (), - opaque, - foreign, - name, - pointer, - ty: ty_diag, - sugg, - }, - _ if self_ty.is_primitive() => errors::OnlyCurrentTraits::Primitive { - span: sp, - note: (), - opaque, - foreign, - name, - pointer, - ty: ty_diag, - sugg, - }, - _ => errors::OnlyCurrentTraits::Arbitrary { - span: sp, - note: (), - opaque, - foreign, - name, - pointer, - ty: ty_diag, - sugg, - }, - }; - tcx.dcx().emit_err(err_struct) + diag.emit() } - traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => { - let mut sp = sp; - for param in generics.params { - if param.name.ident().to_string() == param_ty.to_string() { - sp = param.span; - } - } + traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => { + let mut reported = None; + for param_def_id in uncovered { + let span = tcx.def_ident_span(param_def_id).unwrap(); + let name = tcx.item_name(param_def_id); - match local_type { - Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal { - span: sp, - note: (), - param_ty, - local_type, - }), - None => tcx.dcx().emit_err(errors::TyParamSome { span: sp, note: (), param_ty }), + reported.get_or_insert(match local_ty { + Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal { + span, + note: (), + param: name, + local_type, + }), + None => tcx.dcx().emit_err(errors::TyParamSome { span, note: (), param: name }), + }); } + reported.unwrap() // FIXME(fmease): This is very likely reachable. } - }) + } +} + +fn lint_uncovered_ty_params<'tcx>( + tcx: TyCtxt<'tcx>, + UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<'tcx, FxIndexSet>, + impl_def_id: LocalDefId, +) { + let hir_id = tcx.local_def_id_to_hir_id(impl_def_id); + + for param_def_id in uncovered { + let span = tcx.def_ident_span(param_def_id).unwrap(); + let name = tcx.item_name(param_def_id); + + match local_ty { + Some(local_type) => tcx.emit_node_span_lint( + UNCOVERED_PARAM_IN_PROJECTION, + hir_id, + span, + errors::TyParamFirstLocalLint { span, note: (), param: name, local_type }, + ), + None => tcx.emit_node_span_lint( + UNCOVERED_PARAM_IN_PROJECTION, + hir_id, + span, + errors::TyParamSomeLint { span, note: (), param: name }, + ), + }; + } +} + +struct UncoveredTyParamCollector<'cx, 'tcx> { + infcx: &'cx InferCtxt<'tcx>, + uncovered_params: FxIndexSet, +} + +impl<'tcx> TypeVisitor> for UncoveredTyParamCollector<'_, 'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) { + return; + } + let Some(origin) = self.infcx.type_var_origin(ty) else { + return ty.super_visit_with(self); + }; + if let Some(def_id) = origin.param_def_id { + self.uncovered_params.insert(def_id); + } + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { + if ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) { + ct.super_visit_with(self) + } + } +} + +struct TyVarReplacer<'cx, 'tcx> { + infcx: &'cx InferCtxt<'tcx>, + generics: &'tcx ty::Generics, +} + +impl<'cx, 'tcx> TypeFolder> for TyVarReplacer<'cx, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) { + return ty; + } + let Some(origin) = self.infcx.type_var_origin(ty) else { + return ty.super_fold_with(self); + }; + if let Some(def_id) = origin.param_def_id { + // The generics of an `impl` don't have a parent, we can index directly. + let index = self.generics.param_def_id_to_index[&def_id]; + let name = self.generics.params[index as usize].name; + + Ty::new_param(self.infcx.tcx, index, name) + } else { + ty + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if !ct.has_type_flags(ty::TypeFlags::HAS_TY_INFER) { + return ct; + } + ct.super_fold_with(self) + } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 5cd6862786b46..0f0736f87568b 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; @@ -40,9 +40,9 @@ use std::cell::Cell; use std::iter; use std::ops::Bound; -use crate::astconv::AstConv; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; +use crate::hir_ty_lowering::HirTyLowerer; pub use type_of::test_opaque_hidden_types; mod generics_of; @@ -61,6 +61,9 @@ pub fn provide(providers: &mut Providers) { type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, + item_super_predicates: item_bounds::item_super_predicates, + explicit_item_super_predicates: item_bounds::explicit_item_super_predicates, + item_non_self_assumptions: item_bounds::item_non_self_assumptions, generics_of: generics_of::generics_of, predicates_of: predicates_of::predicates_of, predicates_defined_on, @@ -85,13 +88,12 @@ pub fn provide(providers: &mut Providers) { /////////////////////////////////////////////////////////////////////////// -/// Context specific to some particular item. This is what implements -/// [`AstConv`]. +/// Context specific to some particular item. This is what implements [`HirTyLowerer`]. /// /// # `ItemCtxt` vs `FnCtxt` /// /// `ItemCtxt` is primarily used to type-check item signatures and lower them -/// from HIR to their [`ty::Ty`] representation, which is exposed using [`AstConv`]. +/// from HIR to their [`ty::Ty`] representation, which is exposed using [`HirTyLowerer`]. /// It's also used for the bodies of items like structs where the body (the fields) /// are just signatures. /// @@ -108,11 +110,11 @@ pub fn provide(providers: &mut Providers) { /// `ItemCtxt` has information about the predicates that are defined /// on the trait. Unfortunately, this predicate information is /// available in various different forms at various points in the -/// process. So we can't just store a pointer to e.g., the AST or the +/// process. So we can't just store a pointer to e.g., the HIR or the /// parsed ty form, we have to be more flexible. To this end, the /// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy -/// `get_type_parameter_bounds` requests, drawing the information from -/// the AST (`hir::Generics`), recursively. +/// `probe_ty_param_bounds` requests, drawing the information from +/// the HIR (`hir::Generics`), recursively. pub struct ItemCtxt<'tcx> { tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, @@ -274,7 +276,7 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - convert_item(self.tcx, item.item_id()); + lower_item(self.tcx, item.item_id()); reject_placeholder_type_signatures_in_item(self.tcx, item); intravisit::walk_item(self, item); } @@ -312,12 +314,12 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { } fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - convert_trait_item(self.tcx, trait_item.trait_item_id()); + lower_trait_item(self.tcx, trait_item.trait_item_id()); intravisit::walk_trait_item(self, trait_item); } fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - convert_impl_item(self.tcx, impl_item.impl_item_id()); + lower_impl_item(self.tcx, impl_item.impl_item_id()); intravisit::walk_impl_item(self, impl_item); } } @@ -341,8 +343,8 @@ impl<'tcx> ItemCtxt<'tcx> { ItemCtxt { tcx, item_def_id, tainted_by_errors: Cell::new(None) } } - pub fn to_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - self.astconv().ast_ty_to_ty(ast_ty) + pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + self.lowerer().lower_ty(hir_ty) } pub fn hir_id(&self) -> hir::HirId { @@ -361,7 +363,7 @@ impl<'tcx> ItemCtxt<'tcx> { } } -impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { +impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -370,29 +372,22 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { self.item_def_id.to_def_id() } - fn get_type_parameter_bounds( - &self, - span: Span, - def_id: LocalDefId, - assoc_name: Ident, - ) -> ty::GenericPredicates<'tcx> { - self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name)) + fn allow_infer(&self) -> bool { + false } fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option> { None } - fn allow_ty_infer(&self) -> bool { - false - } - fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { Ty::new_error_with_message(self.tcx(), span, "bad placeholder type") } fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> { let ty = self.tcx.fold_regions(ty, |r, _| match *r { + rustc_type_ir::RegionKind::ReStatic => r, + // This is never reached in practice. If it ever is reached, // `ReErased` should be changed to `ReStatic`, and any other region // left alone. @@ -401,7 +396,16 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { ty::Const::new_error_with_message(self.tcx(), ty, span, "bad placeholder constant") } - fn projected_ty_from_poly_trait_ref( + fn probe_ty_param_bounds( + &self, + span: Span, + def_id: LocalDefId, + assoc_name: Ident, + ) -> ty::GenericPredicates<'tcx> { + self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name)) + } + + fn lower_assoc_ty( &self, span: Span, item_def_id: DefId, @@ -409,7 +413,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { - let item_args = self.astconv().create_args_for_associated_item( + let item_args = self.lowerer().lower_generic_args_of_assoc_item( span, item_def_id, item_segment, @@ -494,10 +498,6 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { ty.ty_adt_def() } - fn set_tainted_by_errors(&self, err: ErrorGuaranteed) { - self.tainted_by_errors.set(Some(err)); - } - fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) { // There's no place to record types from signatures? } @@ -505,6 +505,10 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { fn infcx(&self) -> Option<&InferCtxt<'tcx>> { None } + + fn set_tainted_by_errors(&self, err: ErrorGuaranteed) { + self.tainted_by_errors.set(Some(err)); + } } /// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. @@ -544,9 +548,10 @@ fn get_new_lifetime_name<'tcx>( (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap() } -fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { +#[instrument(level = "debug", skip_all)] +fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { let it = tcx.hir().item(item_id); - debug!("convert: item {} with id {}", it.ident, it.hir_id()); + debug!(item = %it.ident, id = %it.hir_id()); let def_id = item_id.owner_id.def_id; match &it.kind { @@ -588,7 +593,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); - convert_enum_variant_types(tcx, def_id.to_def_id()); + lower_enum_variant_types(tcx, def_id.to_def_id()); } hir::ItemKind::Impl { .. } => { tcx.ensure().generics_of(def_id); @@ -622,7 +627,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } if let Some(ctor_def_id) = struct_def.ctor_def_id() { - convert_variant_ctor(tcx, ctor_def_id); + lower_variant_ctor(tcx, ctor_def_id); } } @@ -633,7 +638,9 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(def_id); tcx.ensure().predicates_of(def_id); tcx.ensure().explicit_item_bounds(def_id); + tcx.ensure().explicit_item_super_predicates(def_id); tcx.ensure().item_bounds(def_id); + tcx.ensure().item_super_predicates(def_id); } hir::ItemKind::TyAlias(..) => { @@ -663,7 +670,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } } -fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { +fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { let trait_item = tcx.hir().trait_item(trait_item_id); let def_id = trait_item_id.owner_id; tcx.ensure().generics_of(def_id); @@ -689,6 +696,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, Some(_)) => { tcx.ensure().item_bounds(def_id); + tcx.ensure().item_super_predicates(def_id); tcx.ensure().type_of(def_id); // Account for `type T = _;`. let mut visitor = HirPlaceholderCollector::default(); @@ -698,6 +706,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, None) => { tcx.ensure().item_bounds(def_id); + tcx.ensure().item_super_predicates(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = HirPlaceholderCollector::default(); @@ -710,7 +719,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { tcx.ensure().predicates_of(def_id); } -fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { +fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { let def_id = impl_item_id.owner_id; tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); @@ -739,13 +748,13 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { } } -fn convert_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { +fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); } -fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { +fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { let def = tcx.adt_def(def_id); let repr_type = def.repr().discr_type(); let initial = repr_type.initial_discriminant(tcx); @@ -778,10 +787,9 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { tcx.ensure().predicates_of(f.did); } - // Convert the ctor, if any. This also registers the variant as - // an item. + // Lower the ctor, if any. This also registers the variant as an item. if let Some(ctor_def_id) = variant.ctor_def_id() { - convert_variant_ctor(tcx, ctor_def_id.expect_local()); + lower_variant_ctor(tcx, ctor_def_id.expect_local()); } } } @@ -968,7 +976,7 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } } -fn convert_variant( +fn lower_variant( tcx: TyCtxt<'_>, variant_did: Option, ident: Ident, @@ -986,7 +994,7 @@ fn convert_variant( .inspect(|f| { has_unnamed_fields |= f.ident.name == kw::Underscore; // We only check named ADT here because anonymous ADTs are checked inside - // the nammed ADT in which they are defined. + // the named ADT in which they are defined. if !is_anonymous { field_uniqueness_check_ctx.check_field(f); } @@ -1053,7 +1061,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { }; distance_from_explicit += 1; - convert_variant( + lower_variant( tcx, Some(v.def_id), v.ident, @@ -1073,7 +1081,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { ItemKind::Struct(..) => AdtKind::Struct, _ => AdtKind::Union, }; - let variants = std::iter::once(convert_variant( + let variants = std::iter::once(lower_variant( tcx, None, item.ident, @@ -1277,7 +1285,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder, def_id: LocalDefId) -> ty::EarlyBinder { - icx.astconv().ty_of_fn(hir_id, header.unsafety, header.abi, decl, Some(generics), None) - } + }) => icx.lowerer().lower_fn_ty( + hir_id, + header.unsafety, + header.abi, + decl, + Some(generics), + None, + ), ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { let abi = tcx.hir().get_foreign_abi(hir_id); @@ -1362,17 +1375,19 @@ fn infer_return_ty_for_fn_sig<'tcx>( // Don't leak types into signatures unless they're nameable! // For example, if a function returns itself, we don't want that // recursive function definition to leak out into the fn sig. - let mut should_recover = false; + let mut recovered_ret_ty = None; - if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false) { + if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) { diag.span_suggestion( ty.span, "replace with the correct return type", - ret_ty, + suggestable_ret_ty, Applicability::MachineApplicable, ); - should_recover = true; - } else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, def_id) { + recovered_ret_ty = Some(suggestable_ret_ty); + } 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", @@ -1391,20 +1406,15 @@ fn infer_return_ty_for_fn_sig<'tcx>( } let guar = diag.emit(); - - if should_recover { - ty::Binder::dummy(fn_sig) - } else { - ty::Binder::dummy(tcx.mk_fn_sig( - fn_sig.inputs().iter().copied(), - Ty::new_error(tcx, guar), - fn_sig.c_variadic, - fn_sig.unsafety, - fn_sig.abi, - )) - } + ty::Binder::dummy(tcx.mk_fn_sig( + fn_sig.inputs().iter().copied(), + recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)), + fn_sig.c_variadic, + fn_sig.unsafety, + fn_sig.abi, + )) } - None => icx.astconv().ty_of_fn( + None => icx.lowerer().lower_fn_ty( hir_id, sig.header.unsafety, sig.header.abi, @@ -1415,11 +1425,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 { let format_as_assoc: fn(_, _, _, _, _) -> _ = |tcx: TyCtxt<'tcx>, @@ -1442,7 +1451,7 @@ fn suggest_impl_trait<'tcx>( let ty::Tuple(types) = *args_tuple.kind() else { return None; }; - let types = types.make_suggestable(tcx, false)?; + let types = types.make_suggestable(tcx, false, None)?; let maybe_ret = if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") }; Some(format!( @@ -1453,24 +1462,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, ), ( - tcx.lang_items().fn_once_trait(), - tcx.lang_items().fn_once_output(), + infcx.tcx.lang_items().fn_mut_trait(), + infcx.tcx.lang_items().fn_once_output(), + format_as_parenthesized, + ), + ( + infcx.tcx.lang_items().fn_once_trait(), + infcx.tcx.lang_items().fn_once_output(), format_as_parenthesized, ), ] { @@ -1480,36 +1493,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) - && 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 @@ -1522,19 +1544,19 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option, def_id: LocalDefId) -> Option, def_id: LocalDefId) -> Option, is_const: bool, - ast_trait_ref: &hir::TraitRef<'_>, + hir_trait_ref: &hir::TraitRef<'_>, ) -> Option { if !is_const { return None; } - let trait_def_id = ast_trait_ref.trait_def_id()?; + let trait_def_id = hir_trait_ref.trait_def_id()?; if tcx.has_attr(trait_def_id, sym::const_trait) { return None; } let trait_name = tcx.item_name(trait_def_id).to_string(); Some(tcx.dcx().emit_err(errors::ConstImplForNonConstTrait { - trait_ref_span: ast_trait_ref.path.span, + trait_ref_span: hir_trait_ref.path.span, trait_name, local_trait_span: trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()), @@ -1671,19 +1693,19 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( }; let hir_id = tcx.local_def_id_to_hir_id(def_id); let fty = - ItemCtxt::new(tcx, def_id).astconv().ty_of_fn(hir_id, unsafety, abi, decl, None, None); + ItemCtxt::new(tcx, def_id).lowerer().lower_fn_ty(hir_id, unsafety, abi, decl, None, None); // Feature gate SIMD types in FFI, since I am not sure that the // ABIs are handled at all correctly. -huonw if abi != abi::Abi::RustIntrinsic && !tcx.features().simd_ffi { - let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| { + let check = |hir_ty: &hir::Ty<'_>, ty: Ty<'_>| { if ty.is_simd() { let snip = tcx .sess .source_map() - .span_to_snippet(ast_ty.span) + .span_to_snippet(hir_ty.span) .map_or_else(|_| String::new(), |s| format!(" `{s}`")); - tcx.dcx().emit_err(errors::SIMDFFIHighlyExperimental { span: ast_ty.span, snip }); + tcx.dcx().emit_err(errors::SIMDFFIHighlyExperimental { span: hir_ty.span, snip }); } }; for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) { diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index c86788db988eb..f83ddc51c76e2 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -195,16 +195,19 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } Some(fn_def_id.to_def_id()) } - ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { .. }, + ItemKind::OpaqueTy(&hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty }, .. }) => { - let parent_id = tcx.hir().get_parent_item(hir_id); - assert_ne!(parent_id, hir::CRATE_OWNER_ID); - debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); + if in_assoc_ty { + assert!(matches!(tcx.def_kind(parent), DefKind::AssocTy)); + } else { + assert!(matches!(tcx.def_kind(parent), DefKind::TyAlias)); + } + debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent); // Opaque types are always nested within another item, and // inherit the generics of the item. - Some(parent_id.to_def_id()) + Some(parent.to_def_id()) } _ => None, }, @@ -218,8 +221,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Deny, } - let no_generics = hir::Generics::empty(); - let ast_generics = node.generics().unwrap_or(no_generics); + let hir_generics = node.generics().unwrap_or(hir::Generics::empty()); let (opt_self, allow_defaults) = match node { Node::Item(item) => { match item.kind { @@ -275,13 +277,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { generics.parent_count + generics.params.len() }); - let mut params: Vec<_> = Vec::with_capacity(ast_generics.params.len() + has_self as usize); + let mut params: Vec<_> = Vec::with_capacity(hir_generics.params.len() + has_self as usize); if let Some(opt_self) = opt_self { params.push(opt_self); } - let early_lifetimes = super::early_bound_lifetimes_from_generics(tcx, ast_generics); + let early_lifetimes = super::early_bound_lifetimes_from_generics(tcx, hir_generics); params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef { name: param.name.ident().name, index: own_start + i as u32, @@ -302,7 +304,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \ `struct`, `enum`, `type`, or `trait` definitions"; - params.extend(ast_generics.params.iter().filter_map(|param| match param.kind { + params.extend(hir_generics.params.iter().filter_map(|param| match param.kind { GenericParamKind::Lifetime { .. } => None, GenericParamKind::Type { default, synthetic, .. } => { if default.is_some() { @@ -508,29 +510,9 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option match &item.kind { - hir::TraitItemKind::Fn(sig, _) => has_late_bound_regions(tcx, item.generics, sig.decl), - _ => None, - }, - Node::ImplItem(item) => match &item.kind { - hir::ImplItemKind::Fn(sig, _) => has_late_bound_regions(tcx, item.generics, sig.decl), - _ => None, - }, - Node::ForeignItem(item) => match item.kind { - hir::ForeignItemKind::Fn(fn_decl, _, generics) => { - has_late_bound_regions(tcx, generics, fn_decl) - } - _ => None, - }, - Node::Item(item) => match &item.kind { - hir::ItemKind::Fn(sig, .., generics, _) => { - has_late_bound_regions(tcx, generics, sig.decl) - } - _ => None, - }, - _ => None, - } + let decl = node.fn_decl()?; + let generics = node.generics()?; + has_late_bound_regions(tcx, generics, decl) } struct AnonConstInParamTyDetector { diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 78c390d09244d..02291cc603e13 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -1,9 +1,10 @@ use super::ItemCtxt; -use crate::astconv::{AstConv, PredicateFilter}; +use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter}; +use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_infer::traits::util; use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; @@ -17,8 +18,9 @@ use rustc_span::Span; fn associated_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, assoc_item_def_id: LocalDefId, - ast_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_bounds: &'tcx [hir::GenericBound<'tcx>], span: Span, + filter: PredicateFilter, ) -> &'tcx [(ty::Clause<'tcx>, Span)] { let item_ty = Ty::new_projection( tcx, @@ -27,9 +29,9 @@ fn associated_type_bounds<'tcx>( ); let icx = ItemCtxt::new(tcx, assoc_item_def_id); - let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds, PredicateFilter::All); + let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter); // Associated types are implicitly sized unless a `?Sized` bound is found - icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span); + icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); let trait_def_id = tcx.local_parent(assoc_item_def_id); let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); @@ -60,15 +62,16 @@ fn associated_type_bounds<'tcx>( fn opaque_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId, - ast_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_bounds: &'tcx [hir::GenericBound<'tcx>], item_ty: Ty<'tcx>, span: Span, + filter: PredicateFilter, ) -> &'tcx [(ty::Clause<'tcx>, Span)] { ty::print::with_reduced_queries!({ let icx = ItemCtxt::new(tcx, opaque_def_id); - let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds, PredicateFilter::All); + let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter); // Opaque types are implicitly sized unless a `?Sized` bound is found - icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span); + icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); debug!(?bounds); tcx.arena.alloc_from_iter(bounds.clauses()) @@ -78,6 +81,21 @@ fn opaque_type_bounds<'tcx>( pub(super) fn explicit_item_bounds( tcx: TyCtxt<'_>, def_id: LocalDefId, +) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { + explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::All) +} + +pub(super) fn explicit_item_super_predicates( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { + explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::SelfOnly) +} + +pub(super) fn explicit_item_bounds_with_filter( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + filter: PredicateFilter, ) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { match tcx.opt_rpitit_info(def_id.to_def_id()) { // RPITIT's bounds are the same as opaque type bounds, but with @@ -95,6 +113,7 @@ pub(super) fn explicit_item_bounds( ty::GenericArgs::identity_for_item(tcx, def_id), ), item.span, + filter, )); } Some(ty::ImplTraitInTraitData::Impl { .. }) => span_bug!( @@ -109,7 +128,7 @@ pub(super) fn explicit_item_bounds( kind: hir::TraitItemKind::Type(bounds, _), span, .. - }) => associated_type_bounds(tcx, def_id, bounds, *span), + }) => associated_type_bounds(tcx, def_id, bounds, *span, filter), hir::Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: false, .. }), span, @@ -117,10 +136,10 @@ pub(super) fn explicit_item_bounds( }) => { let args = GenericArgs::identity_for_item(tcx, def_id); let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); - opaque_type_bounds(tcx, def_id, bounds, item_ty, *span) + opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) } - // Since RPITITs are astconv'd as projections in `ast_ty_to_ty`, when we're asking - // for the item bounds of the *opaques* in a trait's default method signature, we + // Since RPITITs are lowered as projections in `::lower_ty`, when we're + // asking for the item bounds of the *opaques* in a trait's default method signature, we // need to map these projections back to opaques. hir::Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: true, origin, .. }), @@ -135,7 +154,7 @@ pub(super) fn explicit_item_bounds( let args = GenericArgs::identity_for_item(tcx, def_id); let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); tcx.arena.alloc_slice( - &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span) + &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) .to_vec() .fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: fn_def_id.to_def_id() }), ) @@ -146,15 +165,37 @@ pub(super) fn explicit_item_bounds( ty::EarlyBinder::bind(bounds) } -pub(super) fn item_bounds( - tcx: TyCtxt<'_>, - def_id: DefId, -) -> ty::EarlyBinder<&'_ ty::List>> { +pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder> { tcx.explicit_item_bounds(def_id).map_bound(|bounds| { tcx.mk_clauses_from_iter(util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound))) }) } +pub(super) fn item_super_predicates( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> ty::EarlyBinder> { + tcx.explicit_item_super_predicates(def_id).map_bound(|bounds| { + tcx.mk_clauses_from_iter( + util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound)).filter_only_self(), + ) + }) +} + +pub(super) fn item_non_self_assumptions( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> ty::EarlyBinder> { + let all_bounds: FxIndexSet<_> = tcx.item_bounds(def_id).skip_binder().iter().collect(); + let own_bounds: FxIndexSet<_> = + tcx.item_super_predicates(def_id).skip_binder().iter().collect(); + if all_bounds.len() == own_bounds.len() { + ty::EarlyBinder::bind(ty::ListWithCachedTypeInfo::empty()) + } else { + ty::EarlyBinder::bind(tcx.mk_clauses_from_iter(all_bounds.difference(&own_bounds).copied())) + } +} + struct AssocTyToOpaque<'tcx> { tcx: TyCtxt<'tcx>, fn_def_id: DefId, @@ -173,7 +214,7 @@ impl<'tcx> TypeFolder> for AssocTyToOpaque<'tcx> { { self.tcx.type_of(projection_ty.def_id).instantiate(self.tcx, projection_ty.args) } else { - ty + ty.super_fold_with(self) } } } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 6aae4aa21b878..472657290ed1f 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1,7 +1,7 @@ -use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter}; use crate::bounds::Bounds; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; +use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; use hir::{HirId, Node}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; @@ -37,7 +37,7 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic // from the trait itself that *shouldn't* be shown as the source of // an obligation and instead be skipped. Otherwise we'd use // `tcx.def_span(def_id);` - let span = rustc_span::DUMMY_SP; + let span = DUMMY_SP; result.predicates = tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( @@ -123,43 +123,22 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // Preserving the order of insertion is important here so as not to break UI tests. let mut predicates: FxIndexSet<(ty::Clause<'_>, Span)> = FxIndexSet::default(); - let ast_generics = match node { - Node::TraitItem(item) => item.generics, - - Node::ImplItem(item) => item.generics, - - Node::Item(item) => match item.kind { + let hir_generics = node.generics().unwrap_or(NO_GENERICS); + if let Node::Item(item) = node { + match item.kind { ItemKind::Impl(impl_) => { if impl_.defaultness.is_default() { is_default_impl_trait = tcx .impl_trait_ref(def_id) .map(|t| ty::Binder::dummy(t.instantiate_identity())); } - impl_.generics } - ItemKind::Fn(.., generics, _) - | ItemKind::TyAlias(_, generics) - | ItemKind::Const(_, generics, _) - | ItemKind::Enum(_, generics) - | ItemKind::Struct(_, generics) - | ItemKind::Union(_, generics) => generics, - - ItemKind::Trait(_, _, generics, self_bounds, ..) - | ItemKind::TraitAlias(generics, self_bounds) => { + + ItemKind::Trait(_, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, self_bounds) => { is_trait = Some(self_bounds); - generics } - ItemKind::OpaqueTy(OpaqueTy { generics, .. }) => generics, - _ => NO_GENERICS, - }, - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Static(..) => NO_GENERICS, - ForeignItemKind::Fn(_, _, generics) => generics, - ForeignItemKind::Type => NO_GENERICS, - }, - - _ => NO_GENERICS, + _ => {} + } }; let generics = tcx.generics_of(def_id); @@ -170,8 +149,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // like `trait Foo: A + B + C`. if let Some(self_bounds) = is_trait { predicates.extend( - icx.astconv() - .compute_bounds(tcx.types.self_param, self_bounds, PredicateFilter::All) + icx.lowerer() + .lower_mono_bounds(tcx.types.self_param, self_bounds, PredicateFilter::All) .clauses(), ); } @@ -191,19 +170,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // Collect the predicates that were written inline by the user on each // type parameter (e.g., ``). Also add `ConstArgHasType` predicates // for each const parameter. - for param in ast_generics.params { + for param in hir_generics.params { match param.kind { // We already dealt with early bound lifetimes above. GenericParamKind::Lifetime { .. } => (), GenericParamKind::Type { .. } => { - let param_ty = icx.astconv().hir_id_to_bound_ty(param.hir_id); + let param_ty = icx.lowerer().lower_ty_param(param.hir_id); let mut bounds = Bounds::default(); // Params are implicitly sized unless a `?Sized` bound is found - icx.astconv().add_implicitly_sized( + icx.lowerer().add_sized_bound( &mut bounds, param_ty, &[], - Some((param.def_id, ast_generics.predicates)), + Some((param.def_id, hir_generics.predicates)), param.span, ); trace!(?bounds); @@ -215,7 +194,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen .type_of(param.def_id.to_def_id()) .no_bound_vars() .expect("const parameters cannot be generic"); - let ct = icx.astconv().hir_id_to_bound_const(param.hir_id, ct_ty); + let ct = icx.lowerer().lower_const_param(param.hir_id, ct_ty); predicates.insert(( ty::ClauseKind::ConstArgHasType(ct, ct_ty).to_predicate(tcx), param.span, @@ -226,10 +205,10 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen trace!(?predicates); // Add in the bounds that appear in the where-clause. - for predicate in ast_generics.predicates { + for predicate in hir_generics.predicates { match predicate { hir::WherePredicate::BoundPredicate(bound_pred) => { - let ty = icx.to_ty(bound_pred.bounded_ty); + let ty = icx.lower_ty(bound_pred.bounded_ty); let bound_vars = tcx.late_bound_vars(bound_pred.hir_id); // Keep the type around in a dummy predicate, in case of no bounds. // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` @@ -253,7 +232,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } let mut bounds = Bounds::default(); - icx.astconv().add_bounds( + icx.lowerer().lower_poly_bounds( ty, bound_pred.bounds.iter(), &mut bounds, @@ -264,11 +243,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } hir::WherePredicate::RegionPredicate(region_pred) => { - let r1 = icx.astconv().ast_region_to_region(region_pred.lifetime, None); + let r1 = icx.lowerer().lower_lifetime(region_pred.lifetime, None); predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { hir::GenericBound::Outlives(lt) => { - (icx.astconv().ast_region_to_region(lt, None), lt.ident.span) + (icx.lowerer().lower_lifetime(lt, None), lt.ident.span) } bound => { span_bug!( @@ -563,8 +542,8 @@ pub(super) fn explicit_predicates_of<'tcx>( } /// Ensures that the super-predicates of the trait with a `DefId` -/// of `trait_def_id` are converted and stored. This also ensures that -/// the transitive super-predicates are converted. +/// of `trait_def_id` are lowered and stored. This also ensures that +/// the transitive super-predicates are lowered. pub(super) fn super_predicates_of( tcx: TyCtxt<'_>, trait_def_id: LocalDefId, @@ -595,8 +574,8 @@ pub(super) fn implied_predicates_of( } /// Ensures that the super-predicates of the trait with a `DefId` -/// of `trait_def_id` are converted and stored. This also ensures that -/// the transitive super-predicates are converted. +/// of `trait_def_id` are lowered and stored. This also ensures that +/// the transitive super-predicates are lowered. pub(super) fn implied_predicates_with_filter( tcx: TyCtxt<'_>, trait_def_id: DefId, @@ -622,9 +601,9 @@ pub(super) fn implied_predicates_with_filter( let icx = ItemCtxt::new(tcx, trait_def_id); let self_param_ty = tcx.types.self_param; - let superbounds = icx.astconv().compute_bounds(self_param_ty, bounds, filter); + let superbounds = icx.lowerer().lower_mono_bounds(self_param_ty, bounds, filter); - let where_bounds_that_match = icx.type_parameter_bounds_in_generics( + let where_bounds_that_match = icx.probe_ty_param_bounds_in_generics( generics, item.owner_id.def_id, self_param_ty, @@ -636,7 +615,7 @@ pub(super) fn implied_predicates_with_filter( &*tcx.arena.alloc_from_iter(superbounds.clauses().chain(where_bounds_that_match)); debug!(?implied_bounds); - // Now require that immediate supertraits are converted, which will, in + // Now require that immediate supertraits are lowered, which will, in // turn, reach indirect supertraits, so we detect cycles now instead of // overflowing during elaboration. Same for implied predicates, which // make sure we walk into associated type bounds. @@ -645,7 +624,7 @@ pub(super) fn implied_predicates_with_filter( for &(pred, span) in implied_bounds { debug!("superbound: {:?}", pred); if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder() - && bound.polarity == ty::ImplPolarity::Positive + && bound.polarity == ty::PredicatePolarity::Positive { tcx.at(span).super_predicates_of(bound.def_id()); } @@ -655,7 +634,7 @@ pub(super) fn implied_predicates_with_filter( for &(pred, span) in implied_bounds { debug!("superbound: {:?}", pred); if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder() - && bound.polarity == ty::ImplPolarity::Positive + && bound.polarity == ty::PredicatePolarity::Positive { tcx.at(span).implied_predicates_of(bound.def_id()); } @@ -677,7 +656,7 @@ pub(super) fn type_param_predicates( use rustc_hir::*; use rustc_middle::ty::Ty; - // In the AST, bounds can derive from two places. Either + // In the HIR, bounds can derive from two places. Either // written inline like `` or in a where-clause like // `where T: Foo`. @@ -697,56 +676,28 @@ pub(super) fn type_param_predicates( let mut result = parent .map(|parent| { let icx = ItemCtxt::new(tcx, parent); - icx.get_type_parameter_bounds(DUMMY_SP, def_id, assoc_name) + icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_name) }) .unwrap_or_default(); let mut extend = None; let item_hir_id = tcx.local_def_id_to_hir_id(item_def_id); - let ast_generics = match tcx.hir_node(item_hir_id) { - Node::TraitItem(item) => item.generics, - - Node::ImplItem(item) => item.generics, - - Node::Item(item) => { - match item.kind { - ItemKind::Fn(.., generics, _) - | ItemKind::Impl(&hir::Impl { generics, .. }) - | ItemKind::TyAlias(_, generics) - | ItemKind::Const(_, generics, _) - | ItemKind::OpaqueTy(&OpaqueTy { - generics, - origin: hir::OpaqueTyOrigin::TyAlias { .. }, - .. - }) - | ItemKind::Enum(_, generics) - | ItemKind::Struct(_, generics) - | ItemKind::Union(_, generics) => generics, - ItemKind::Trait(_, _, generics, ..) => { - // Implied `Self: Trait` and supertrait bounds. - if param_id == item_hir_id { - let identity_trait_ref = - ty::TraitRef::identity(tcx, item_def_id.to_def_id()); - extend = Some((identity_trait_ref.to_predicate(tcx), item.span)); - } - generics - } - _ => return result, - } - } - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(_, _, generics) => generics, - _ => return result, - }, - _ => return result, - }; + let hir_node = tcx.hir_node(item_hir_id); + let Some(hir_generics) = hir_node.generics() else { return result }; + if let Node::Item(item) = hir_node + && let ItemKind::Trait(..) = item.kind + // Implied `Self: Trait` and supertrait bounds. + && param_id == item_hir_id + { + let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id.to_def_id()); + extend = Some((identity_trait_ref.to_predicate(tcx), item.span)); + } let icx = ItemCtxt::new(tcx, item_def_id); let extra_predicates = extend.into_iter().chain( - icx.type_parameter_bounds_in_generics( - ast_generics, + icx.probe_ty_param_bounds_in_generics( + hir_generics, def_id, ty, PredicateFilter::SelfThatDefines(assoc_name), @@ -763,21 +714,22 @@ pub(super) fn type_param_predicates( } impl<'tcx> ItemCtxt<'tcx> { - /// Finds bounds from `hir::Generics`. This requires scanning through the - /// AST. We do this to avoid having to convert *all* the bounds, which - /// would create artificial cycles. Instead, we can only convert the - /// bounds for a type parameter `X` if `X::Foo` is used. - #[instrument(level = "trace", skip(self, ast_generics))] - fn type_parameter_bounds_in_generics( + /// Finds bounds from `hir::Generics`. + /// + /// This requires scanning through the HIR. + /// We do this to avoid having to lower *all* the bounds, which would create artificial cycles. + /// Instead, we can only lower the bounds for a type parameter `X` if `X::Foo` is used. + #[instrument(level = "trace", skip(self, hir_generics))] + fn probe_ty_param_bounds_in_generics( &self, - ast_generics: &'tcx hir::Generics<'tcx>, + hir_generics: &'tcx hir::Generics<'tcx>, param_def_id: LocalDefId, ty: Ty<'tcx>, filter: PredicateFilter, ) -> Vec<(ty::Clause<'tcx>, Span)> { let mut bounds = Bounds::default(); - for predicate in ast_generics.predicates { + for predicate in hir_generics.predicates { let hir::WherePredicate::BoundPredicate(predicate) = predicate else { continue; }; @@ -799,13 +751,13 @@ impl<'tcx> ItemCtxt<'tcx> { let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) { ty } else if matches!(filter, PredicateFilter::All) { - self.to_ty(predicate.bounded_ty) + self.lower_ty(predicate.bounded_ty) } else { continue; }; let bound_vars = self.tcx.late_bound_vars(predicate.hir_id); - self.astconv().add_bounds( + self.lowerer().lower_poly_bounds( bound_ty, predicate.bounds.iter().filter(|bound| { assoc_name 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 d1da2fa0fdc26..a5f038d383d88 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -13,14 +13,13 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node}; +use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirId, HirIdMap, LifetimeName, Node}; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; -use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -108,7 +107,7 @@ enum Scope<'a> { /// queried later. However, if we enter an elision scope, we have to /// later append the elided bound vars to the list and need to know what /// to append to. - hir_id: hir::HirId, + hir_id: HirId, s: ScopeRef<'a>, @@ -262,7 +261,7 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou visitor.visit_impl_item(item) } hir::OwnerNode::Crate(_) => {} - hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), + hir::OwnerNode::Synthetic => unreachable!(), } let mut rl = ResolveBoundVars::default(); @@ -418,8 +417,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { { if let &hir::ClosureBinder::For { span: for_sp, .. } = binder { fn span_of_infer(ty: &hir::Ty<'_>) -> Option { - struct V; - impl<'v> Visitor<'v> for V { + /// Look for `_` anywhere in the signature of a `for<> ||` closure. + /// This is currently disallowed. + struct FindInferInClosureWithBinder; + impl<'v> Visitor<'v> for FindInferInClosureWithBinder { type Result = ControlFlow; fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result { if matches!(t.kind, hir::TyKind::Infer) { @@ -429,7 +430,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } } - V.visit_ty(ty).break_value() + FindInferInClosureWithBinder.visit_ty(ty).break_value() } let infer_in_rt_sp = match fn_decl.output { @@ -556,6 +557,50 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } + fn visit_precise_capturing_arg( + &mut self, + arg: &'tcx hir::PreciseCapturingArg<'tcx>, + ) -> Self::Result { + match *arg { + hir::PreciseCapturingArg::Lifetime(lt) => match lt.res { + LifetimeName::Param(def_id) => { + self.resolve_lifetime_ref(def_id, lt); + } + LifetimeName::Error => {} + LifetimeName::ImplicitObjectLifetimeDefault + | LifetimeName::Infer + | LifetimeName::Static => { + self.tcx.dcx().emit_err(errors::BadPreciseCapture { + span: lt.ident.span, + kind: "lifetime", + found: format!("`{}`", lt.ident.name), + }); + } + }, + hir::PreciseCapturingArg::Param(param) => match param.res { + Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id) + | Res::SelfTyParam { trait_: def_id } => { + self.resolve_type_ref(def_id.expect_local(), param.hir_id); + } + Res::Err => {} + Res::SelfTyAlias { alias_to, .. } => { + self.tcx.dcx().emit_err(errors::PreciseCaptureSelfAlias { + span: param.ident.span, + self_span: self.tcx.def_span(alias_to), + what: self.tcx.def_descr(alias_to), + }); + } + res => { + self.tcx.dcx().emit_err(errors::BadPreciseCapture { + span: param.ident.span, + kind: "type or const", + found: res.descr().to_string(), + }); + } + }, + } + } + fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) { match item.kind { hir::ForeignItemKind::Fn(_, _, generics) => { @@ -716,6 +761,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::*; @@ -775,7 +825,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } } - fn visit_path(&mut self, path: &hir::Path<'tcx>, hir_id: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, hir_id: HirId) { for (i, segment) in path.segments.iter().enumerate() { let depth = path.segments.len() - i - 1; if let Some(args) = segment.args { @@ -793,12 +843,20 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { fd: &'tcx hir::FnDecl<'tcx>, body_id: hir::BodyId, _: Span, - _: LocalDefId, + def_id: LocalDefId, ) { let output = match fd.output { hir::FnRetTy::DefaultReturn(_) => None, hir::FnRetTy::Return(ty) => Some(ty), }; + if let Some(ty) = output + && let hir::TyKind::InferDelegation(sig_id, _) = ty.kind + { + let bound_vars: Vec<_> = + self.tcx.fn_sig(sig_id).skip_binder().bound_vars().iter().collect(); + let hir_id = self.tcx.local_def_id_to_hir_id(def_id); + self.map.late_bound_vars.insert(hir_id, bound_vars); + } self.visit_fn_like_elision(fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); intravisit::walk_fn_kind(self, fk); self.visit_nested_body(body_id) @@ -857,31 +915,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }) => { self.visit_lifetime(lifetime); walk_list!(self, visit_param_bound, bounds); - - if lifetime.res != hir::LifetimeName::Static { - for bound in bounds { - let hir::GenericBound::Outlives(lt) = bound else { - continue; - }; - if lt.res != hir::LifetimeName::Static { - continue; - } - self.insert_lifetime(lt, ResolvedArg::StaticLifetime); - self.tcx.node_span_lint( - lint::builtin::UNUSED_LIFETIMES, - lifetime.hir_id, - lifetime.ident.span, - format!("unnecessary lifetime parameter `{}`", lifetime.ident), - |lint| { - let help = format!( - "you can use the `'static` lifetime directly, in place of `{}`", - lifetime.ident, - ); - lint.help(help); - }, - ); - } - } } &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { self.visit_ty(lhs_ty); @@ -994,7 +1027,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } } - fn record_late_bound_vars(&mut self, hir_id: hir::HirId, binder: Vec) { + fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec) { if let Some(old) = self.map.late_bound_vars.insert(hir_id, binder) { bug!( "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}", @@ -1021,12 +1054,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { /// already in scope (for a fn item, that will be 0, but for a method it might not be). Late /// bound lifetimes are resolved by name and associated with a binder ID (`binder_id`), so the /// ordering is not important there. - fn visit_early_late( - &mut self, - hir_id: hir::HirId, - generics: &'tcx hir::Generics<'tcx>, - walk: F, - ) where + fn visit_early_late(&mut self, hir_id: HirId, generics: &'tcx hir::Generics<'tcx>, walk: F) + where F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>), { let mut named_late_bound_vars = 0; @@ -1073,7 +1102,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { self.with(scope, walk); } - fn visit_early(&mut self, hir_id: hir::HirId, generics: &'tcx hir::Generics<'tcx>, walk: F) + fn visit_early(&mut self, hir_id: HirId, generics: &'tcx hir::Generics<'tcx>, walk: F) where F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>), { @@ -1299,7 +1328,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { ); } - fn resolve_type_ref(&mut self, param_def_id: LocalDefId, hir_id: hir::HirId) { + fn resolve_type_ref(&mut self, param_def_id: LocalDefId, hir_id: HirId) { // Walk up the scope chain, tracking the number of fn scopes // that we pass through, until we find a lifetime with the // given name or we run out of scopes. @@ -1917,18 +1946,18 @@ fn is_late_bound_map( /// /// If we conservatively considered `'a` unconstrained then we could break users who had written code before /// we started correctly handling aliases. If we considered `'a` constrained then it would become late bound - /// causing an error during astconv as the `'a` is not constrained by the input type `<() as Trait<'a>>::Assoc` + /// causing an error during HIR ty lowering as the `'a` is not constrained by the input type `<() as Trait<'a>>::Assoc` /// but appears in the output type `<() as Trait<'a>>::Assoc`. /// /// We must therefore "look into" the `Alias` to see whether we should consider `'a` constrained or not. /// /// See #100508 #85533 #47511 for additional context - struct ConstrainedCollectorPostAstConv { + struct ConstrainedCollectorPostHirTyLowering { arg_is_constrained: Box<[bool]>, } use ty::Ty; - impl<'tcx> TypeVisitor> for ConstrainedCollectorPostAstConv { + impl<'tcx> TypeVisitor> for ConstrainedCollectorPostHirTyLowering { fn visit_ty(&mut self, t: Ty<'tcx>) { match t.kind() { ty::Param(param_ty) => { @@ -1970,10 +1999,10 @@ fn is_late_bound_map( None, hir::Path { res: Res::Def(DefKind::TyAlias, alias_def), segments, span }, )) => { - // See comments on `ConstrainedCollectorPostAstConv` for why this arm does not just consider - // args to be unconstrained. + // See comments on `ConstrainedCollectorPostHirTyLowering` for why this arm does not + // just consider args to be unconstrained. let generics = self.tcx.generics_of(alias_def); - let mut walker = ConstrainedCollectorPostAstConv { + let mut walker = ConstrainedCollectorPostHirTyLowering { arg_is_constrained: vec![false; generics.params.len()].into_boxed_slice(), }; walker.visit_ty(self.tcx.type_of(alias_def).instantiate_identity()); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index fd86f2dd1b16c..9d7deebac48e4 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -47,7 +47,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { let ty = tcx.fold_regions(ty, |r, _| { if r.is_erased() { ty::Region::new_error_misc(tcx) } else { r } }); - let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) { + let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false, None) { (ty, Some((span, Applicability::MachineApplicable))) } else { (ty, None) @@ -97,10 +97,10 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU Node::Ty(hir_ty @ hir::Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { // Find the Item containing the associated type so we can create an ItemCtxt. - // Using the ItemCtxt convert the HIR for the unresolved assoc type into a + // Using the ItemCtxt lower the HIR for the unresolved assoc type into a // ty which is a fully resolved projection. - // For the code example above, this would mean converting Self::Assoc<3> - // into a ty::Alias(ty::Projection, ::Assoc<3>) + // For the code example above, this would mean lowering `Self::Assoc<3>` + // to a ty::Alias(ty::Projection, `::Assoc<3>`). let item_def_id = tcx .hir() .parent_owner_iter(hir_id) @@ -108,7 +108,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { .unwrap() .0 .def_id; - let ty = ItemCtxt::new(tcx, item_def_id).to_ty(hir_ty); + let ty = ItemCtxt::new(tcx, item_def_id).lower_ty(hir_ty); // Iterate through the generics of the projection to find the one that corresponds to // the def_id that this query was called with. We filter to only type and const args here @@ -369,8 +369,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder icx.to_ty(ty), + .unwrap_or_else(|| icx.lower_ty(ty)), + TraitItemKind::Type(_, Some(ty)) => icx.lower_ty(ty), TraitItemKind::Type(_, None) => { span_bug!(item.span, "associated type missing default"); } @@ -392,7 +392,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { @@ -400,7 +400,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder, def_id: LocalDefId) -> ty::EarlyBinder { if ty.is_suggestable_infer_ty() { infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant") } else { - icx.to_ty(ty) + icx.lower_ty(ty) } } - ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty), + ItemKind::TyAlias(self_ty, _) => icx.lower_ty(self_ty), ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() { spans if spans.len() > 0 => { let guar = tcx @@ -434,7 +434,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder icx.to_ty(*self_ty), + _ => icx.lower_ty(*self_ty), }, ItemKind::Fn(..) => { let args = ty::GenericArgs::identity_for_item(tcx, def_id); @@ -466,7 +466,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder icx.to_ty(t), + ForeignItemKind::Static(t, _) => icx.lower_ty(t), ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()), }, @@ -474,13 +474,13 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { tcx.type_of(tcx.hir().get_parent_item(hir_id)).instantiate_identity() } - VariantData::Tuple(..) => { + VariantData::Tuple(_, _, ctor) => { let args = ty::GenericArgs::identity_for_item(tcx, def_id); - Ty::new_fn_def(tcx, def_id.to_def_id(), args) + Ty::new_fn_def(tcx, ctor.to_def_id(), args) } }, - Node::Field(field) => icx.to_ty(field.ty), + Node::Field(field) => icx.lower_ty(field.ty), Node::Expr(&Expr { kind: ExprKind::Closure { .. }, .. }) => { tcx.typeck(def_id).node_type(hir_id) @@ -495,7 +495,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder match ¶m.kind { GenericParamKind::Type { default: Some(ty), .. } - | GenericParamKind::Const { ty, .. } => icx.to_ty(ty), + | GenericParamKind::Const { ty, .. } => icx.lower_ty(ty), x => bug!("unexpected non-type Node::GenericParam: {:?}", x), }, @@ -587,7 +587,7 @@ fn infer_placeholder_type<'a>( suggestions.clear(); } - if let Some(ty) = ty.make_suggestable(tcx, false) { + if let Some(ty) = ty.make_suggestable(tcx, false, None) { err.span_suggestion( span, format!("provide a type for the {kind}"), @@ -606,7 +606,7 @@ fn infer_placeholder_type<'a>( let mut diag = bad_placeholder(tcx, vec![span], kind); if !ty.references_error() { - if let Some(ty) = ty.make_suggestable(tcx, false) { + if let Some(ty) = ty.make_suggestable(tcx, false, None) { diag.span_suggestion( span, "replace with the correct type", diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 7bf87f444db29..1c99713b3ae18 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -7,6 +7,11 @@ use rustc_errors::{ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span, Symbol}; +mod pattern_types; +pub use pattern_types::*; + +mod precise_captures; +pub(crate) use precise_captures::*; #[derive(Diagnostic)] #[diag(hir_analysis_ambiguous_assoc_item)] @@ -295,6 +300,44 @@ pub struct AssocTypeBindingNotAllowed { pub fn_trait_expansion: Option, } +#[derive(Diagnostic)] +#[diag(hir_analysis_param_in_ty_of_assoc_const_binding)] +pub(crate) struct ParamInTyOfAssocConstBinding<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub assoc_const: Ident, + pub param_name: Symbol, + pub param_def_kind: &'static str, + pub param_category: &'static str, + #[label(hir_analysis_param_defined_here_label)] + pub param_defined_here_label: Option, + #[subdiagnostic] + pub ty_note: Option>, +} + +#[derive(Subdiagnostic, Clone, Copy)] +#[note(hir_analysis_ty_of_assoc_const_binding_note)] +pub(crate) struct TyOfAssocConstBindingNote<'tcx> { + pub assoc_const: Ident, + pub ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding)] +pub(crate) struct EscapingBoundVarInTyOfAssocConstBinding<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub assoc_const: Ident, + pub var_name: Symbol, + pub var_def_kind: &'static str, + #[label(hir_analysis_var_defined_here_label)] + pub var_defined_here_label: Span, + #[subdiagnostic] + pub ty_note: Option>, +} + #[derive(Subdiagnostic)] #[help(hir_analysis_parenthesized_fn_trait_expansion)] pub struct ParenthesizedFnTraitExpansion { @@ -1312,33 +1355,58 @@ pub struct CrossCrateTraitsDefined { pub traits: String, } +// FIXME(fmease): Deduplicate: + #[derive(Diagnostic)] #[diag(hir_analysis_ty_param_first_local, code = E0210)] #[note] -pub struct TyParamFirstLocal<'a> { +pub struct TyParamFirstLocal<'tcx> { #[primary_span] #[label] pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param_ty: Ty<'a>, - pub local_type: Ty<'a>, + pub param: Symbol, + pub local_type: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_ty_param_first_local, code = E0210)] +#[note] +pub struct TyParamFirstLocalLint<'tcx> { + #[label] + pub span: Span, + #[note(hir_analysis_case_note)] + pub note: (), + pub param: Symbol, + pub local_type: Ty<'tcx>, } #[derive(Diagnostic)] #[diag(hir_analysis_ty_param_some, code = E0210)] #[note] -pub struct TyParamSome<'a> { +pub struct TyParamSome { #[primary_span] #[label] pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param_ty: Ty<'a>, + pub param: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_ty_param_some, code = E0210)] +#[note] +pub struct TyParamSomeLint { + #[label] + pub span: Span, + #[note(hir_analysis_only_note)] + pub note: (), + pub param: Symbol, } #[derive(Diagnostic)] -pub enum OnlyCurrentTraits<'a> { +pub enum OnlyCurrentTraits { #[diag(hir_analysis_only_current_traits_outside, code = E0117)] Outside { #[primary_span] @@ -1346,18 +1414,6 @@ pub enum OnlyCurrentTraits<'a> { span: Span, #[note(hir_analysis_only_current_traits_note)] note: (), - #[subdiagnostic] - opaque: Vec, - #[subdiagnostic] - foreign: Vec, - #[subdiagnostic] - name: Vec>, - #[subdiagnostic] - pointer: Vec>, - #[subdiagnostic] - ty: Vec>, - #[subdiagnostic] - sugg: Option>, }, #[diag(hir_analysis_only_current_traits_primitive, code = E0117)] Primitive { @@ -1366,18 +1422,6 @@ pub enum OnlyCurrentTraits<'a> { span: Span, #[note(hir_analysis_only_current_traits_note)] note: (), - #[subdiagnostic] - opaque: Vec, - #[subdiagnostic] - foreign: Vec, - #[subdiagnostic] - name: Vec>, - #[subdiagnostic] - pointer: Vec>, - #[subdiagnostic] - ty: Vec>, - #[subdiagnostic] - sugg: Option>, }, #[diag(hir_analysis_only_current_traits_arbitrary, code = E0117)] Arbitrary { @@ -1386,18 +1430,6 @@ pub enum OnlyCurrentTraits<'a> { span: Span, #[note(hir_analysis_only_current_traits_note)] note: (), - #[subdiagnostic] - opaque: Vec, - #[subdiagnostic] - foreign: Vec, - #[subdiagnostic] - name: Vec>, - #[subdiagnostic] - pointer: Vec>, - #[subdiagnostic] - ty: Vec>, - #[subdiagnostic] - sugg: Option>, }, } @@ -1407,7 +1439,6 @@ pub struct OnlyCurrentTraitsOpaque { #[primary_span] pub span: Span, } - #[derive(Subdiagnostic)] #[label(hir_analysis_only_current_traits_foreign)] pub struct OnlyCurrentTraitsForeign { @@ -1439,6 +1470,14 @@ pub struct OnlyCurrentTraitsTy<'a> { pub ty: Ty<'a>, } +#[derive(Subdiagnostic)] +#[label(hir_analysis_only_current_traits_adt)] +pub struct OnlyCurrentTraitsAdt { + #[primary_span] + pub span: Span, + pub name: String, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( hir_analysis_only_current_traits_pointer_sugg, @@ -1620,3 +1659,20 @@ pub struct OpaqueCapturesHigherRankedLifetime { pub decl_span: Span, pub bad_place: &'static str, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_pattern_type_non_const_range)] +pub struct NonConstRange { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_invalid_receiver_ty, code = E0307)] +#[note] +#[help(hir_analysis_invalid_receiver_ty_help)] +pub struct InvalidReceiverTy<'tcx> { + #[primary_span] + pub span: Span, + pub receiver_ty: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs new file mode 100644 index 0000000000000..008d26989890d --- /dev/null +++ b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs @@ -0,0 +1,9 @@ +use rustc_macros::Diagnostic; +use rustc_span::Span; + +#[derive(Diagnostic)] +#[diag(hir_analysis_pattern_type_wild_pat)] +pub struct WildPatTy { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs new file mode 100644 index 0000000000000..8a9b5fe636967 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -0,0 +1,73 @@ +use rustc_macros::Diagnostic; +use rustc_span::{Span, Symbol}; + +#[derive(Diagnostic)] +#[diag(hir_analysis_param_not_captured)] +#[note] +pub struct ParamNotCaptured { + #[primary_span] + pub opaque_span: Span, + #[label] + pub param_span: Span, + pub kind: &'static str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_self_ty_not_captured)] +#[note] +pub struct SelfTyNotCaptured { + #[primary_span] + pub opaque_span: Span, + #[label] + pub trait_span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_lifetime_not_captured)] +pub struct LifetimeNotCaptured { + #[primary_span] + pub use_span: Span, + #[label(hir_analysis_param_label)] + pub param_span: Span, + #[label] + pub opaque_span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_bad_precise_capture)] +pub struct BadPreciseCapture { + #[primary_span] + pub span: Span, + pub kind: &'static str, + pub found: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_precise_capture_self_alias)] +pub struct PreciseCaptureSelfAlias { + #[primary_span] + pub span: Span, + #[label] + pub self_span: Span, + pub what: &'static str, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_duplicate_precise_capture)] +pub struct DuplicatePreciseCapture { + #[primary_span] + pub first_span: Span, + pub name: Symbol, + #[label] + pub second_span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_lifetime_must_be_first)] +pub struct LifetimesMustBeFirst { + #[primary_span] + pub lifetime_span: Span, + pub name: Symbol, + #[label] + pub other_span: Span, +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs new file mode 100644 index 0000000000000..11bd3e5282dc1 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -0,0 +1,682 @@ +use std::ops::ControlFlow; + +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_errors::{codes::*, struct_span_code_err}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; +use rustc_span::symbol::Ident; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_trait_selection::traits; +use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; +use smallvec::SmallVec; + +use crate::bounds::Bounds; +use crate::errors; +use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; + +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Add a `Sized` bound to the `bounds` if appropriate. + /// + /// Doesn't add the bound if the HIR bounds contain any of `Sized`, `?Sized` or `!Sized`. + pub(crate) fn add_sized_bound( + &self, + bounds: &mut Bounds<'tcx>, + self_ty: Ty<'tcx>, + hir_bounds: &'tcx [hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, + span: Span, + ) { + let tcx = self.tcx(); + let sized_def_id = tcx.lang_items().sized_trait(); + let mut seen_negative_sized_bound = false; + let mut seen_positive_sized_bound = false; + + // Try to find an unbound in bounds. + let mut unbounds: SmallVec<[_; 1]> = SmallVec::new(); + let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| { + for hir_bound in hir_bounds { + let hir::GenericBound::Trait(ptr, modifier) = hir_bound else { + continue; + }; + match modifier { + hir::TraitBoundModifier::Maybe => unbounds.push(ptr), + hir::TraitBoundModifier::Negative => { + if let Some(sized_def_id) = sized_def_id + && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) + { + seen_negative_sized_bound = true; + } + } + hir::TraitBoundModifier::None => { + if let Some(sized_def_id) = sized_def_id + && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) + { + seen_positive_sized_bound = true; + } + } + _ => {} + } + } + }; + search_bounds(hir_bounds); + if let Some((self_ty, where_clause)) = self_ty_where_predicates { + for clause in where_clause { + if let hir::WherePredicate::BoundPredicate(pred) = clause + && pred.is_param_bound(self_ty.to_def_id()) + { + search_bounds(pred.bounds); + } + } + } + + if unbounds.len() > 1 { + tcx.dcx().emit_err(errors::MultipleRelaxedDefaultBounds { + spans: unbounds.iter().map(|ptr| ptr.span).collect(), + }); + } + + let mut seen_sized_unbound = false; + for unbound in unbounds { + if let Some(sized_def_id) = sized_def_id + && unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) + { + seen_sized_unbound = true; + continue; + } + // There was a `?Trait` bound, but it was not `?Sized`; warn. + tcx.dcx().span_warn( + unbound.span, + "relaxing a default bound only does something for `?Sized`; \ + all other traits are not bound by default", + ); + } + + if seen_sized_unbound || seen_negative_sized_bound || seen_positive_sized_bound { + // There was in fact a `?Sized`, `!Sized` or explicit `Sized` bound; + // we don't need to do anything. + } else if sized_def_id.is_some() { + // There was no `?Sized`, `!Sized` or explicit `Sized` bound; + // add `Sized` if it's available. + bounds.push_sized(tcx, self_ty, span); + } + } + + /// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any. + /// + /// ### Examples + /// + /// ```ignore (illustrative) + /// fn foo() where for<'a> T: Trait<'a> + Copy {} + /// // ^^^^^^^ ^ ^^^^^^^^^^^^^^^^ `hir_bounds`, in HIR form + /// // | | + /// // | `param_ty`, in ty form + /// // `bound_vars`, in ty form + /// + /// fn bar() where T: for<'a> Trait<'a> + Copy {} // no overarching `bound_vars` here! + /// // ^ ^^^^^^^^^^^^^^^^^^^^^^^^ `hir_bounds`, in HIR form + /// // | + /// // `param_ty`, in ty form + /// ``` + /// + /// ### A Note on Binders + /// + /// There is an implied binder around `param_ty` and `hir_bounds`. + /// See `lower_poly_trait_ref` for more details. + #[instrument(level = "debug", skip(self, hir_bounds, bounds))] + pub(crate) fn lower_poly_bounds<'hir, I: Iterator>>( + &self, + param_ty: Ty<'tcx>, + hir_bounds: I, + bounds: &mut Bounds<'tcx>, + bound_vars: &'tcx ty::List, + only_self_bounds: OnlySelfBounds, + ) where + 'tcx: 'hir, + { + for hir_bound in hir_bounds { + match hir_bound { + hir::GenericBound::Trait(poly_trait_ref, modifier) => { + let (constness, polarity) = match modifier { + hir::TraitBoundModifier::Const => { + (ty::BoundConstness::Const, ty::PredicatePolarity::Positive) + } + hir::TraitBoundModifier::MaybeConst => { + (ty::BoundConstness::ConstIfConst, ty::PredicatePolarity::Positive) + } + hir::TraitBoundModifier::None => { + (ty::BoundConstness::NotConst, ty::PredicatePolarity::Positive) + } + hir::TraitBoundModifier::Negative => { + (ty::BoundConstness::NotConst, ty::PredicatePolarity::Negative) + } + hir::TraitBoundModifier::Maybe => continue, + }; + let _ = self.lower_poly_trait_ref( + &poly_trait_ref.trait_ref, + poly_trait_ref.span, + constness, + polarity, + param_ty, + bounds, + only_self_bounds, + ); + } + hir::GenericBound::Outlives(lifetime) => { + let region = self.lower_lifetime(lifetime, None); + bounds.push_region_bound( + self.tcx(), + ty::Binder::bind_with_vars( + ty::OutlivesPredicate(param_ty, region), + bound_vars, + ), + lifetime.ident.span, + ); + } + } + } + } + + /// Lower HIR bounds into `bounds` given the self type `param_ty` and *no* overarching late-bound vars. + /// + /// ### Example + /// + /// ```ignore (illustrative) + /// fn foo() { } + /// // ^ ^^^^^^^^^ hir_bounds + /// // param_ty + /// ``` + pub(crate) fn lower_mono_bounds( + &self, + param_ty: Ty<'tcx>, + hir_bounds: &[hir::GenericBound<'tcx>], + filter: PredicateFilter, + ) -> Bounds<'tcx> { + let mut bounds = Bounds::default(); + + let only_self_bounds = match filter { + PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { + OnlySelfBounds(false) + } + PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => OnlySelfBounds(true), + }; + + self.lower_poly_bounds( + param_ty, + hir_bounds.iter().filter(|bound| match filter { + PredicateFilter::All + | PredicateFilter::SelfOnly + | PredicateFilter::SelfAndAssociatedTypeBounds => true, + PredicateFilter::SelfThatDefines(assoc_name) => { + if let Some(trait_ref) = bound.trait_ref() + && let Some(trait_did) = trait_ref.trait_def_id() + && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) + { + true + } else { + false + } + } + }), + &mut bounds, + ty::List::empty(), + only_self_bounds, + ); + debug!(?bounds); + + bounds + } + + /// Lower an associated item binding from HIR into `bounds`. + /// + /// ### A Note on Binders + /// + /// Given something like `T: for<'a> Iterator`, + /// the `trait_ref` here will be `for<'a> T: Iterator`. + /// The `binding` data however is from *inside* the binder + /// (e.g., `&'a u32`) and hence may reference bound regions. + #[instrument(level = "debug", skip(self, bounds, dup_bindings, path_span))] + pub(super) fn lower_assoc_item_binding( + &self, + hir_ref_id: hir::HirId, + trait_ref: ty::PolyTraitRef<'tcx>, + binding: &hir::TypeBinding<'tcx>, + bounds: &mut Bounds<'tcx>, + dup_bindings: &mut FxIndexMap, + path_span: Span, + only_self_bounds: OnlySelfBounds, + ) -> Result<(), ErrorGuaranteed> { + let tcx = self.tcx(); + + let assoc_kind = if binding.gen_args.parenthesized + == hir::GenericArgsParentheses::ReturnTypeNotation + { + ty::AssocKind::Fn + } else if let hir::TypeBindingKind::Equality { term: hir::Term::Const(_) } = binding.kind { + ty::AssocKind::Const + } else { + ty::AssocKind::Type + }; + + // Given something like `U: Trait`, we want to produce a predicate like + // `::T = X`. + // This is somewhat subtle in the event that `T` is defined in a supertrait of `Trait`, + // because in that case we need to upcast. I.e., we want to produce + // `>::T == X` for `B: SubTrait` where + // + // trait SubTrait: SuperTrait {} + // trait SuperTrait { type T; } + let candidate = if self.probe_trait_that_defines_assoc_item( + trait_ref.def_id(), + assoc_kind, + binding.ident, + ) { + // Simple case: The assoc item is defined in the current trait. + trait_ref + } else { + // Otherwise, we have to walk through the supertraits to find + // one that does define it. + self.probe_single_bound_for_assoc_item( + || traits::supertraits(tcx, trait_ref), + trait_ref.skip_binder().print_only_trait_name(), + None, + assoc_kind, + binding.ident, + path_span, + Some(binding), + )? + }; + + let (assoc_ident, def_scope) = + tcx.adjust_ident_and_get_scope(binding.ident, candidate.def_id(), hir_ref_id); + + // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` + // instead of calling `filter_by_name_and_kind` which would needlessly normalize the + // `assoc_ident` again and again. + let assoc_item = tcx + .associated_items(candidate.def_id()) + .filter_by_name_unhygienic(assoc_ident.name) + .find(|i| i.kind == assoc_kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident) + .expect("missing associated item"); + + if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) { + let reported = tcx + .dcx() + .struct_span_err( + binding.span, + format!("{} `{}` is private", assoc_item.kind, binding.ident), + ) + .with_span_label(binding.span, format!("private {}", assoc_item.kind)) + .emit(); + self.set_tainted_by_errors(reported); + } + tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); + + dup_bindings + .entry(assoc_item.def_id) + .and_modify(|prev_span| { + tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified { + span: binding.span, + prev_span: *prev_span, + item_name: binding.ident, + def_path: tcx.def_path_str(assoc_item.container_id(tcx)), + }); + }) + .or_insert(binding.span); + + let projection_ty = if let ty::AssocKind::Fn = assoc_kind { + let mut emitted_bad_param_err = None; + // If we have an method return type bound, then we need to instantiate + // the method's early bound params with suitable late-bound params. + let mut num_bound_vars = candidate.bound_vars().len(); + let args = + candidate.skip_binder().args.extend_to(tcx, assoc_item.def_id, |param, _| { + let arg = match param.kind { + ty::GenericParamDefKind::Lifetime => ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_usize(num_bound_vars), + kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name), + }, + ) + .into(), + ty::GenericParamDefKind::Type { .. } => { + let guar = *emitted_bad_param_err.get_or_insert_with(|| { + tcx.dcx().emit_err( + crate::errors::ReturnTypeNotationIllegalParam::Type { + span: path_span, + param_span: tcx.def_span(param.def_id), + }, + ) + }); + Ty::new_error(tcx, guar).into() + } + ty::GenericParamDefKind::Const { .. } => { + let guar = *emitted_bad_param_err.get_or_insert_with(|| { + tcx.dcx().emit_err( + crate::errors::ReturnTypeNotationIllegalParam::Const { + span: path_span, + param_span: tcx.def_span(param.def_id), + }, + ) + }); + let ty = tcx + .type_of(param.def_id) + .no_bound_vars() + .expect("ct params cannot have early bound vars"); + ty::Const::new_error(tcx, guar, ty).into() + } + }; + num_bound_vars += 1; + arg + }); + + // Next, we need to check that the return-type notation is being used on + // an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait). + let output = tcx.fn_sig(assoc_item.def_id).skip_binder().output(); + let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind() + && tcx.is_impl_trait_in_trait(alias_ty.def_id) + { + alias_ty + } else { + return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit { + span: binding.span, + ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output), + fn_span: tcx.hir().span_if_local(assoc_item.def_id), + note: (), + })); + }; + + // Finally, move the fn return type's bound vars over to account for the early bound + // params (and trait ref's late bound params). This logic is very similar to + // `rustc_middle::ty::predicate::Clause::instantiate_supertrait` + // and it's no coincidence why. + let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output); + let instantiation_output = ty::EarlyBinder::bind(shifted_output).instantiate(tcx, args); + + let bound_vars = tcx.late_bound_vars(binding.hir_id); + ty::Binder::bind_with_vars(instantiation_output, bound_vars) + } else { + // Create the generic arguments for the associated type or constant by joining the + // parent arguments (the arguments of the trait) and the own arguments (the ones of + // the associated item itself) and construct an alias type using them. + let alias_ty = candidate.map_bound(|trait_ref| { + let ident = Ident::new(assoc_item.name, binding.ident.span); + let item_segment = hir::PathSegment { + ident, + hir_id: binding.hir_id, + res: Res::Err, + args: Some(binding.gen_args), + infer_args: false, + }; + + let alias_args = self.lower_generic_args_of_assoc_item( + path_span, + assoc_item.def_id, + &item_segment, + trait_ref.args, + ); + debug!(?alias_args); + + // Note that we're indeed also using `AliasTy` (alias *type*) for associated + // *constants* to represent *const projections*. Alias *term* would be a more + // appropriate name but alas. + ty::AliasTy::new(tcx, assoc_item.def_id, alias_args) + }); + + // Provide the resolved type of the associated constant to `type_of(AnonConst)`. + if let hir::TypeBindingKind::Equality { term: hir::Term::Const(anon_const) } = + binding.kind + { + let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args)); + let ty = check_assoc_const_binding_type(tcx, assoc_ident, ty, binding.hir_id); + tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); + } + + alias_ty + }; + + match binding.kind { + hir::TypeBindingKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => { + return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound { + span: binding.span, + })); + } + // Lower an equality constraint like `Item = u32` as found in HIR bound `T: Iterator` + // to a projection predicate: `::Item = u32`. + hir::TypeBindingKind::Equality { term } => { + let term = match term { + hir::Term::Ty(ty) => self.lower_ty(ty).into(), + hir::Term::Const(ct) => ty::Const::from_anon_const(tcx, ct.def_id).into(), + }; + + // Find any late-bound regions declared in `ty` that are not + // declared in the trait-ref or assoc_item. These are not well-formed. + // + // Example: + // + // for<'a> ::Item = &'a str // <-- 'a is bad + // for<'a> >::Output = &'a str // <-- 'a is ok + let late_bound_in_projection_ty = + tcx.collect_constrained_late_bound_regions(projection_ty); + let late_bound_in_term = + tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term)); + debug!(?late_bound_in_projection_ty); + debug!(?late_bound_in_term); + + // FIXME: point at the type params that don't have appropriate lifetimes: + // struct S1 Fn(&i32, &i32) -> &'a i32>(F); + // ---- ---- ^^^^^^^ + // NOTE(associated_const_equality): This error should be impossible to trigger + // with associated const equality bounds. + self.validate_late_bound_regions( + late_bound_in_projection_ty, + late_bound_in_term, + |br_name| { + struct_span_code_err!( + tcx.dcx(), + binding.span, + E0582, + "binding for associated type `{}` references {}, \ + which does not appear in the trait input types", + binding.ident, + br_name + ) + }, + ); + + bounds.push_projection_bound( + tcx, + projection_ty + .map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }), + binding.span, + ); + } + // Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator` + // to a bound involving a projection: `::Item: Debug`. + hir::TypeBindingKind::Constraint { bounds: hir_bounds } => { + // NOTE: If `only_self_bounds` is true, do NOT expand this associated type bound into + // a trait predicate, since we only want to add predicates for the `Self` type. + if !only_self_bounds.0 { + // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` + // parameter to have a skipped binder. + let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); + self.lower_poly_bounds( + param_ty, + hir_bounds.iter(), + bounds, + projection_ty.bound_vars(), + only_self_bounds, + ); + } + } + } + Ok(()) + } +} + +/// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings. +/// +/// FIXME(const_generics): This is a temporary and semi-artifical restriction until the +/// arrival of *generic const generics*[^1]. +/// +/// It might actually be possible that we can already support early-bound generic params +/// in such types if we just lifted some more checks in other places, too, for example +/// inside [`ty::Const::from_anon_const`]. However, even if that were the case, we should +/// probably gate this behind another feature flag. +/// +/// [^1]: . +fn check_assoc_const_binding_type<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_const: Ident, + ty: ty::Binder<'tcx, Ty<'tcx>>, + hir_id: hir::HirId, +) -> Ty<'tcx> { + // We can't perform the checks for early-bound params during name resolution unlike E0770 + // because this information depends on *type* resolution. + // We can't perform these checks in `resolve_bound_vars` either for the same reason. + // Consider the trait ref `for<'a> Trait<'a, C = { &0 }>`. We need to know the fully + // resolved type of `Trait::C` in order to know if it references `'a` or not. + + let ty = ty.skip_binder(); + if !ty.has_param() && !ty.has_escaping_bound_vars() { + return ty; + } + + let mut collector = GenericParamAndBoundVarCollector { + tcx, + params: Default::default(), + vars: Default::default(), + depth: ty::INNERMOST, + }; + let mut guar = ty.visit_with(&mut collector).break_value(); + + let ty_note = ty + .make_suggestable(tcx, false, None) + .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); + + let enclosing_item_owner_id = tcx + .hir() + .parent_owner_iter(hir_id) + .find_map(|(owner_id, parent)| parent.generics().map(|_| owner_id)) + .unwrap(); + let generics = tcx.generics_of(enclosing_item_owner_id); + for index in collector.params { + let param = generics.param_at(index as _, tcx); + let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper; + guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding { + span: assoc_const.span, + assoc_const, + param_name: param.name, + param_def_kind: tcx.def_descr(param.def_id), + param_category: if is_self_param { + "self" + } else if param.kind.is_synthetic() { + "synthetic" + } else { + "normal" + }, + param_defined_here_label: + (!is_self_param).then(|| tcx.def_ident_span(param.def_id).unwrap()), + ty_note, + })); + } + for (var_def_id, var_name) in collector.vars { + guar.get_or_insert(tcx.dcx().emit_err( + crate::errors::EscapingBoundVarInTyOfAssocConstBinding { + span: assoc_const.span, + assoc_const, + var_name, + var_def_kind: tcx.def_descr(var_def_id), + var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(), + ty_note, + }, + )); + } + + let guar = guar.unwrap_or_else(|| bug!("failed to find gen params or bound vars in ty")); + Ty::new_error(tcx, guar) +} + +struct GenericParamAndBoundVarCollector<'tcx> { + tcx: TyCtxt<'tcx>, + params: FxIndexSet, + vars: FxIndexSet<(DefId, Symbol)>, + depth: ty::DebruijnIndex, +} + +impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'tcx> { + type Result = ControlFlow; + + fn visit_binder>>( + &mut self, + binder: &ty::Binder<'tcx, T>, + ) -> Self::Result { + self.depth.shift_in(1); + let result = binder.super_visit_with(self); + self.depth.shift_out(1); + result + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + match ty.kind() { + ty::Param(param) => { + self.params.insert(param.index); + } + ty::Bound(db, bt) if *db >= self.depth => { + self.vars.insert(match bt.kind { + ty::BoundTyKind::Param(def_id, name) => (def_id, name), + ty::BoundTyKind::Anon => { + let reported = self + .tcx + .dcx() + .delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var)); + return ControlFlow::Break(reported); + } + }); + } + _ if ty.has_param() || ty.has_bound_vars() => return ty.super_visit_with(self), + _ => {} + } + ControlFlow::Continue(()) + } + + fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result { + match re.kind() { + ty::ReEarlyParam(param) => { + self.params.insert(param.index); + } + ty::ReBound(db, br) if db >= self.depth => { + self.vars.insert(match br.kind { + ty::BrNamed(def_id, name) => (def_id, name), + ty::BrAnon | ty::BrEnv => { + let guar = self + .tcx + .dcx() + .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind)); + return ControlFlow::Break(guar); + } + }); + } + _ => {} + } + ControlFlow::Continue(()) + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { + match ct.kind() { + ty::ConstKind::Param(param) => { + self.params.insert(param.index); + } + ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => { + let guar = self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var"); + return ControlFlow::Break(guar); + } + _ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self), + _ => {} + } + ControlFlow::Continue(()) + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs new file mode 100644 index 0000000000000..8e64a9425bb9f --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -0,0 +1,1580 @@ +use crate::errors::{ + self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, + ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, +}; +use crate::fluent_generated as fluent; +use crate::hir_ty_lowering::HirTyLowerer; +use crate::traits::error_reporting::report_object_safety_error; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::unord::UnordMap; +use rustc_errors::MultiSpan; +use rustc_errors::{ + codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, +}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::traits::FulfillmentError; +use rustc_middle::query::Key; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{self, suggest_constraining_type_param}; +use rustc_middle::ty::{AdtDef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{Binder, TraitRef}; +use rustc_session::parse::feature_err; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::BytePos; +use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_trait_selection::traits::{ + object_safety_violations_for_assoc_item, TraitAliasExpansionInfo, +}; + +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// On missing type parameters, emit an E0393 error and provide a structured suggestion using + /// the type parameter's name as a placeholder. + pub(crate) fn complain_about_missing_type_params( + &self, + missing_type_params: Vec, + def_id: DefId, + span: Span, + empty_generic_args: bool, + ) { + if missing_type_params.is_empty() { + return; + } + + self.tcx().dcx().emit_err(MissingTypeParams { + span, + def_span: self.tcx().def_span(def_id), + span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(), + missing_type_params, + empty_generic_args, + }); + } + + /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit + /// an error and attempt to build a reasonable structured suggestion. + pub(crate) fn complain_about_internal_fn_trait( + &self, + span: Span, + trait_def_id: DefId, + trait_segment: &'_ hir::PathSegment<'_>, + is_impl: bool, + ) { + if self.tcx().features().unboxed_closures { + return; + } + + let trait_def = self.tcx().trait_def(trait_def_id); + if !trait_def.paren_sugar { + if trait_segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar { + // For now, require that parenthetical notation be used only with `Fn()` etc. + feature_err( + &self.tcx().sess, + sym::unboxed_closures, + span, + "parenthetical notation is only stable when used with `Fn`-family traits", + ) + .emit(); + } + + return; + } + + let sess = self.tcx().sess; + + if trait_segment.args().parenthesized != hir::GenericArgsParentheses::ParenSugar { + // For now, require that parenthetical notation be used only with `Fn()` etc. + let mut err = feature_err( + sess, + sym::unboxed_closures, + span, + "the precise format of `Fn`-family traits' type parameters is subject to change", + ); + // Do not suggest the other syntax if we are in trait impl: + // the desugaring would contain an associated type constraint. + if !is_impl { + err.span_suggestion( + span, + "use parenthetical notation instead", + fn_trait_to_string(self.tcx(), trait_segment, true), + Applicability::MaybeIncorrect, + ); + } + err.emit(); + } + + if is_impl { + let trait_name = self.tcx().def_path_str(trait_def_id); + self.tcx().dcx().emit_err(ManualImplementation { span, trait_name }); + } + } + + pub(super) fn complain_about_assoc_item_not_found( + &self, + all_candidates: impl Fn() -> I, + ty_param_name: &str, + ty_param_def_id: Option, + assoc_kind: ty::AssocKind, + assoc_name: Ident, + span: Span, + binding: Option<&hir::TypeBinding<'tcx>>, + ) -> ErrorGuaranteed + where + I: Iterator>, + { + let tcx = self.tcx(); + + // First and foremost, provide a more user-friendly & “intuitive” error on kind mismatches. + if let Some(assoc_item) = all_candidates().find_map(|r| { + tcx.associated_items(r.def_id()) + .filter_by_name_unhygienic(assoc_name.name) + .find(|item| tcx.hygienic_eq(assoc_name, item.ident(tcx), r.def_id())) + }) { + return self.complain_about_assoc_kind_mismatch( + assoc_item, assoc_kind, assoc_name, span, binding, + ); + } + + let assoc_kind_str = super::assoc_kind_str(assoc_kind); + + // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a + // valid span, so we point at the whole path segment instead. + let is_dummy = assoc_name.span == DUMMY_SP; + + let mut err = errors::AssocItemNotFound { + span: if is_dummy { span } else { assoc_name.span }, + assoc_name, + assoc_kind: assoc_kind_str, + ty_param_name, + label: None, + sugg: None, + }; + + if is_dummy { + err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span }); + return tcx.dcx().emit_err(err); + } + + let all_candidate_names: Vec<_> = all_candidates() + .flat_map(|r| tcx.associated_items(r.def_id()).in_definition_order()) + .filter_map(|item| { + (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name) + }) + .collect(); + + if let Some(suggested_name) = + find_best_match_for_name(&all_candidate_names, assoc_name.name, None) + { + err.sugg = Some(errors::AssocItemNotFoundSugg::Similar { + span: assoc_name.span, + assoc_kind: assoc_kind_str, + suggested_name, + }); + return tcx.dcx().emit_err(err); + } + + // If we didn't find a good item in the supertraits (or couldn't get + // the supertraits), like in ItemCtxt, then look more generally from + // all visible traits. If there's one clear winner, just suggest that. + + let visible_traits: Vec<_> = tcx + .all_traits() + .filter(|trait_def_id| { + let viz = tcx.visibility(*trait_def_id); + let def_id = self.item_def_id(); + viz.is_accessible_from(def_id, tcx) + }) + .collect(); + + let wider_candidate_names: Vec<_> = visible_traits + .iter() + .flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order()) + .filter_map(|item| { + (!item.is_impl_trait_in_trait() && item.kind == assoc_kind).then_some(item.name) + }) + .collect(); + + if let Some(suggested_name) = + find_best_match_for_name(&wider_candidate_names, assoc_name.name, None) + { + if let [best_trait] = visible_traits + .iter() + .copied() + .filter(|trait_def_id| { + tcx.associated_items(trait_def_id) + .filter_by_name_unhygienic(suggested_name) + .any(|item| item.kind == assoc_kind) + }) + .collect::>()[..] + { + let trait_name = tcx.def_path_str(best_trait); + err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait { + span: assoc_name.span, + assoc_kind: assoc_kind_str, + trait_name: &trait_name, + suggested_name, + identically_named: suggested_name == assoc_name.name, + }); + let hir = tcx.hir(); + if let Some(def_id) = ty_param_def_id + && let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id)) + && let Some(generics) = hir.get_generics(parent.def_id) + { + if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( + |b| match b { + hir::GenericBound::Trait(t, ..) => { + t.trait_ref.trait_def_id() == Some(best_trait) + } + _ => false, + }, + ) { + // The type param already has a bound for `trait_name`, we just need to + // change the associated item. + err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { + span: assoc_name.span, + assoc_kind: assoc_kind_str, + suggested_name, + }); + return tcx.dcx().emit_err(err); + } + + let mut err = tcx.dcx().create_err(err); + if suggest_constraining_type_param( + tcx, + generics, + &mut err, + &ty_param_name, + &trait_name, + None, + None, + ) && suggested_name != assoc_name.name + { + // We suggested constraining a type parameter, but the associated item on it + // was also not an exact match, so we also suggest changing it. + err.span_suggestion_verbose( + assoc_name.span, + fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + suggested_name, + Applicability::MaybeIncorrect, + ); + } + return err.emit(); + } + return tcx.dcx().emit_err(err); + } + } + + // If we still couldn't find any associated item, and only one associated item exists, + // suggests using it. + if let [candidate_name] = all_candidate_names.as_slice() { + // This should still compile, except on `#![feature(associated_type_defaults)]` + // where it could suggests `type A = Self::A`, thus recursing infinitely. + let applicability = + if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults { + Applicability::Unspecified + } else { + Applicability::MaybeIncorrect + }; + + err.sugg = Some(errors::AssocItemNotFoundSugg::Other { + span: assoc_name.span, + applicability, + ty_param_name, + assoc_kind: assoc_kind_str, + suggested_name: *candidate_name, + }); + } else { + err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span }); + } + + tcx.dcx().emit_err(err) + } + + fn complain_about_assoc_kind_mismatch( + &self, + assoc_item: &ty::AssocItem, + assoc_kind: ty::AssocKind, + ident: Ident, + span: Span, + binding: Option<&hir::TypeBinding<'tcx>>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + + let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind + && let Some(binding) = binding + && let hir::TypeBindingKind::Constraint { .. } = binding.kind + { + let lo = if binding.gen_args.span_ext.is_dummy() { + ident.span + } else { + binding.gen_args.span_ext + }; + Some(lo.between(span.shrink_to_hi())) + } else { + None + }; + + // FIXME(associated_const_equality): This has quite a few false positives and negatives. + let wrap_in_braces_sugg = if let Some(binding) = binding + && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(hir_ty) } = binding.kind + && let ty = self.lower_ty(hir_ty) + && (ty.is_enum() || ty.references_error()) + && tcx.features().associated_const_equality + { + Some(errors::AssocKindMismatchWrapInBracesSugg { + lo: hir_ty.span.shrink_to_lo(), + hi: hir_ty.span.shrink_to_hi(), + }) + } else { + None + }; + + // For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since + // one can argue that that's more “intuitive” to the user. + let (span, expected_because_label, expected, got) = if let Some(binding) = binding + && let hir::TypeBindingKind::Equality { term } = binding.kind + { + let span = match term { + hir::Term::Ty(ty) => ty.span, + hir::Term::Const(ct) => tcx.def_span(ct.def_id), + }; + (span, Some(ident.span), assoc_item.kind, assoc_kind) + } else { + (ident.span, None, assoc_kind, assoc_item.kind) + }; + + tcx.dcx().emit_err(errors::AssocKindMismatch { + span, + expected: super::assoc_kind_str(expected), + got: super::assoc_kind_str(got), + expected_because_label, + assoc_kind: super::assoc_kind_str(assoc_item.kind), + def_span: tcx.def_span(assoc_item.def_id), + bound_on_assoc_const_label, + wrap_in_braces_sugg, + }) + } + + pub(super) fn report_ambiguous_assoc_ty( + &self, + span: Span, + types: &[String], + traits: &[String], + name: Symbol, + ) -> ErrorGuaranteed { + let mut err = + struct_span_code_err!(self.tcx().dcx(), span, E0223, "ambiguous associated type"); + if self + .tcx() + .resolutions(()) + .confused_type_with_std_module + .keys() + .any(|full_span| full_span.contains(span)) + { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "you are looking for the module in `std`, not the primitive type", + "std::", + Applicability::MachineApplicable, + ); + } else { + let mut types = types.to_vec(); + types.sort(); + let mut traits = traits.to_vec(); + traits.sort(); + match (&types[..], &traits[..]) { + ([], []) => { + err.span_suggestion_verbose( + span, + format!( + "if there were a type named `Type` that implements a trait named \ + `Trait` with associated type `{name}`, you could use the \ + fully-qualified path", + ), + format!("::{name}"), + Applicability::HasPlaceholders, + ); + } + ([], [trait_str]) => { + err.span_suggestion_verbose( + span, + format!( + "if there were a type named `Example` that implemented `{trait_str}`, \ + you could use the fully-qualified path", + ), + format!("::{name}"), + Applicability::HasPlaceholders, + ); + } + ([], traits) => { + err.span_suggestions( + span, + format!( + "if there were a type named `Example` that implemented one of the \ + traits with associated type `{name}`, you could use the \ + fully-qualified path", + ), + traits.iter().map(|trait_str| format!("::{name}")), + Applicability::HasPlaceholders, + ); + } + ([type_str], []) => { + err.span_suggestion_verbose( + span, + format!( + "if there were a trait named `Example` with associated type `{name}` \ + implemented for `{type_str}`, you could use the fully-qualified path", + ), + format!("<{type_str} as Example>::{name}"), + Applicability::HasPlaceholders, + ); + } + (types, []) => { + err.span_suggestions( + span, + format!( + "if there were a trait named `Example` with associated type `{name}` \ + implemented for one of the types, you could use the fully-qualified \ + path", + ), + types + .into_iter() + .map(|type_str| format!("<{type_str} as Example>::{name}")), + Applicability::HasPlaceholders, + ); + } + (types, traits) => { + let mut suggestions = vec![]; + for type_str in types { + for trait_str in traits { + suggestions.push(format!("<{type_str} as {trait_str}>::{name}")); + } + } + err.span_suggestions( + span, + "use fully-qualified syntax", + suggestions, + Applicability::MachineApplicable, + ); + } + } + } + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported + } + + pub(crate) fn complain_about_ambiguous_inherent_assoc_ty( + &self, + name: Ident, + candidates: Vec, + span: Span, + ) -> ErrorGuaranteed { + let mut err = struct_span_code_err!( + self.tcx().dcx(), + name.span, + E0034, + "multiple applicable items in scope" + ); + err.span_label(name.span, format!("multiple `{name}` found")); + self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span); + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported + } + + // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. + fn note_ambiguous_inherent_assoc_ty( + &self, + err: &mut Diag<'_>, + candidates: Vec, + span: Span, + ) { + let tcx = self.tcx(); + + // Dynamic limit to avoid hiding just one candidate, which is silly. + let limit = if candidates.len() == 5 { 5 } else { 4 }; + + for (index, &item) in candidates.iter().take(limit).enumerate() { + let impl_ = tcx.impl_of_method(item).unwrap(); + + let note_span = if item.is_local() { + Some(tcx.def_span(item)) + } else if impl_.is_local() { + Some(tcx.def_span(impl_)) + } else { + None + }; + + let title = if candidates.len() > 1 { + format!("candidate #{}", index + 1) + } else { + "the candidate".into() + }; + + let impl_ty = tcx.at(span).type_of(impl_).instantiate_identity(); + let note = format!("{title} is defined in an impl for the type `{impl_ty}`"); + + if let Some(span) = note_span { + err.span_note(span, note); + } else { + err.note(note); + } + } + if candidates.len() > limit { + err.note(format!("and {} others", candidates.len() - limit)); + } + } + + // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. + pub(crate) fn complain_about_inherent_assoc_ty_not_found( + &self, + name: Ident, + self_ty: Ty<'tcx>, + candidates: Vec<(DefId, (DefId, DefId))>, + fulfillment_errors: Vec>, + span: Span, + ) -> ErrorGuaranteed { + // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`. + // Either + // * update this code by applying changes similar to #106702 or by taking a + // Vec<(DefId, (DefId, DefId), Option>>)> or + // * deduplicate this code across the two crates. + + let tcx = self.tcx(); + + let adt_did = self_ty.ty_adt_def().map(|def| def.did()); + let add_def_label = |err: &mut Diag<'_>| { + if let Some(did) = adt_did { + err.span_label( + tcx.def_span(did), + format!("associated item `{name}` not found for this {}", tcx.def_descr(did)), + ); + } + }; + + if fulfillment_errors.is_empty() { + // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. + + let limit = if candidates.len() == 5 { 5 } else { 4 }; + let type_candidates = candidates + .iter() + .take(limit) + .map(|&(impl_, _)| { + format!("- `{}`", tcx.at(span).type_of(impl_).instantiate_identity()) + }) + .collect::>() + .join("\n"); + let additional_types = if candidates.len() > limit { + format!("\nand {} more types", candidates.len() - limit) + } else { + String::new() + }; + + let mut err = struct_span_code_err!( + tcx.dcx(), + name.span, + E0220, + "associated type `{name}` not found for `{self_ty}` in the current scope" + ); + err.span_label(name.span, format!("associated item not found in `{self_ty}`")); + err.note(format!( + "the associated type was found for\n{type_candidates}{additional_types}", + )); + add_def_label(&mut err); + return err.emit(); + } + + let mut bound_spans: SortedMap> = Default::default(); + + let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { + let msg = format!("`{}`", if obligation.len() > 50 { quiet } else { obligation }); + match &self_ty.kind() { + // Point at the type that couldn't satisfy the bound. + ty::Adt(def, _) => { + bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) + } + // Point at the trait object that couldn't satisfy the bound. + ty::Dynamic(preds, _, _) => { + for pred in preds.iter() { + match pred.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => { + bound_spans + .get_mut_or_insert_default(tcx.def_span(tr.def_id)) + .push(msg.clone()); + } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => {} + } + } + } + // Point at the closure that couldn't satisfy the bound. + ty::Closure(def_id, _) => { + bound_spans + .get_mut_or_insert_default(tcx.def_span(*def_id)) + .push(format!("`{quiet}`")); + } + _ => {} + } + }; + + let format_pred = |pred: ty::Predicate<'tcx>| { + let bound_predicate = pred.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { + let pred = bound_predicate.rebind(pred); + // `::Item = String`. + let projection_ty = pred.skip_binder().projection_ty; + + let args_with_infer_self = tcx.mk_args_from_iter( + std::iter::once(Ty::new_var(tcx, ty::TyVid::ZERO).into()) + .chain(projection_ty.args.iter().skip(1)), + ); + + let quiet_projection_ty = + ty::AliasTy::new(tcx, projection_ty.def_id, args_with_infer_self); + + let term = pred.skip_binder().term; + + let obligation = format!("{projection_ty} = {term}"); + let quiet = format!("{quiet_projection_ty} = {term}"); + + bound_span_label(projection_ty.self_ty(), &obligation, &quiet); + Some((obligation, projection_ty.self_ty())) + } + ty::PredicateKind::Clause(ty::ClauseKind::Trait(poly_trait_ref)) => { + let p = poly_trait_ref.trait_ref; + let self_ty = p.self_ty(); + let path = p.print_only_trait_path(); + let obligation = format!("{self_ty}: {path}"); + let quiet = format!("_: {path}"); + bound_span_label(self_ty, &obligation, &quiet); + Some((obligation, self_ty)) + } + _ => None, + } + }; + + // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds. + // I would do the same here if it didn't mean more code duplication. + let mut bounds: Vec<_> = fulfillment_errors + .into_iter() + .map(|error| error.root_obligation.predicate) + .filter_map(format_pred) + .map(|(p, _)| format!("`{p}`")) + .collect(); + bounds.sort(); + bounds.dedup(); + + let mut err = tcx.dcx().struct_span_err( + name.span, + format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") + ); + if !bounds.is_empty() { + err.note(format!( + "the following trait bounds were not satisfied:\n{}", + bounds.join("\n") + )); + } + err.span_label( + name.span, + format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") + ); + + for (span, mut bounds) in bound_spans { + if !tcx.sess.source_map().is_span_accessible(span) { + continue; + } + bounds.sort(); + bounds.dedup(); + let msg = match &bounds[..] { + [bound] => format!("doesn't satisfy {bound}"), + bounds if bounds.len() > 4 => format!("doesn't satisfy {} bounds", bounds.len()), + [bounds @ .., last] => format!("doesn't satisfy {} or {last}", bounds.join(", ")), + [] => unreachable!(), + }; + err.span_label(span, msg); + } + add_def_label(&mut err); + err.emit() + } + + /// When there are any missing associated types, emit an E0191 error and attempt to supply a + /// reasonable suggestion on how to write it. For the case of multiple associated types in the + /// same trait bound have the same name (as they come from different supertraits), we instead + /// emit a generic note suggesting using a `where` clause to constraint instead. + pub(crate) fn complain_about_missing_assoc_tys( + &self, + associated_types: FxIndexMap>, + potential_assoc_types: Vec, + trait_bounds: &[hir::PolyTraitRef<'_>], + ) { + if associated_types.values().all(|v| v.is_empty()) { + return; + } + + let tcx = self.tcx(); + // FIXME: Marked `mut` so that we can replace the spans further below with a more + // appropriate one, but this should be handled earlier in the span assignment. + let mut associated_types: FxIndexMap> = associated_types + .into_iter() + .map(|(span, def_ids)| { + (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect()) + }) + .collect(); + let mut names: FxIndexMap> = Default::default(); + let mut names_len = 0; + + // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and + // `issue-22560.rs`. + let mut trait_bound_spans: Vec = vec![]; + let mut object_safety_violations = false; + for (span, items) in &associated_types { + if !items.is_empty() { + trait_bound_spans.push(*span); + } + for assoc_item in items { + let trait_def_id = assoc_item.container_id(tcx); + names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name); + names_len += 1; + + let violations = + object_safety_violations_for_assoc_item(tcx, trait_def_id, *assoc_item); + if !violations.is_empty() { + report_object_safety_error(tcx, *span, None, trait_def_id, &violations).emit(); + object_safety_violations = true; + } + } + } + if object_safety_violations { + return; + } + if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { + match bound.trait_ref.path.segments { + // FIXME: `trait_ref.path.span` can point to a full path with multiple + // segments, even though `trait_ref.path.segments` is of length `1`. Work + // around that bug here, even though it should be fixed elsewhere. + // This would otherwise cause an invalid suggestion. For an example, look at + // `tests/ui/issues/issue-28344.rs` where instead of the following: + // + // error[E0191]: the value of the associated type `Output` + // (from trait `std::ops::BitXor`) must be specified + // --> $DIR/issue-28344.rs:4:17 + // | + // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); + // | ^^^^^^ help: specify the associated type: + // | `BitXor` + // + // we would output: + // + // error[E0191]: the value of the associated type `Output` + // (from trait `std::ops::BitXor`) must be specified + // --> $DIR/issue-28344.rs:4:17 + // | + // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); + // | ^^^^^^^^^^^^^ help: specify the associated type: + // | `BitXor::bitor` + [segment] if segment.args.is_none() => { + trait_bound_spans = vec![segment.ident.span]; + associated_types = associated_types + .into_values() + .map(|items| (segment.ident.span, items)) + .collect(); + } + _ => {} + } + } + + // We get all the associated items that _are_ set, + // so that we can check if any of their names match one of the ones we are missing. + // This would mean that they are shadowing the associated type we are missing, + // and we can then use their span to indicate this to the user. + let bound_names = trait_bounds + .iter() + .filter_map(|poly_trait_ref| { + let path = poly_trait_ref.trait_ref.path.segments.last()?; + let args = path.args?; + + Some(args.bindings.iter().filter_map(|binding| { + let ident = binding.ident; + let trait_def = path.res.def_id(); + let assoc_item = tcx.associated_items(trait_def).find_by_name_and_kind( + tcx, + ident, + ty::AssocKind::Type, + trait_def, + ); + + Some((ident.name, assoc_item?)) + })) + }) + .flatten() + .collect::>(); + + let mut names = names + .into_iter() + .map(|(trait_, mut assocs)| { + assocs.sort(); + format!( + "{} in `{trait_}`", + match &assocs[..] { + [] => String::new(), + [only] => format!("`{only}`"), + [assocs @ .., last] => format!( + "{} and `{last}`", + assocs.iter().map(|a| format!("`{a}`")).collect::>().join(", ") + ), + } + ) + }) + .collect::>(); + names.sort(); + let names = names.join(", "); + + trait_bound_spans.sort(); + let mut err = struct_span_code_err!( + tcx.dcx(), + trait_bound_spans, + E0191, + "the value of the associated type{} {} must be specified", + pluralize!(names_len), + names, + ); + let mut suggestions = vec![]; + let mut types_count = 0; + let mut where_constraints = vec![]; + let mut already_has_generics_args_suggestion = false; + for (span, assoc_items) in &associated_types { + let mut names: UnordMap<_, usize> = Default::default(); + for item in assoc_items { + types_count += 1; + *names.entry(item.name).or_insert(0) += 1; + } + let mut dupes = false; + let mut shadows = false; + for item in assoc_items { + let prefix = if names[&item.name] > 1 { + let trait_def_id = item.container_id(tcx); + dupes = true; + format!("{}::", tcx.def_path_str(trait_def_id)) + } else if bound_names.get(&item.name).is_some_and(|x| x != &item) { + let trait_def_id = item.container_id(tcx); + shadows = true; + format!("{}::", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; + + let mut is_shadowed = false; + + if let Some(assoc_item) = bound_names.get(&item.name) + && assoc_item != &item + { + is_shadowed = true; + + let rename_message = + if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" }; + err.span_label( + tcx.def_span(assoc_item.def_id), + format!("`{}{}` shadowed here{}", prefix, item.name, rename_message), + ); + } + + let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; + + if let Some(sp) = tcx.hir().span_if_local(item.def_id) { + err.span_label( + sp, + format!("`{}{}` defined here{}", prefix, item.name, rename_message), + ); + } + } + if potential_assoc_types.len() == assoc_items.len() { + // When the amount of missing associated types equals the number of + // extra type arguments present. A suggesting to replace the generic args with + // associated types is already emitted. + already_has_generics_args_suggestion = true; + } else if let (Ok(snippet), false, false) = + (tcx.sess.source_map().span_to_snippet(*span), dupes, shadows) + { + let types: Vec<_> = + assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect(); + let code = if snippet.ends_with('>') { + // The user wrote `Trait<'a>` or similar and we don't have a type we can + // suggest, but at least we can clue them to the correct syntax + // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the + // suggestion. + format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", ")) + } else { + // The user wrote `Iterator`, so we don't have a type we can suggest, but at + // least we can clue them to the correct syntax `Iterator`. + format!("{}<{}>", snippet, types.join(", ")) + }; + suggestions.push((*span, code)); + } else if dupes { + where_constraints.push(*span); + } + } + let where_msg = "consider introducing a new type parameter, adding `where` constraints \ + using the fully-qualified path to the associated types"; + if !where_constraints.is_empty() && suggestions.is_empty() { + // If there are duplicates associated type names and a single trait bound do not + // use structured suggestion, it means that there are multiple supertraits with + // the same associated type name. + err.help(where_msg); + } + if suggestions.len() != 1 || already_has_generics_args_suggestion { + // We don't need this label if there's an inline suggestion, show otherwise. + for (span, assoc_items) in &associated_types { + let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); + for item in assoc_items { + types_count += 1; + *names.entry(item.name).or_insert(0) += 1; + } + let mut label = vec![]; + for item in assoc_items { + let postfix = if names[&item.name] > 1 { + let trait_def_id = item.container_id(tcx); + format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; + label.push(format!("`{}`{}", item.name, postfix)); + } + if !label.is_empty() { + err.span_label( + *span, + format!( + "associated type{} {} must be specified", + pluralize!(label.len()), + label.join(", "), + ), + ); + } + } + } + suggestions.sort_by_key(|&(span, _)| span); + // There are cases where one bound points to a span within another bound's span, like when + // you have code like the following (#115019), so we skip providing a suggestion in those + // cases to avoid having a malformed suggestion. + // + // pub struct Flatten { + // inner: >::IntoIterator as Item>::core, + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // | ^^^^^^^^^^^^^^^^^^^^^ + // | | + // | associated types `Item`, `IntoIter` must be specified + // associated types `Item`, `IntoIter` must be specified + // } + let overlaps = suggestions.windows(2).any(|pair| pair[0].0.overlaps(pair[1].0)); + if !suggestions.is_empty() && !overlaps { + err.multipart_suggestion( + format!("specify the associated type{}", pluralize!(types_count)), + suggestions, + Applicability::HasPlaceholders, + ); + if !where_constraints.is_empty() { + err.span_help(where_constraints, where_msg); + } + } + + self.set_tainted_by_errors(err.emit()); + } + + /// On ambiguous associated type, look for an associated function whose name matches the + /// extended path and, if found, emit an E0223 error with a structured suggestion. + /// e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195) + pub(crate) fn maybe_report_similar_assoc_fn( + &self, + span: Span, + qself_ty: Ty<'tcx>, + qself: &hir::Ty<'_>, + ) -> Result<(), ErrorGuaranteed> { + let tcx = self.tcx(); + if let Some((_, node)) = tcx.hir().parent_iter(qself.hir_id).skip(1).next() + && let hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::Path(hir::QPath::TypeRelative( + hir::Ty { + kind: + hir::TyKind::Path(hir::QPath::TypeRelative( + _, + hir::PathSegment { ident: ident2, .. }, + )), + .. + }, + hir::PathSegment { ident: ident3, .. }, + )), + .. + }) = node + && let Some(ty_def_id) = qself_ty.ty_def_id() + && let Ok([inherent_impl]) = tcx.inherent_impls(ty_def_id) + && let name = format!("{ident2}_{ident3}") + && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx + .associated_items(inherent_impl) + .filter_by_name_unhygienic(Symbol::intern(&name)) + .next() + { + let reported = + struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type") + .with_span_suggestion_verbose( + ident2.span.to(ident3.span), + format!("there is an associated function with a similar name: `{name}`"), + name, + Applicability::MaybeIncorrect, + ) + .emit(); + self.set_tainted_by_errors(reported); + Err(reported) + } else { + Ok(()) + } + } + + pub fn report_prohibit_generics_error<'a>( + &self, + segments: impl Iterator> + Clone, + args_visitors: impl Iterator> + Clone, + err_extend: GenericsArgsErrExtend<'_>, + ) -> ErrorGuaranteed { + #[derive(PartialEq, Eq, Hash)] + enum ProhibitGenericsArg { + Lifetime, + Type, + Const, + Infer, + } + + let mut prohibit_args = FxIndexSet::default(); + args_visitors.for_each(|arg| { + match arg { + hir::GenericArg::Lifetime(_) => prohibit_args.insert(ProhibitGenericsArg::Lifetime), + hir::GenericArg::Type(_) => prohibit_args.insert(ProhibitGenericsArg::Type), + hir::GenericArg::Const(_) => prohibit_args.insert(ProhibitGenericsArg::Const), + hir::GenericArg::Infer(_) => prohibit_args.insert(ProhibitGenericsArg::Infer), + }; + }); + + let types_and_spans: Vec<_> = segments + .clone() + .flat_map(|segment| { + if segment.args().args.is_empty() { + None + } else { + Some(( + match segment.res { + hir::def::Res::PrimTy(ty) => { + format!("{} `{}`", segment.res.descr(), ty.name()) + } + hir::def::Res::Def(_, def_id) + if let Some(name) = self.tcx().opt_item_name(def_id) => + { + format!("{} `{name}`", segment.res.descr()) + } + hir::def::Res::Err => "this type".to_string(), + _ => segment.res.descr().to_string(), + }, + segment.ident.span, + )) + } + }) + .collect(); + let this_type = match &types_and_spans[..] { + [.., _, (last, _)] => format!( + "{} and {last}", + types_and_spans[..types_and_spans.len() - 1] + .iter() + .map(|(x, _)| x.as_str()) + .intersperse(", ") + .collect::() + ), + [(only, _)] => only.to_string(), + [] => "this type".to_string(), + }; + + let arg_spans: Vec = segments + .clone() + .flat_map(|segment| segment.args().args) + .map(|arg| arg.span()) + .collect(); + + let mut kinds = Vec::with_capacity(4); + prohibit_args.iter().for_each(|arg| match arg { + ProhibitGenericsArg::Lifetime => kinds.push("lifetime"), + ProhibitGenericsArg::Type => kinds.push("type"), + ProhibitGenericsArg::Const => kinds.push("const"), + ProhibitGenericsArg::Infer => kinds.push("generic"), + }); + + let (kind, s) = match kinds[..] { + [.., _, last] => ( + format!( + "{} and {last}", + kinds[..kinds.len() - 1] + .iter() + .map(|&x| x) + .intersperse(", ") + .collect::() + ), + "s", + ), + [only] => (only.to_string(), ""), + [] => unreachable!("expected at least one generic to prohibit"), + }; + let last_span = *arg_spans.last().unwrap(); + let span: MultiSpan = arg_spans.into(); + let mut err = struct_span_code_err!( + self.tcx().dcx(), + span, + E0109, + "{kind} arguments are not allowed on {this_type}", + ); + err.span_label(last_span, format!("{kind} argument{s} not allowed")); + for (what, span) in types_and_spans { + err.span_label(span, format!("not allowed on {what}")); + } + generics_args_err_extend(self.tcx(), segments, &mut err, err_extend); + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported + } + + pub fn report_trait_object_addition_traits_error( + &self, + regular_traits: &Vec>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + let first_trait = ®ular_traits[0]; + let additional_trait = ®ular_traits[1]; + let mut err = struct_span_code_err!( + tcx.dcx(), + additional_trait.bottom().1, + E0225, + "only auto traits can be used as additional traits in a trait object" + ); + additional_trait.label_with_exp_info( + &mut err, + "additional non-auto trait", + "additional use", + ); + first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); + err.help(format!( + "consider creating a new trait with all of these as supertraits and using that \ + trait here instead: `trait NewTrait: {} {{}}`", + regular_traits + .iter() + // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... + .map(|t| t.trait_ref().print_only_trait_path().to_string()) + .collect::>() + .join(" + "), + )); + err.note( + "auto-traits like `Send` and `Sync` are traits that have special properties; \ + for more information on them, visit \ + ", + ); + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported + } + + pub fn report_trait_object_with_no_traits_error( + &self, + span: Span, + trait_bounds: &Vec<(Binder<'tcx, TraitRef<'tcx>>, Span)>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + let trait_alias_span = trait_bounds + .iter() + .map(|&(trait_ref, _)| trait_ref.def_id()) + .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) + .map(|trait_ref| tcx.def_span(trait_ref)); + let reported = + tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); + self.set_tainted_by_errors(reported); + reported + } +} + +/// Emits an error regarding forbidden type binding associations +pub fn prohibit_assoc_item_binding( + tcx: TyCtxt<'_>, + binding: &hir::TypeBinding<'_>, + segment: Option<(DefId, &hir::PathSegment<'_>, Span)>, +) -> ErrorGuaranteed { + let mut err = tcx.dcx().create_err(AssocTypeBindingNotAllowed { + span: binding.span, + fn_trait_expansion: if let Some((_, segment, span)) = segment + && segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar + { + Some(ParenthesizedFnTraitExpansion { + span, + expanded_type: fn_trait_to_string(tcx, segment, false), + }) + } else { + None + }, + }); + + // Emit a suggestion to turn the assoc item binding into a generic arg + // if the relevant item has a generic param whose name matches the binding name; + // otherwise suggest the removal of the binding. + if let Some((def_id, segment, _)) = segment + && segment.args().parenthesized == hir::GenericArgsParentheses::No + && let hir::TypeBindingKind::Equality { term } = binding.kind + { + // Suggests removal of the offending binding + let suggest_removal = |e: &mut Diag<'_>| { + let bindings = segment.args().bindings; + let args = segment.args().args; + let binding_span = binding.span; + + // Compute the span to remove based on the position + // of the binding. We do that as follows: + // 1. Find the index of the binding in the list of bindings + // 2. Locate the spans preceding and following the binding. + // If it's the first binding the preceding span would be + // that of the last arg + // 3. Using this information work out whether the span + // to remove will start from the end of the preceding span, + // the start of the next span or will simply be the + // span encomassing everything within the generics brackets + + let Some(binding_index) = bindings.iter().position(|b| b.hir_id == binding.hir_id) + else { + bug!("a type binding exists but its HIR ID not found in generics"); + }; + + let preceding_span = if binding_index > 0 { + Some(bindings[binding_index - 1].span) + } else { + args.last().map(|a| a.span()) + }; + + let next_span = if binding_index < bindings.len() - 1 { + Some(bindings[binding_index + 1].span) + } else { + None + }; + + let removal_span = match (preceding_span, next_span) { + (Some(prec), _) => binding_span.with_lo(prec.hi()), + (None, Some(next)) => binding_span.with_hi(next.lo()), + (None, None) => { + let Some(generics_span) = segment.args().span_ext() else { + bug!("a type binding exists but generic span is empty"); + }; + + generics_span + } + }; + + // Now emit the suggestion + if let Ok(suggestion) = tcx.sess.source_map().span_to_snippet(removal_span) { + e.span_suggestion_verbose( + removal_span, + "consider removing this type binding", + suggestion, + Applicability::MaybeIncorrect, + ); + } + }; + + // Suggest replacing the associated item binding with a generic argument. + // i.e., replacing `<..., T = A, ...>` with `<..., A, ...>`. + let suggest_direct_use = |e: &mut Diag<'_>, sp: Span| { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sp) { + e.span_suggestion_verbose( + binding.span, + format!("to use `{snippet}` as a generic argument specify it directly"), + snippet, + Applicability::MaybeIncorrect, + ); + } + }; + + // Check if the type has a generic param with the + // same name as the assoc type name in type binding + let generics = tcx.generics_of(def_id); + let matching_param = + generics.params.iter().find(|p| p.name.as_str() == binding.ident.as_str()); + + // Now emit the appropriate suggestion + if let Some(matching_param) = matching_param { + match (&matching_param.kind, term) { + (GenericParamDefKind::Type { .. }, hir::Term::Ty(ty)) => { + suggest_direct_use(&mut err, ty.span); + } + (GenericParamDefKind::Const { .. }, hir::Term::Const(c)) => { + let span = tcx.hir().span(c.hir_id); + suggest_direct_use(&mut err, span); + } + _ => suggest_removal(&mut err), + } + } else { + suggest_removal(&mut err); + } + } + + err.emit() +} + +pub(crate) fn fn_trait_to_string( + tcx: TyCtxt<'_>, + trait_segment: &hir::PathSegment<'_>, + parenthesized: bool, +) -> String { + let args = trait_segment + .args + .as_ref() + .and_then(|args| args.args.get(0)) + .and_then(|arg| match arg { + hir::GenericArg::Type(ty) => match ty.kind { + hir::TyKind::Tup(t) => t + .iter() + .map(|e| tcx.sess.source_map().span_to_snippet(e.span)) + .collect::, _>>() + .map(|a| a.join(", ")), + _ => tcx.sess.source_map().span_to_snippet(ty.span), + } + .map(|s| { + // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma + if parenthesized || s.is_empty() { format!("({s})") } else { format!("({s},)") } + }) + .ok(), + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + + let ret = trait_segment + .args() + .bindings + .iter() + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { + (true, hir::TypeBindingKind::Equality { term }) => { + let span = match term { + hir::Term::Ty(ty) => ty.span, + hir::Term::Const(c) => tcx.hir().span(c.hir_id), + }; + + (span != tcx.hir().span(trait_segment.hir_id)) + .then_some(tcx.sess.source_map().span_to_snippet(span).ok()) + .flatten() + } + _ => None, + }) + .unwrap_or_else(|| "()".to_string()); + + if parenthesized { + format!("{}{} -> {}", trait_segment.ident, args, ret) + } else { + format!("{}<{}, Output={}>", trait_segment.ident, args, ret) + } +} + +/// Used for generics args error extend. +pub enum GenericsArgsErrExtend<'tcx> { + EnumVariant { + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, + adt_def: AdtDef<'tcx>, + }, + OpaqueTy, + PrimTy(hir::PrimTy), + SelfTyAlias { + def_id: DefId, + span: Span, + }, + SelfTyParam(Span), + TyParam(DefId), + DefVariant, + None, +} + +fn generics_args_err_extend<'a>( + tcx: TyCtxt<'_>, + segments: impl Iterator> + Clone, + err: &mut Diag<'_>, + err_extend: GenericsArgsErrExtend<'_>, +) { + match err_extend { + GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def } => { + err.note("enum variants can't have type parameters"); + let type_name = tcx.item_name(adt_def.did()); + let msg = format!( + "you might have meant to specify type parameters on enum \ + `{type_name}`" + ); + let Some(args) = assoc_segment.args else { + return; + }; + // Get the span of the generics args *including* the leading `::`. + // We do so by stretching args.span_ext to the left by 2. Earlier + // it was done based on the end of assoc segment but that sometimes + // led to impossible spans and caused issues like #116473 + let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); + if tcx.generics_of(adt_def.did()).count() == 0 { + // FIXME(estebank): we could also verify that the arguments being + // work for the `enum`, instead of just looking if it takes *any*. + err.span_suggestion_verbose( + args_span, + format!("{type_name} doesn't have generic parameters"), + "", + Applicability::MachineApplicable, + ); + return; + } + let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else { + err.note(msg); + return; + }; + let (qself_sugg_span, is_self) = + if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { + // If the path segment already has type params, we want to overwrite + // them. + match &path.segments { + // `segment` is the previous to last element on the path, + // which would normally be the `enum` itself, while the last + // `_` `PathSegment` corresponds to the variant. + [ + .., + hir::PathSegment { + ident, args, res: Res::Def(DefKind::Enum, _), .. + }, + _, + ] => ( + // We need to include the `::` in `Type::Variant::` + // to point the span to `::`, not just ``. + ident + .span + .shrink_to_hi() + .to(args.map_or(ident.span.shrink_to_hi(), |a| a.span_ext)), + false, + ), + [segment] => { + ( + // We need to include the `::` in `Type::Variant::` + // to point the span to `::`, not just ``. + segment.ident.span.shrink_to_hi().to(segment + .args + .map_or(segment.ident.span.shrink_to_hi(), |a| a.span_ext)), + kw::SelfUpper == segment.ident.name, + ) + } + _ => { + err.note(msg); + return; + } + } + } else { + err.note(msg); + return; + }; + let suggestion = vec![ + if is_self { + // Account for people writing `Self::Variant::`, where + // `Self` is the enum, and suggest replacing `Self` with the + // appropriate type: `Type::::Variant`. + (qself.span, format!("{type_name}{snippet}")) + } else { + (qself_sugg_span, snippet) + }, + (args_span, String::new()), + ]; + err.multipart_suggestion_verbose(msg, suggestion, Applicability::MaybeIncorrect); + } + GenericsArgsErrExtend::PrimTy(prim_ty) => { + let name = prim_ty.name_str(); + for segment in segments { + if let Some(args) = segment.args { + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi().to(args.span_ext), + format!("primitive type `{name}` doesn't have generic parameters"), + "", + Applicability::MaybeIncorrect, + ); + } + } + } + GenericsArgsErrExtend::OpaqueTy => { + err.note("`impl Trait` types can't have type parameters"); + } + GenericsArgsErrExtend::DefVariant => { + err.note("enum variants can't have type parameters"); + } + GenericsArgsErrExtend::TyParam(def_id) => { + if let Some(span) = tcx.def_ident_span(def_id) { + let name = tcx.item_name(def_id); + err.span_note(span, format!("type parameter `{name}` defined here")); + } + } + GenericsArgsErrExtend::SelfTyParam(span) => { + err.span_suggestion_verbose( + span, + "the `Self` type doesn't accept type parameters", + "", + Applicability::MaybeIncorrect, + ); + } + GenericsArgsErrExtend::SelfTyAlias { def_id, span } => { + let ty = tcx.at(span).type_of(def_id).instantiate_identity(); + let span_of_impl = tcx.span_of_impl(def_id); + let def_id = match *ty.kind() { + ty::Adt(self_def, _) => self_def.did(), + _ => return, + }; + + let type_name = tcx.item_name(def_id); + let span_of_ty = tcx.def_ident_span(def_id); + let generics = tcx.generics_of(def_id).count(); + + let msg = format!("`Self` is of type `{ty}`"); + if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { + let mut span: MultiSpan = vec![t_sp].into(); + span.push_span_label( + i_sp, + format!("`Self` is on type `{type_name}` in this `impl`"), + ); + let mut postfix = ""; + if generics == 0 { + postfix = ", which doesn't have generic parameters"; + } + span.push_span_label(t_sp, format!("`Self` corresponds to this type{postfix}")); + err.span_note(span, msg); + } else { + err.note(msg); + } + for segment in segments { + if let Some(args) = segment.args + && segment.ident.name == kw::SelfUpper + { + if generics == 0 { + // FIXME(estebank): we could also verify that the arguments being + // work for the `enum`, instead of just looking if it takes *any*. + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi().to(args.span_ext), + "the `Self` type doesn't accept type parameters", + "", + Applicability::MachineApplicable, + ); + return; + } else { + err.span_suggestion_verbose( + segment.ident.span, + format!( + "the `Self` type doesn't accept type parameters, use the \ + concrete type's name `{type_name}` instead if you want to \ + specify its type parameters" + ), + type_name, + Applicability::MaybeIncorrect, + ); + } + } + } + } + _ => {} + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs new file mode 100644 index 0000000000000..e6a7dfa759c8e --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -0,0 +1,658 @@ +use super::IsMethodCall; +use crate::hir_ty_lowering::{ + errors::prohibit_assoc_item_binding, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, GenericArgPosition, GenericArgsLowerer, +}; +use crate::structured_errors::{GenericArgsInfo, StructuredDiag, WrongNumberOfGenericArgs}; +use rustc_ast::ast::ParamKindOrd; +use rustc_errors::{ + codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, +}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::GenericArg; +use rustc_middle::ty::{ + self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt, +}; +use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; +use rustc_span::symbol::{kw, sym}; +use smallvec::SmallVec; + +/// Report an error that a generic argument did not match the generic parameter that was +/// expected. +fn generic_arg_mismatch_err( + tcx: TyCtxt<'_>, + arg: &GenericArg<'_>, + param: &GenericParamDef, + possible_ordering_error: bool, + help: Option, +) -> ErrorGuaranteed { + let sess = tcx.sess; + let mut err = struct_span_code_err!( + tcx.dcx(), + arg.span(), + E0747, + "{} provided when a {} was expected", + arg.descr(), + param.kind.descr(), + ); + + if let GenericParamDefKind::Const { .. } = param.kind { + if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { + err.help("const arguments cannot yet be inferred with `_`"); + tcx.disabled_nightly_features( + &mut err, + param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)), + [(String::new(), sym::generic_arg_infer)], + ); + } + } + + let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diag<'_>| { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + err.multipart_suggestion( + "if this generic argument was intended as a const parameter, \ + surround it with braces", + suggestions, + Applicability::MaybeIncorrect, + ); + }; + + // Specific suggestion set for diagnostics + match (arg, ¶m.kind) { + ( + GenericArg::Type(hir::Ty { + kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), + .. + }), + GenericParamDefKind::Const { .. }, + ) => match path.res { + Res::Err => { + add_braces_suggestion(arg, &mut err); + return err + .with_primary_message("unresolved item provided when a constant was expected") + .emit(); + } + Res::Def(DefKind::TyParam, src_def_id) => { + if let Some(param_local_id) = param.def_id.as_local() { + let param_name = tcx.hir().ty_param_name(param_local_id); + let param_type = tcx.type_of(param.def_id).instantiate_identity(); + if param_type.is_suggestable(tcx, false) { + err.span_suggestion( + tcx.def_span(src_def_id), + "consider changing this type parameter to a const parameter", + format!("const {param_name}: {param_type}"), + Applicability::MaybeIncorrect, + ); + }; + } + } + _ => add_braces_suggestion(arg, &mut err), + }, + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), + GenericParamDefKind::Const { .. }, + ) => add_braces_suggestion(arg, &mut err), + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), + GenericParamDefKind::Const { .. }, + ) if tcx.type_of(param.def_id).skip_binder() == tcx.types.usize => { + let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); + if let Ok(snippet) = snippet { + err.span_suggestion( + arg.span(), + "array type provided where a `usize` was expected, try", + format!("{{ {snippet} }}"), + Applicability::MaybeIncorrect, + ); + } + } + (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { + let body = tcx.hir().body(cnst.value.body); + if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body.value.kind + { + if let Res::Def(DefKind::Fn { .. }, id) = path.res { + err.help(format!("`{}` is a function item, not a type", tcx.item_name(id))); + err.help("function item types cannot be named directly"); + } + } + } + _ => {} + } + + let kind_ord = param.kind.to_ord(); + let arg_ord = arg.to_ord(); + + // This note is only true when generic parameters are strictly ordered by their kind. + if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { + let (first, last) = if kind_ord < arg_ord { + (param.kind.descr(), arg.descr()) + } else { + (arg.descr(), param.kind.descr()) + }; + err.note(format!("{first} arguments must be provided before {last} arguments")); + if let Some(help) = help { + err.help(help); + } + } + + err.emit() +} + +/// Lower generic arguments from the HIR to the [`rustc_middle::ty`] representation. +/// +/// This is a rather complex function. Let us try to explain the role +/// of each of its parameters: +/// +/// To start, we are given the `def_id` of the thing whose generic parameters we +/// are creating, and a partial set of arguments `parent_args`. In general, +/// the generic arguments for an item begin with arguments for all the "parents" +/// of that item -- e.g., for a method it might include the parameters from the impl. +/// +/// Therefore, the method begins by walking down these parents, +/// starting with the outermost parent and proceed inwards until +/// it reaches `def_id`. For each parent `P`, it will check `parent_args` +/// first to see if the parent's arguments are listed in there. If so, +/// we can append those and move on. Otherwise, it uses the provided +/// [`GenericArgsLowerer`] `ctx` which has the following methods: +/// +/// - `args_for_def_id`: given the `DefId` `P`, supplies back the +/// generic arguments that were given to that parent from within +/// the path; so e.g., if you have `::Bar`, the `DefId` +/// might refer to the trait `Foo`, and the arguments might be +/// `[T]`. The boolean value indicates whether to infer values +/// for arguments whose values were not explicitly provided. +/// - `provided_kind`: given the generic parameter and the value +/// from `args_for_def_id`, creating a `GenericArg`. +/// - `inferred_kind`: if no parameter was provided, and inference +/// is enabled, then creates a suitable inference variable. +pub fn lower_generic_args<'tcx: 'a, 'a>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + parent_args: &[ty::GenericArg<'tcx>], + has_self: bool, + self_ty: Option>, + arg_count: &GenericArgCountResult, + ctx: &mut impl GenericArgsLowerer<'a, 'tcx>, +) -> GenericArgsRef<'tcx> { + // Collect the segments of the path; we need to instantiate arguments + // for parameters throughout the entire path (wherever there are + // generic parameters). + let mut parent_defs = tcx.generics_of(def_id); + let count = parent_defs.count(); + let mut stack = vec![(def_id, parent_defs)]; + while let Some(def_id) = parent_defs.parent { + parent_defs = tcx.generics_of(def_id); + stack.push((def_id, parent_defs)); + } + + // We manually build up the generic arguments, rather than using convenience + // methods in `rustc_middle/src/ty/generic_args.rs`, so that we can iterate over the arguments and + // parameters in lock-step linearly, instead of trying to match each pair. + let mut args: SmallVec<[ty::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); + // Iterate over each segment of the path. + while let Some((def_id, defs)) = stack.pop() { + let mut params = defs.params.iter().peekable(); + + // If we have already computed the generic arguments for parents, + // we can use those directly. + while let Some(¶m) = params.peek() { + if let Some(&kind) = parent_args.get(param.index as usize) { + args.push(kind); + params.next(); + } else { + break; + } + } + + // `Self` is handled first, unless it's been handled in `parent_args`. + if has_self { + if let Some(¶m) = params.peek() { + if param.index == 0 { + if let GenericParamDefKind::Type { .. } = param.kind { + args.push( + self_ty + .map(|ty| ty.into()) + .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), + ); + params.next(); + } + } + } + } + + // Check whether this segment takes generic arguments and the user has provided any. + let (generic_args, infer_args) = ctx.args_for_def_id(def_id); + + let mut args_iter = + generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args_iter.peek(), params.peek()) { + (Some(&arg), Some(¶m)) => { + match (arg, ¶m.kind, arg_count.explicit_late_bound) { + ( + GenericArg::Const(hir::ConstArg { + is_desugared_from_effects: true, + .. + }), + GenericParamDefKind::Const { is_host_effect: false, .. } + | GenericParamDefKind::Type { .. } + | GenericParamDefKind::Lifetime, + _, + ) => { + // SPECIAL CASE FOR DESUGARED EFFECT PARAMS + // This comes from the following example: + // + // ``` + // #[const_trait] + // pub trait PartialEq {} + // impl const PartialEq for () {} + // ``` + // + // Since this is a const impl, we need to insert a host arg at the end of + // `PartialEq`'s generics, but this errors since `Rhs` isn't specified. + // To work around this, we infer all arguments until we reach the host param. + args.push(ctx.inferred_kind(Some(&args), param, infer_args)); + params.next(); + } + (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) + | ( + GenericArg::Type(_) | GenericArg::Infer(_), + GenericParamDefKind::Type { .. }, + _, + ) + | ( + GenericArg::Const(_) | GenericArg::Infer(_), + GenericParamDefKind::Const { .. }, + _, + ) => { + args.push(ctx.provided_kind(param, arg)); + args_iter.next(); + params.next(); + } + ( + GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDefKind::Lifetime, + _, + ) => { + // We expected a lifetime argument, but got a type or const + // argument. That means we're inferring the lifetimes. + args.push(ctx.inferred_kind(None, param, infer_args)); + force_infer_lt = Some((arg, param)); + params.next(); + } + (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { + // We've come across a lifetime when we expected something else in + // the presence of explicit late bounds. This is most likely + // due to the presence of the explicit bound so we're just going to + // ignore it. + args_iter.next(); + } + (_, _, _) => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if arg_count.correct.is_ok() { + // We're going to iterate over the parameters to sort them out, and + // show that order to the user as a possible order for the parameters + let mut param_types_present = defs + .params + .iter() + .map(|param| (param.kind.to_ord(), param.clone())) + .collect::>(); + param_types_present.sort_by_key(|(ord, _)| *ord); + let (mut param_types_present, ordered_params): ( + Vec, + Vec, + ) = param_types_present.into_iter().unzip(); + param_types_present.dedup(); + + generic_arg_mismatch_err( + tcx, + arg, + param, + !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()), + Some(format!( + "reorder the arguments: {}: `<{}>`", + param_types_present + .into_iter() + .map(|ord| format!("{ord}s")) + .collect::>() + .join(", then "), + ordered_params + .into_iter() + .filter_map(|param| { + if param.name == kw::SelfUpper { + None + } else { + Some(param.name.to_string()) + } + }) + .collect::>() + .join(", ") + )), + ); + } + + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args_iter.next().is_some() {} + } + } + } + + (Some(&arg), None) => { + // We should never be able to reach this point with well-formed input. + // There are three situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. There are late-bound lifetime parameters present, yet the + // lifetime arguments have also been explicitly specified by the + // user. + // 3. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + + if arg_count.correct.is_ok() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { + let kind = arg.descr(); + assert_eq!(kind, "lifetime"); + let (provided_arg, param) = + force_infer_lt.expect("lifetimes ought to have been inferred"); + generic_arg_mismatch_err(tcx, provided_arg, param, false, None); + } + + break; + } + + (None, Some(¶m)) => { + // If there are fewer arguments than parameters, it means + // we're inferring the remaining arguments. + args.push(ctx.inferred_kind(Some(&args), param, infer_args)); + params.next(); + } + + (None, None) => break, + } + } + } + + tcx.mk_args(&args) +} + +/// Checks that the correct number of generic arguments have been provided. +/// Used specifically for function calls. +pub fn check_generic_arg_count_for_call( + tcx: TyCtxt<'_>, + def_id: DefId, + generics: &ty::Generics, + seg: &hir::PathSegment<'_>, + is_method_call: IsMethodCall, +) -> GenericArgCountResult { + let gen_pos = match is_method_call { + IsMethodCall::Yes => GenericArgPosition::MethodCall, + IsMethodCall::No => GenericArgPosition::Value, + }; + let has_self = generics.parent.is_none() && generics.has_self; + check_generic_arg_count(tcx, def_id, seg, generics, gen_pos, has_self) +} + +/// Checks that the correct number of generic arguments have been provided. +/// This is used both for datatypes and function calls. +#[instrument(skip(tcx, gen_pos), level = "debug")] +pub(crate) fn check_generic_arg_count( + tcx: TyCtxt<'_>, + def_id: DefId, + seg: &hir::PathSegment<'_>, + gen_params: &ty::Generics, + gen_pos: GenericArgPosition, + has_self: bool, +) -> GenericArgCountResult { + let gen_args = seg.args(); + let default_counts = gen_params.own_defaults(); + let param_counts = gen_params.own_counts(); + + // Subtracting from param count to ensure type params synthesized from `impl Trait` + // cannot be explicitly specified. + let synth_type_param_count = gen_params + .params + .iter() + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. })) + .count(); + let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; + let synth_const_param_count = gen_params + .params + .iter() + .filter(|param| { + matches!(param.kind, ty::GenericParamDefKind::Const { is_host_effect: true, .. }) + }) + .count(); + let named_const_param_count = param_counts.consts - synth_const_param_count; + let infer_lifetimes = + (gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_params(); + + if gen_pos != GenericArgPosition::Type + && let Some(b) = gen_args.bindings.first() + { + prohibit_assoc_item_binding(tcx, b, None); + } + + let explicit_late_bound = + prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); + + let mut invalid_args = vec![]; + + let mut check_lifetime_args = |min_expected_args: usize, + max_expected_args: usize, + provided_args: usize, + late_bounds_ignore: bool| { + if (min_expected_args..=max_expected_args).contains(&provided_args) { + return Ok(()); + } + + if late_bounds_ignore { + return Ok(()); + } + + if provided_args > max_expected_args { + invalid_args.extend( + gen_args.args[max_expected_args..provided_args].iter().map(|arg| arg.span()), + ); + }; + + let gen_args_info = if provided_args > min_expected_args { + invalid_args.extend( + gen_args.args[min_expected_args..provided_args].iter().map(|arg| arg.span()), + ); + let num_redundant_args = provided_args - min_expected_args; + GenericArgsInfo::ExcessLifetimes { num_redundant_args } + } else { + let num_missing_args = min_expected_args - provided_args; + GenericArgsInfo::MissingLifetimes { num_missing_args } + }; + + let reported = WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + has_self as usize, + gen_args, + def_id, + ) + .diagnostic() + .emit(); + + Err(reported) + }; + + let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; + let max_expected_lifetime_args = param_counts.lifetimes; + let num_provided_lifetime_args = gen_args.num_lifetime_params(); + + let lifetimes_correct = check_lifetime_args( + min_expected_lifetime_args, + max_expected_lifetime_args, + num_provided_lifetime_args, + explicit_late_bound == ExplicitLateBound::Yes, + ); + + let mut check_types_and_consts = |expected_min, + expected_max, + expected_max_with_synth, + provided, + params_offset, + args_offset| { + debug!( + ?expected_min, + ?expected_max, + ?provided, + ?params_offset, + ?args_offset, + "check_types_and_consts" + ); + if (expected_min..=expected_max).contains(&provided) { + return Ok(()); + } + + let num_default_params = expected_max - expected_min; + + let gen_args_info = if provided > expected_max { + invalid_args.extend( + gen_args.args[args_offset + expected_max..args_offset + provided] + .iter() + .map(|arg| arg.span()), + ); + let num_redundant_args = provided - expected_max; + + // Provide extra note if synthetic arguments like `impl Trait` are specified. + let synth_provided = provided <= expected_max_with_synth; + + GenericArgsInfo::ExcessTypesOrConsts { + num_redundant_args, + num_default_params, + args_offset, + synth_provided, + } + } else { + let num_missing_args = expected_max - provided; + + GenericArgsInfo::MissingTypesOrConsts { + num_missing_args, + num_default_params, + args_offset, + } + }; + + debug!(?gen_args_info); + + let reported = WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + params_offset, + gen_args, + def_id, + ) + .diagnostic() + .emit_unless(gen_args.has_err()); + + Err(reported) + }; + + let args_correct = { + let expected_min = if seg.infer_args { + 0 + } else { + param_counts.consts + named_type_param_count + - default_counts.types + - default_counts.consts + }; + debug!(?expected_min); + debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); + + let provided = gen_args.num_generic_params(); + + check_types_and_consts( + expected_min, + named_const_param_count + named_type_param_count, + named_const_param_count + named_type_param_count + synth_type_param_count, + provided, + param_counts.lifetimes + has_self as usize, + gen_args.num_lifetime_params(), + ) + }; + + GenericArgCountResult { + explicit_late_bound, + correct: lifetimes_correct + .and(args_correct) + .map_err(|reported| GenericArgCountMismatch { reported: Some(reported), invalid_args }), + } +} + +/// Prohibits explicit lifetime arguments if late-bound lifetime parameters +/// are present. This is used both for datatypes and function calls. +pub(crate) fn prohibit_explicit_late_bound_lifetimes( + tcx: TyCtxt<'_>, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, +) -> ExplicitLateBound { + let param_counts = def.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params(); + + if infer_lifetimes { + return ExplicitLateBound::No; + } + + if let Some(span_late) = def.has_late_bound_regions { + let msg = "cannot specify lifetime arguments explicitly \ + if late bound lifetime parameters are present"; + let note = "the late bound lifetime parameter is introduced here"; + let span = args.args[0].span(); + + if position == GenericArgPosition::Value + && args.num_lifetime_params() != param_counts.lifetimes + { + struct_span_code_err!(tcx.dcx(), span, E0794, "{}", msg) + .with_span_note(span_late, note) + .emit(); + } else { + let mut multispan = MultiSpan::from_span(span); + multispan.push_span_label(span_late, note); + tcx.node_span_lint( + LATE_BOUND_LIFETIME_ARGUMENTS, + args.args[0].hir_id(), + multispan, + msg, + |_| {}, + ); + } + + ExplicitLateBound::Yes + } else { + ExplicitLateBound::No + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs new file mode 100644 index 0000000000000..d9d36f5299b58 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -0,0 +1,301 @@ +use rustc_ast::TraitObjectSyntax; +use rustc_errors::{codes::*, Diag, EmissionGuarantee, StashKey}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability}; +use rustc_span::Span; +use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; + +use super::HirTyLowerer; + +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Prohibit or lint against *bare* trait object types depending on the edition. + /// + /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. + /// In edition 2021 and onward we emit a hard error for them. + pub(super) fn prohibit_or_lint_bare_trait_object_ty( + &self, + self_ty: &hir::Ty<'_>, + in_path: bool, + ) { + let tcx = self.tcx(); + + let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = + self_ty.kind + else { + return; + }; + + let needs_bracket = in_path + && !tcx + .sess + .source_map() + .span_to_prev_source(self_ty.span) + .ok() + .is_some_and(|s| s.trim_end().ends_with('<')); + + let is_global = poly_trait_ref.trait_ref.path.is_global(); + + let mut sugg = vec![( + self_ty.span.shrink_to_lo(), + format!( + "{}dyn {}", + if needs_bracket { "<" } else { "" }, + if is_global { "(" } else { "" }, + ), + )]; + + if is_global || needs_bracket { + sugg.push(( + self_ty.span.shrink_to_hi(), + format!( + "{}{}", + if is_global { ")" } else { "" }, + if needs_bracket { ">" } else { "" }, + ), + )); + } + + if self_ty.span.edition().at_least_rust_2021() { + let msg = "trait objects must include the `dyn` keyword"; + let label = "add `dyn` keyword before this trait"; + let mut diag = + rustc_errors::struct_span_code_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg); + if self_ty.span.can_be_used_for_suggestions() + && !self.maybe_suggest_impl_trait(self_ty, &mut diag) + { + // FIXME: Only emit this suggestion if the trait is object safe. + diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable); + } + // Check if the impl trait that we are considering is an impl of a local trait. + self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); + self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); + diag.stash(self_ty.span, StashKey::TraitMissingMethod); + } else { + let msg = "trait objects without an explicit `dyn` are deprecated"; + tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, msg, |lint| { + if self_ty.span.can_be_used_for_suggestions() { + lint.multipart_suggestion_verbose( + "if this is an object-safe trait, use `dyn`", + sugg, + Applicability::MachineApplicable, + ); + } + self.maybe_suggest_blanket_trait_impl(self_ty, lint); + }); + } + } + + /// Make sure that we are in the condition to suggest the blanket implementation. + fn maybe_suggest_blanket_trait_impl( + &self, + self_ty: &hir::Ty<'_>, + diag: &mut Diag<'_, G>, + ) { + let tcx = self.tcx(); + let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; + if let hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Impl(hir::Impl { + self_ty: impl_self_ty, + of_trait: Some(of_trait_ref), + generics, + .. + }), + .. + }) = tcx.hir_node_by_def_id(parent_id) + && self_ty.hir_id == impl_self_ty.hir_id + { + if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { + return; + } + let of_trait_span = of_trait_ref.path.span; + // make sure that we are not calling unwrap to abort during the compilation + let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { + return; + }; + + let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span) + else { + return; + }; + let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); + if sugg.is_empty() { + return; + }; + diag.multipart_suggestion( + format!( + "alternatively use a blanket implementation to implement `{of_trait_name}` for \ + all types that also implement `{impl_trait_name}`" + ), + sugg, + Applicability::MaybeIncorrect, + ); + } + } + + fn add_generic_param_suggestion( + &self, + generics: &hir::Generics<'_>, + self_ty_span: Span, + impl_trait_name: &str, + ) -> Vec<(Span, String)> { + // check if the trait has generics, to make a correct suggestion + let param_name = generics.params.next_type_param_name(None); + + let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { + (span, format!(", {param_name}: {impl_trait_name}")) + } else { + (generics.span, format!("<{param_name}: {impl_trait_name}>")) + }; + vec![(self_ty_span, param_name), add_generic_sugg] + } + + /// Make sure that we are in the condition to suggest `impl Trait`. + fn maybe_suggest_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool { + let tcx = self.tcx(); + let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; + // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0` + // and suggest `Trait0`. + let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => { + (sig, generics, None) + } + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + generics, + owner_id, + .. + }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), + _ => return false, + }; + let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { + return false; + }; + let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; + let mut is_downgradable = true; + let is_object_safe = match self_ty.kind { + hir::TyKind::TraitObject(objects, ..) => { + objects.iter().all(|o| match o.trait_ref.path.res { + Res::Def(DefKind::Trait, id) => { + if Some(id) == owner { + // For recursive traits, don't downgrade the error. (#119652) + is_downgradable = false; + } + tcx.check_is_object_safe(id) + } + _ => false, + }) + } + _ => false, + }; + if let hir::FnRetTy::Return(ty) = sig.decl.output + && ty.hir_id == self_ty.hir_id + { + let pre = if !is_object_safe { + format!("`{trait_name}` is not object safe, ") + } else { + String::new() + }; + let msg = format!( + "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ + single underlying type", + ); + diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); + if is_object_safe { + diag.multipart_suggestion_verbose( + "alternatively, you can return an owned trait object", + vec![ + (ty.span.shrink_to_lo(), "Box".to_string()), + ], + Applicability::MachineApplicable, + ); + } else if is_downgradable { + // We'll emit the object safety error already, with a structured suggestion. + diag.downgrade_to_delayed_bug(); + } + return true; + } + for ty in sig.decl.inputs { + if ty.hir_id != self_ty.hir_id { + continue; + } + let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name); + if !sugg.is_empty() { + diag.multipart_suggestion_verbose( + format!("use a new generic type parameter, constrained by `{trait_name}`"), + sugg, + Applicability::MachineApplicable, + ); + diag.multipart_suggestion_verbose( + "you can also use an opaque type, but users won't be able to specify the type \ + parameter when calling the `fn`, having to rely exclusively on type inference", + impl_sugg, + Applicability::MachineApplicable, + ); + } + if !is_object_safe { + diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`")); + if is_downgradable { + // We'll emit the object safety error already, with a structured suggestion. + diag.downgrade_to_delayed_bug(); + } + } else { + let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { + // There are more than one trait bound, we need surrounding parentheses. + vec![ + (self_ty.span.shrink_to_lo(), "&(dyn ".to_string()), + (self_ty.span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![(self_ty.span.shrink_to_lo(), "&dyn ".to_string())] + }; + diag.multipart_suggestion_verbose( + format!( + "alternatively, use a trait object to accept any type that implements \ + `{trait_name}`, accessing its methods at runtime using dynamic dispatch", + ), + sugg, + Applicability::MachineApplicable, + ); + } + return true; + } + false + } + + fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) { + let mut parents = self.tcx().hir().parent_iter(self_ty.hir_id); + + if let Some((_, hir::Node::TypeBinding(binding))) = parents.next() + && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(obj_ty) } = binding.kind + { + if let Some((_, hir::Node::TraitRef(..))) = parents.next() + && let Some((_, hir::Node::Ty(ty))) = parents.next() + && let hir::TyKind::TraitObject(..) = ty.kind + { + // Assoc ty bounds aren't permitted inside trait object types. + return; + } + + let lo = if binding.gen_args.span_ext.is_dummy() { + binding.ident.span + } else { + binding.gen_args.span_ext + }; + let hi = obj_ty.span; + + if !lo.eq_ctxt(hi) { + return; + } + + diag.span_suggestion_verbose( + lo.between(hi), + "you might have meant to write a bound here", + ": ", + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs new file mode 100644 index 0000000000000..f7213442ac2dd --- /dev/null +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -0,0 +1,2558 @@ +//! HIR ty lowering: Lowers type-system entities[^1] from the [HIR][hir] to +//! the [`rustc_middle::ty`] representation. +//! +//! Not to be confused with *AST lowering* which lowers AST constructs to HIR ones +//! or with *THIR* / *MIR* *lowering* / *building* which lowers HIR *bodies* +//! (i.e., “executable code”) to THIR / MIR. +//! +//! Most lowering routines are defined on [`dyn HirTyLowerer`](HirTyLowerer) directly, +//! like the main routine of this module, `lower_ty`. +//! +//! This module used to be called `astconv`. +//! +//! [^1]: This includes types, lifetimes / regions, constants in type positions, +//! trait references and bounds. + +mod bounds; +pub mod errors; +pub mod generics; +mod lint; +mod object_safety; + +use crate::bounds::Bounds; +use crate::collect::HirPlaceholderCollector; +use crate::errors::{AmbiguousLifetimeBound, WildPatTy}; +use crate::hir_ty_lowering::errors::{prohibit_assoc_item_binding, GenericsArgsErrExtend}; +use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; +use crate::middle::resolve_bound_vars as rbv; +use crate::require_c_abi_if_c_variadic; +use rustc_ast::TraitObjectSyntax; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_errors::{ + codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError, +}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::{walk_generics, Visitor as _}; +use rustc_hir::{GenericArg, GenericArgs, HirId}; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_infer::traits::ObligationCause; +use rustc_middle::middle::stability::AllowUnstable; +use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::ty::{ + self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, + TypeVisitableExt, +}; +use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_target::spec::abi; +use rustc_trait_selection::traits::wf::object_region_bounds; +use rustc_trait_selection::traits::{self, ObligationCtxt}; + +use std::fmt::Display; +use std::slice; + +/// A path segment that is semantically allowed to have generic arguments. +#[derive(Debug)] +pub struct GenericPathSegment(pub DefId, pub usize); + +#[derive(Copy, Clone, Debug)] +pub struct OnlySelfBounds(pub bool); + +#[derive(Copy, Clone, Debug)] +pub enum PredicateFilter { + /// All predicates may be implied by the trait. + All, + + /// Only traits that reference `Self: ..` are implied by the trait. + SelfOnly, + + /// Only traits that reference `Self: ..` and define an associated type + /// with the given ident are implied by the trait. + SelfThatDefines(Ident), + + /// Only traits that reference `Self: ..` and their associated type bounds. + /// For example, given `Self: Tr`, this would expand to `Self: Tr` + /// and `::A: B`. + SelfAndAssociatedTypeBounds, +} + +/// A context which can lower type-system entities from the [HIR][hir] to +/// the [`rustc_middle::ty`] representation. +/// +/// This trait used to be called `AstConv`. +pub trait HirTyLowerer<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx>; + + /// Returns the [`DefId`] of the overarching item whose constituents get lowered. + fn item_def_id(&self) -> DefId; + + /// Returns `true` if the current context allows the use of inference variables. + fn allow_infer(&self) -> bool; + + /// Returns the region to use when a lifetime is omitted (and not elided). + fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) + -> Option>; + + /// Returns the type to use when a type is omitted. + fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; + + /// Returns the const to use when a const is omitted. + fn ct_infer( + &self, + ty: Ty<'tcx>, + param: Option<&ty::GenericParamDef>, + span: Span, + ) -> Const<'tcx>; + + /// Probe bounds in scope where the bounded type coincides with the given type parameter. + /// + /// Rephrased, this returns bounds of the form `T: Trait`, where `T` is a type parameter + /// with the given `def_id`. This is a subset of the full set of bounds. + /// + /// This method may use the given `assoc_name` to disregard bounds whose trait reference + /// doesn't define an associated item with the provided name. + /// + /// This is used for one specific purpose: Resolving “short-hand” associated type references + /// like `T::Item` where `T` is a type parameter. In principle, we would do that by first + /// getting the full set of predicates in scope and then filtering down to find those that + /// apply to `T`, but this can lead to cycle errors. The problem is that we have to do this + /// resolution *in order to create the predicates in the first place*. + /// Hence, we have this “special pass”. + fn probe_ty_param_bounds( + &self, + span: Span, + def_id: LocalDefId, + assoc_name: Ident, + ) -> ty::GenericPredicates<'tcx>; + + /// Lower an associated type to a projection. + /// + /// This method has to be defined by the concrete lowering context because + /// dealing with higher-ranked trait references depends on its capabilities: + /// + /// If the context can make use of type inference, it can simply instantiate + /// any late-bound vars bound by the trait reference with inference variables. + /// If it doesn't support type inference, there is nothing reasonable it can + /// do except reject the associated type. + /// + /// The canonical example of this is associated type `T::P` where `T` is a type + /// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`. + fn lower_assoc_ty( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx>; + + /// Returns `AdtDef` if `ty` is an ADT. + /// + /// Note that `ty` might be a alias type that needs normalization. + /// This used to get the enum variants in scope of the type. + /// For example, `Self::A` could refer to an associated type + /// or to an enum variant depending on the result of this function. + fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option>; + + /// Record the lowered type of a HIR node in this context. + fn record_ty(&self, hir_id: HirId, ty: Ty<'tcx>, span: Span); + + /// The inference context of the lowering context if applicable. + fn infcx(&self) -> Option<&InferCtxt<'tcx>>; + + /// Taint the context with errors. + /// + /// Invoke this when you encounter an error from some prior pass like name resolution. + /// This is used to help suppress derived errors typeck might otherwise report. + fn set_tainted_by_errors(&self, e: ErrorGuaranteed); + + /// Convenience method for coercing the lowering context into a trait object type. + /// + /// Most lowering routines are defined on the trait object type directly + /// necessitating a coercion step from the concrete lowering context. + fn lowerer(&self) -> &dyn HirTyLowerer<'tcx> + where + Self: Sized, + { + self + } +} + +/// New-typed boolean indicating whether explicit late-bound lifetimes +/// are present in a set of generic arguments. +/// +/// For example if we have some method `fn f<'a>(&'a self)` implemented +/// for some type `T`, although `f` is generic in the lifetime `'a`, `'a` +/// is late-bound so should not be provided explicitly. Thus, if `f` is +/// instantiated with some generic arguments providing `'a` explicitly, +/// we taint those arguments with `ExplicitLateBound::Yes` so that we +/// can provide an appropriate diagnostic later. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum ExplicitLateBound { + Yes, + No, +} + +#[derive(Copy, Clone, PartialEq)] +pub enum IsMethodCall { + Yes, + No, +} + +/// Denotes the "position" of a generic argument, indicating if it is a generic type, +/// generic function or generic method call. +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum GenericArgPosition { + Type, + Value, // e.g., functions + MethodCall, +} + +/// A marker denoting that the generic arguments that were +/// provided did not match the respective generic parameters. +#[derive(Clone, Default, Debug)] +pub struct GenericArgCountMismatch { + /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). + pub reported: Option, + /// A list of spans of arguments provided that were not valid. + pub invalid_args: Vec, +} + +/// Decorates the result of a generic argument count mismatch +/// check with whether explicit late bounds were provided. +#[derive(Clone, Debug)] +pub struct GenericArgCountResult { + pub explicit_late_bound: ExplicitLateBound, + pub correct: Result<(), GenericArgCountMismatch>, +} + +/// A context which can lower HIR's [`GenericArg`] to `rustc_middle`'s [`ty::GenericArg`]. +/// +/// Its only consumer is [`generics::lower_generic_args`]. +/// Read its documentation to learn more. +pub trait GenericArgsLowerer<'a, 'tcx> { + fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'tcx>>, bool); + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'tcx>, + ) -> ty::GenericArg<'tcx>; + + fn inferred_kind( + &mut self, + args: Option<&[ty::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + infer_args: bool, + ) -> ty::GenericArg<'tcx>; +} + +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Lower a lifetime from the HIR to our internal notion of a lifetime called a *region*. + #[instrument(level = "debug", skip(self), ret)] + pub fn lower_lifetime( + &self, + lifetime: &hir::Lifetime, + def: Option<&ty::GenericParamDef>, + ) -> ty::Region<'tcx> { + let tcx = self.tcx(); + let lifetime_name = |def_id| tcx.hir().name(tcx.local_def_id_to_hir_id(def_id)); + + match tcx.named_bound_var(lifetime.hir_id) { + Some(rbv::ResolvedArg::StaticLifetime) => tcx.lifetimes.re_static, + + Some(rbv::ResolvedArg::LateBound(debruijn, index, def_id)) => { + let name = lifetime_name(def_id.expect_local()); + let br = ty::BoundRegion { + var: ty::BoundVar::from_u32(index), + kind: ty::BrNamed(def_id, name), + }; + ty::Region::new_bound(tcx, debruijn, br) + } + + Some(rbv::ResolvedArg::EarlyBound(def_id)) => { + let name = tcx.hir().ty_param_name(def_id.expect_local()); + let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + ty::Region::new_early_param(tcx, ty::EarlyParamRegion { def_id, index, name }) + } + + Some(rbv::ResolvedArg::Free(scope, id)) => { + let name = lifetime_name(id.expect_local()); + ty::Region::new_late_param(tcx, scope, ty::BrNamed(id, name)) + + // (*) -- not late-bound, won't change + } + + Some(rbv::ResolvedArg::Error(guar)) => ty::Region::new_error(tcx, guar), + + None => { + self.re_infer(def, lifetime.ident.span).unwrap_or_else(|| { + debug!(?lifetime, "unelided lifetime in signature"); + + // This indicates an illegal lifetime + // elision. `resolve_lifetime` should have + // reported an error in this case -- but if + // not, let's error out. + ty::Region::new_error_with_message( + tcx, + lifetime.ident.span, + "unelided lifetime in signature", + ) + }) + } + } + } + + pub fn lower_generic_args_of_path_segment( + &self, + span: Span, + def_id: DefId, + item_segment: &hir::PathSegment<'tcx>, + ) -> GenericArgsRef<'tcx> { + let (args, _) = self.lower_generic_args_of_path( + span, + def_id, + &[], + item_segment, + None, + ty::BoundConstness::NotConst, + ); + if let Some(b) = item_segment.args().bindings.first() { + prohibit_assoc_item_binding(self.tcx(), b, Some((def_id, item_segment, span))); + } + args + } + + /// Lower the generic arguments provided to some path. + /// + /// If this is a trait reference, you also need to pass the self type `self_ty`. + /// The lowering process may involve applying defaulted type parameters. + /// + /// Associated item bindings are not handled here! + /// + /// ### Example + /// + /// ```ignore (illustrative) + /// T: std::ops::Index + /// // ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 + /// ``` + /// + /// 1. The `self_ty` here would refer to the type `T`. + /// 2. The path in question is the path to the trait `std::ops::Index`, + /// which will have been resolved to a `def_id` + /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type + /// parameters are returned in the `GenericArgsRef` + /// 4. Associated type bindings like `Output = u32` are contained in `generic_args.bindings`. + /// + /// Note that the type listing given here is *exactly* what the user provided. + /// + /// For (generic) associated types + /// + /// ```ignore (illustrative) + /// as Iterable>::Iter::<'a> + /// ``` + /// + /// We have the parent args are the args for the parent trait: + /// `[Vec, u8]` and `generic_args` are the arguments for the associated + /// type itself: `['a]`. The returned `GenericArgsRef` concatenates these two + /// lists: `[Vec, u8, 'a]`. + #[instrument(level = "debug", skip(self, span), ret)] + fn lower_generic_args_of_path( + &self, + span: Span, + def_id: DefId, + parent_args: &[ty::GenericArg<'tcx>], + segment: &hir::PathSegment<'tcx>, + self_ty: Option>, + constness: ty::BoundConstness, + ) -> (GenericArgsRef<'tcx>, GenericArgCountResult) { + // If the type is parameterized by this region, then replace this + // region with the current anon region binding (in other words, + // whatever & would get replaced with). + + let tcx = self.tcx(); + let generics = tcx.generics_of(def_id); + debug!(?generics); + + if generics.has_self { + if generics.parent.is_some() { + // The parent is a trait so it should have at least one + // generic parameter for the `Self` type. + assert!(!parent_args.is_empty()) + } else { + // This item (presumably a trait) needs a self-type. + assert!(self_ty.is_some()); + } + } else { + assert!(self_ty.is_none()); + } + + let mut arg_count = check_generic_arg_count( + tcx, + def_id, + segment, + generics, + GenericArgPosition::Type, + self_ty.is_some(), + ); + + if let Err(err) = &arg_count.correct + && let Some(reported) = err.reported + { + self.set_tainted_by_errors(reported); + } + + // Skip processing if type has no generic parameters. + // Traits always have `Self` as a generic parameter, which means they will not return early + // here and so associated type bindings will be handled regardless of whether there are any + // non-`Self` generic parameters. + if generics.params.is_empty() { + return (tcx.mk_args(parent_args), arg_count); + } + + struct GenericArgsCtxt<'a, 'tcx> { + lowerer: &'a dyn HirTyLowerer<'tcx>, + def_id: DefId, + generic_args: &'a GenericArgs<'tcx>, + span: Span, + inferred_params: Vec, + infer_args: bool, + } + + impl<'a, 'tcx> GenericArgsLowerer<'a, 'tcx> for GenericArgsCtxt<'a, 'tcx> { + fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'tcx>>, bool) { + if did == self.def_id { + (Some(self.generic_args), self.infer_args) + } else { + // The last component of this tuple is unimportant. + (None, false) + } + } + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'tcx>, + ) -> ty::GenericArg<'tcx> { + let tcx = self.lowerer.tcx(); + + let mut handle_ty_args = |has_default, ty: &hir::Ty<'tcx>| { + if has_default { + tcx.check_optional_stability( + param.def_id, + Some(arg.hir_id()), + arg.span(), + None, + AllowUnstable::No, + |_, _| { + // Default generic parameters may not be marked + // with stability attributes, i.e. when the + // default parameter was defined at the same time + // as the rest of the type. As such, we ignore missing + // stability attributes. + }, + ); + } + if let (hir::TyKind::Infer, false) = (&ty.kind, self.lowerer.allow_infer()) { + self.inferred_params.push(ty.span); + Ty::new_misc_error(tcx).into() + } else { + self.lowerer.lower_ty(ty).into() + } + }; + + match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + self.lowerer.lower_lifetime(lt, Some(param)).into() + } + (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { + handle_ty_args(has_default, ty) + } + (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => { + handle_ty_args(has_default, &inf.to_ty()) + } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { + let did = ct.value.def_id; + tcx.feed_anon_const_type(did, tcx.type_of(param.def_id)); + ty::Const::from_anon_const(tcx, did).into() + } + (&GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => { + let ty = tcx + .at(self.span) + .type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + if self.lowerer.allow_infer() { + self.lowerer.ct_infer(ty, Some(param), inf.span).into() + } else { + self.inferred_params.push(inf.span); + ty::Const::new_misc_error(tcx, ty).into() + } + } + (kind, arg) => span_bug!( + self.span, + "mismatched path argument for kind {kind:?}: found arg {arg:?}" + ), + } + } + + fn inferred_kind( + &mut self, + args: Option<&[ty::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + infer_args: bool, + ) -> ty::GenericArg<'tcx> { + let tcx = self.lowerer.tcx(); + match param.kind { + GenericParamDefKind::Lifetime => self + .lowerer + .re_infer(Some(param), self.span) + .unwrap_or_else(|| { + debug!(?param, "unelided lifetime in signature"); + + // This indicates an illegal lifetime in a non-assoc-trait position + ty::Region::new_error_with_message( + tcx, + self.span, + "unelided lifetime in signature", + ) + }) + .into(), + GenericParamDefKind::Type { has_default, .. } => { + if !infer_args && has_default { + // No type parameter provided, but a default exists. + let args = args.unwrap(); + if args.iter().any(|arg| match arg.unpack() { + GenericArgKind::Type(ty) => ty.references_error(), + _ => false, + }) { + // Avoid ICE #86756 when type error recovery goes awry. + return Ty::new_misc_error(tcx).into(); + } + tcx.at(self.span).type_of(param.def_id).instantiate(tcx, args).into() + } else if infer_args { + self.lowerer.ty_infer(Some(param), self.span).into() + } else { + // We've already errored above about the mismatch. + Ty::new_misc_error(tcx).into() + } + } + GenericParamDefKind::Const { has_default, .. } => { + let ty = tcx + .at(self.span) + .type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + if let Err(guar) = ty.error_reported() { + return ty::Const::new_error(tcx, guar, ty).into(); + } + // FIXME(effects) see if we should special case effect params here + if !infer_args && has_default { + tcx.const_param_default(param.def_id) + .instantiate(tcx, args.unwrap()) + .into() + } else { + if infer_args { + self.lowerer.ct_infer(ty, Some(param), self.span).into() + } else { + // We've already errored above about the mismatch. + ty::Const::new_misc_error(tcx, ty).into() + } + } + } + } + } + } + + let mut args_ctx = GenericArgsCtxt { + lowerer: self, + def_id, + span, + generic_args: segment.args(), + inferred_params: vec![], + infer_args: segment.infer_args, + }; + if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness + && generics.has_self + && !tcx.has_attr(def_id, sym::const_trait) + { + let e = tcx.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { + span, + modifier: constness.as_str(), + }); + self.set_tainted_by_errors(e); + arg_count.correct = + Err(GenericArgCountMismatch { reported: Some(e), invalid_args: vec![] }); + } + let args = lower_generic_args( + tcx, + def_id, + parent_args, + self_ty.is_some(), + self_ty, + &arg_count, + &mut args_ctx, + ); + + (args, arg_count) + } + + #[instrument(level = "debug", skip_all)] + pub fn lower_generic_args_of_assoc_item( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'tcx>, + parent_args: GenericArgsRef<'tcx>, + ) -> GenericArgsRef<'tcx> { + debug!(?span, ?item_def_id, ?item_segment); + let (args, _) = self.lower_generic_args_of_path( + span, + item_def_id, + parent_args, + item_segment, + None, + ty::BoundConstness::NotConst, + ); + if let Some(b) = item_segment.args().bindings.first() { + prohibit_assoc_item_binding(self.tcx(), b, Some((item_def_id, item_segment, span))); + } + args + } + + /// Lower a trait reference as found in an impl header as the implementee. + /// + /// The self type `self_ty` is the implementer of the trait. + pub fn lower_impl_trait_ref( + &self, + trait_ref: &hir::TraitRef<'tcx>, + self_ty: Ty<'tcx>, + ) -> ty::TraitRef<'tcx> { + let _ = self.prohibit_generic_args( + trait_ref.path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); + + self.lower_mono_trait_ref( + trait_ref.path.span, + trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), + self_ty, + trait_ref.path.segments.last().unwrap(), + true, + ty::BoundConstness::NotConst, + ) + } + + /// Lower a polymorphic trait reference given a self type into `bounds`. + /// + /// *Polymorphic* in the sense that it may bind late-bound vars. + /// + /// This may generate auxiliary bounds if the trait reference contains associated item bindings. + /// + /// ### Example + /// + /// Given the trait ref `Iterator` and the self type `Ty`, this will add the + /// + /// 1. *trait predicate* `` (known as `Foo: Iterator` in surface syntax) and the + /// 2. *projection predicate* `::Item = u32` + /// + /// to `bounds`. + /// + /// ### A Note on Binders + /// + /// Against our usual convention, there is an implied binder around the `self_ty` and the + /// `trait_ref` here. So they may reference late-bound vars. + /// + /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>` + /// where `'a` is a bound region at depth 0. Similarly, the `trait_ref` would be `Bar<'a>`. + /// The lowered poly-trait-ref will track this binder explicitly, however. + #[instrument(level = "debug", skip(self, span, constness, bounds))] + pub(crate) fn lower_poly_trait_ref( + &self, + trait_ref: &hir::TraitRef<'tcx>, + span: Span, + constness: ty::BoundConstness, + polarity: ty::PredicatePolarity, + self_ty: Ty<'tcx>, + bounds: &mut Bounds<'tcx>, + only_self_bounds: OnlySelfBounds, + ) -> GenericArgCountResult { + let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); + let trait_segment = trait_ref.path.segments.last().unwrap(); + + let _ = self.prohibit_generic_args( + trait_ref.path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); + self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); + + let (generic_args, arg_count) = self.lower_generic_args_of_path( + trait_ref.path.span, + trait_def_id, + &[], + trait_segment, + Some(self_ty), + constness, + ); + + let tcx = self.tcx(); + let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id); + debug!(?bound_vars); + + let poly_trait_ref = ty::Binder::bind_with_vars( + ty::TraitRef::new(tcx, trait_def_id, generic_args), + bound_vars, + ); + + debug!(?poly_trait_ref); + bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity); + + let mut dup_bindings = FxIndexMap::default(); + for binding in trait_segment.args().bindings { + // Don't register additional associated type bounds for negative bounds, + // since we should have emitten an error for them earlier, and they will + // not be well-formed! + if polarity != ty::PredicatePolarity::Positive { + assert!( + self.tcx().dcx().has_errors().is_some(), + "negative trait bounds should not have bindings", + ); + continue; + } + + // Specify type to assert that error was already reported in `Err` case. + let _: Result<_, ErrorGuaranteed> = self.lower_assoc_item_binding( + trait_ref.hir_ref_id, + poly_trait_ref, + binding, + bounds, + &mut dup_bindings, + binding.span, + only_self_bounds, + ); + // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). + } + + arg_count + } + + /// Lower a monomorphic trait reference given a self type while prohibiting associated item bindings. + /// + /// *Monomorphic* in the sense that it doesn't bind any late-bound vars. + fn lower_mono_trait_ref( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + trait_segment: &hir::PathSegment<'tcx>, + is_impl: bool, + // FIXME(effects): Move all host param things in HIR ty lowering to AST lowering. + constness: ty::BoundConstness, + ) -> ty::TraitRef<'tcx> { + self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); + + let (generic_args, _) = self.lower_generic_args_of_path( + span, + trait_def_id, + &[], + trait_segment, + Some(self_ty), + constness, + ); + if let Some(b) = trait_segment.args().bindings.first() { + prohibit_assoc_item_binding(self.tcx(), b, Some((trait_def_id, trait_segment, span))); + } + ty::TraitRef::new(self.tcx(), trait_def_id, generic_args) + } + + fn probe_trait_that_defines_assoc_item( + &self, + trait_def_id: DefId, + assoc_kind: ty::AssocKind, + assoc_name: Ident, + ) -> bool { + self.tcx() + .associated_items(trait_def_id) + .find_by_name_and_kind(self.tcx(), assoc_name, assoc_kind, trait_def_id) + .is_some() + } + + fn lower_path_segment( + &self, + span: Span, + did: DefId, + item_segment: &hir::PathSegment<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + let args = self.lower_generic_args_of_path_segment(span, did, item_segment); + + if let DefKind::TyAlias = tcx.def_kind(did) + && tcx.type_alias_is_lazy(did) + { + // Type aliases defined in crates that have the + // feature `lazy_type_alias` enabled get encoded as a type alias that normalization will + // then actually instantiate the where bounds of. + let alias_ty = ty::AliasTy::new(tcx, did, args); + Ty::new_alias(tcx, ty::Weak, alias_ty) + } else { + tcx.at(span).type_of(did).instantiate(tcx, args) + } + } + + /// Search for a trait bound on a type parameter whose trait defines the associated type given by `assoc_name`. + /// + /// This fails if there is no such bound in the list of candidates or if there are multiple + /// candidates in which case it reports ambiguity. + /// + /// `ty_param_def_id` is the `LocalDefId` of the type parameter. + #[instrument(level = "debug", skip_all, ret)] + fn probe_single_ty_param_bound_for_assoc_ty( + &self, + ty_param_def_id: LocalDefId, + assoc_name: Ident, + span: Span, + ) -> Result, ErrorGuaranteed> { + debug!(?ty_param_def_id, ?assoc_name, ?span); + let tcx = self.tcx(); + + let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates; + debug!("predicates={:#?}", predicates); + + let param_name = tcx.hir().ty_param_name(ty_param_def_id); + self.probe_single_bound_for_assoc_item( + || { + traits::transitive_bounds_that_define_assoc_item( + tcx, + predicates + .iter() + .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))), + assoc_name, + ) + }, + param_name, + Some(ty_param_def_id), + ty::AssocKind::Type, + assoc_name, + span, + None, + ) + } + + /// Search for a single trait bound whose trait defines the associated item given by `assoc_name`. + /// + /// This fails if there is no such bound in the list of candidates or if there are multiple + /// candidates in which case it reports ambiguity. + #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, binding), ret)] + fn probe_single_bound_for_assoc_item( + &self, + all_candidates: impl Fn() -> I, + ty_param_name: impl Display, + ty_param_def_id: Option, + assoc_kind: ty::AssocKind, + assoc_name: Ident, + span: Span, + binding: Option<&hir::TypeBinding<'tcx>>, + ) -> Result, ErrorGuaranteed> + where + I: Iterator>, + { + let tcx = self.tcx(); + + let mut matching_candidates = all_candidates().filter(|r| { + self.probe_trait_that_defines_assoc_item(r.def_id(), assoc_kind, assoc_name) + }); + + let Some(bound) = matching_candidates.next() else { + let reported = self.complain_about_assoc_item_not_found( + all_candidates, + &ty_param_name.to_string(), + ty_param_def_id, + assoc_kind, + assoc_name, + span, + binding, + ); + self.set_tainted_by_errors(reported); + return Err(reported); + }; + debug!(?bound); + + if let Some(bound2) = matching_candidates.next() { + debug!(?bound2); + + let assoc_kind_str = assoc_kind_str(assoc_kind); + let ty_param_name = &ty_param_name.to_string(); + let mut err = tcx.dcx().create_err(crate::errors::AmbiguousAssocItem { + span, + assoc_kind: assoc_kind_str, + assoc_name, + ty_param_name, + }); + // Provide a more specific error code index entry for equality bindings. + err.code( + if let Some(binding) = binding + && let hir::TypeBindingKind::Equality { .. } = binding.kind + { + E0222 + } else { + E0221 + }, + ); + + // FIXME(#97583): Resugar equality bounds to type/const bindings. + // FIXME: Turn this into a structured, translateable & more actionable suggestion. + let mut where_bounds = vec![]; + for bound in [bound, bound2].into_iter().chain(matching_candidates) { + let bound_id = bound.def_id(); + let bound_span = tcx + .associated_items(bound_id) + .find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id) + .and_then(|item| tcx.hir().span_if_local(item.def_id)); + + if let Some(bound_span) = bound_span { + err.span_label( + bound_span, + format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),), + ); + if let Some(binding) = binding { + match binding.kind { + hir::TypeBindingKind::Equality { term } => { + let term: ty::Term<'_> = match term { + hir::Term::Ty(ty) => self.lower_ty(ty).into(), + hir::Term::Const(ct) => { + ty::Const::from_anon_const(tcx, ct.def_id).into() + } + }; + // FIXME(#97583): This isn't syntactically well-formed! + where_bounds.push(format!( + " T: {trait}::{assoc_name} = {term}", + trait = bound.print_only_trait_path(), + )); + } + // FIXME: Provide a suggestion. + hir::TypeBindingKind::Constraint { bounds: _ } => {} + } + } else { + err.span_suggestion_verbose( + span.with_hi(assoc_name.span.lo()), + "use fully-qualified syntax to disambiguate", + format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()), + Applicability::MaybeIncorrect, + ); + } + } else { + err.note(format!( + "associated {assoc_kind_str} `{assoc_name}` could derive from `{}`", + bound.print_only_trait_path(), + )); + } + } + if !where_bounds.is_empty() { + err.help(format!( + "consider introducing a new type parameter `T` and adding `where` constraints:\ + \n where\n T: {ty_param_name},\n{}", + where_bounds.join(",\n"), + )); + } + let reported = err.emit(); + self.set_tainted_by_errors(reported); + if !where_bounds.is_empty() { + return Err(reported); + } + } + + Ok(bound) + } + + /// Lower a [type-relative] path referring to an associated type or to an enum variant. + /// + /// If the path refers to an enum variant and `permit_variants` holds, + /// the returned type is simply the provided self type `qself_ty`. + /// + /// A path like `A::B::C::D` is understood as `::D`. I.e., + /// `qself_ty` / `qself` is `A::B::C` and `assoc_segment` is `D`. + /// We return the lowered type and the `DefId` for the whole path. + /// + /// We only support associated type paths whose self type is a type parameter or a `Self` + /// type alias (in a trait impl) like `T::Ty` (where `T` is a ty param) or `Self::Ty`. + /// We **don't** support paths whose self type is an arbitrary type like `Struct::Ty` where + /// struct `Struct` impls an in-scope trait that defines an associated type called `Ty`. + /// For the latter case, we report ambiguity. + /// While desirable to support, the implemention would be non-trivial. Tracked in [#22519]. + /// + /// At the time of writing, *inherent associated types* are also resolved here. This however + /// is [problematic][iat]. A proper implementation would be as non-trivial as the one + /// described in the previous paragraph and their modeling of projections would likely be + /// very similar in nature. + /// + /// [type-relative]: hir::QPath::TypeRelative + /// [#22519]: https://github.com/rust-lang/rust/issues/22519 + /// [iat]: https://github.com/rust-lang/rust/issues/8995#issuecomment-1569208403 + // + // NOTE: When this function starts resolving `Trait::AssocTy` successfully + // it should also start reporting the `BARE_TRAIT_OBJECTS` lint. + #[instrument(level = "debug", skip_all, ret)] + pub fn lower_assoc_path( + &self, + hir_ref_id: HirId, + span: Span, + qself_ty: Ty<'tcx>, + qself: &'tcx hir::Ty<'tcx>, + assoc_segment: &'tcx hir::PathSegment<'tcx>, + permit_variants: bool, + ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { + debug!(%qself_ty, ?assoc_segment.ident); + let tcx = self.tcx(); + + let assoc_ident = assoc_segment.ident; + let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { + path.res + } else { + Res::Err + }; + + // Check if we have an enum variant or an inherent associated type. + let mut variant_resolution = None; + if let Some(adt_def) = self.probe_adt(span, qself_ty) { + if adt_def.is_enum() { + let variant_def = adt_def + .variants() + .iter() + .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did())); + if let Some(variant_def) = variant_def { + if permit_variants { + tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); + let _ = self.prohibit_generic_args( + slice::from_ref(assoc_segment).iter(), + GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def }, + ); + return Ok((qself_ty, DefKind::Variant, variant_def.def_id)); + } else { + variant_resolution = Some(variant_def.def_id); + } + } + } + + // FIXME(inherent_associated_types, #106719): Support self types other than ADTs. + if let Some((ty, did)) = self.probe_inherent_assoc_ty( + assoc_ident, + assoc_segment, + adt_def.did(), + qself_ty, + hir_ref_id, + span, + )? { + return Ok((ty, DefKind::AssocTy, did)); + } + } + + // Find the type of the associated item, and the trait where the associated + // item is declared. + let bound = match (&qself_ty.kind(), qself_res) { + (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => { + // `Self` in an impl of a trait -- we have a concrete self type and a + // trait reference. + let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else { + // A cycle error occurred, most likely. + tcx.dcx().span_bug(span, "expected cycle error"); + }; + + self.probe_single_bound_for_assoc_item( + || { + traits::supertraits( + tcx, + ty::Binder::dummy(trait_ref.instantiate_identity()), + ) + }, + kw::SelfUpper, + None, + ty::AssocKind::Type, + assoc_ident, + span, + None, + )? + } + ( + &ty::Param(_), + Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), + ) => self.probe_single_ty_param_bound_for_assoc_ty( + param_did.expect_local(), + assoc_ident, + span, + )?, + _ => { + let reported = if variant_resolution.is_some() { + // Variant in type position + let msg = format!("expected type, found variant `{assoc_ident}`"); + tcx.dcx().span_err(span, msg) + } else if qself_ty.is_enum() { + let mut err = struct_span_code_err!( + tcx.dcx(), + assoc_ident.span, + E0599, + "no variant named `{}` found for enum `{}`", + assoc_ident, + qself_ty, + ); + + let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); + if let Some(suggested_name) = find_best_match_for_name( + &adt_def + .variants() + .iter() + .map(|variant| variant.name) + .collect::>(), + assoc_ident.name, + None, + ) { + err.span_suggestion( + assoc_ident.span, + "there is a variant with a similar name", + suggested_name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_label( + assoc_ident.span, + format!("variant not found in `{qself_ty}`"), + ); + } + + if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) { + err.span_label(sp, format!("variant `{assoc_ident}` not found here")); + } + + err.emit() + } else if let Err(reported) = qself_ty.error_reported() { + reported + } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() { + // `::Assoc` makes no sense. + struct_span_code_err!( + tcx.dcx(), + tcx.def_span(alias_ty.def_id), + E0667, + "`impl Trait` is not allowed in path parameters" + ) + .emit() // Already reported in an earlier stage. + } else { + self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?; + + let traits: Vec<_> = + self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); + + // Don't print `ty::Error` to the user. + self.report_ambiguous_assoc_ty( + span, + &[qself_ty.to_string()], + &traits, + assoc_ident.name, + ) + }; + self.set_tainted_by_errors(reported); + return Err(reported); + } + }; + + let trait_did = bound.def_id(); + let assoc_ty_did = self.probe_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap(); + let ty = self.lower_assoc_ty(span, assoc_ty_did, assoc_segment, bound); + + if let Some(variant_def_id) = variant_resolution { + tcx.node_span_lint( + AMBIGUOUS_ASSOCIATED_ITEMS, + hir_ref_id, + span, + "ambiguous associated item", + |lint| { + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + assoc_ident, + also, + tcx.def_kind_descr(kind, def_id) + ); + lint.span_note(tcx.def_span(def_id), note_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(DefKind::AssocTy, assoc_ty_did, " also"); + + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), + Applicability::MachineApplicable, + ); + }, + ); + } + Ok((ty, DefKind::AssocTy, assoc_ty_did)) + } + + fn probe_inherent_assoc_ty( + &self, + name: Ident, + segment: &hir::PathSegment<'tcx>, + adt_did: DefId, + self_ty: Ty<'tcx>, + block: HirId, + span: Span, + ) -> Result, DefId)>, ErrorGuaranteed> { + let tcx = self.tcx(); + + // Don't attempt to look up inherent associated types when the feature is not enabled. + // Theoretically it'd be fine to do so since we feature-gate their definition site. + // However, due to current limitations of the implementation (caused by us performing + // selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle + // errors (#108491) which mask the feature-gate error, needlessly confusing users + // who use IATs by accident (#113265). + if !tcx.features().inherent_associated_types { + return Ok(None); + } + + let candidates: Vec<_> = tcx + .inherent_impls(adt_did)? + .iter() + .filter_map(|&impl_| Some((impl_, self.probe_assoc_ty_unchecked(name, block, impl_)?))) + .collect(); + + if candidates.is_empty() { + return Ok(None); + } + + // + // Select applicable inherent associated type candidates modulo regions. + // + + // In contexts that have no inference context, just make a new one. + // We do need a local variable to store it, though. + let infcx_; + let infcx = match self.infcx() { + Some(infcx) => infcx, + None => { + assert!(!self_ty.has_infer()); + infcx_ = tcx.infer_ctxt().ignoring_regions().build(); + &infcx_ + } + }; + + // FIXME(inherent_associated_types): Acquiring the ParamEnv this early leads to cycle errors + // when inside of an ADT (#108491) or where clause. + let param_env = tcx.param_env(block.owner); + + let mut universes = if self_ty.has_escaping_bound_vars() { + vec![None; self_ty.outer_exclusive_binder().as_usize()] + } else { + vec![] + }; + + let (impl_, (assoc_item, def_scope)) = crate::traits::with_replaced_escaping_bound_vars( + infcx, + &mut universes, + self_ty, + |self_ty| { + self.select_inherent_assoc_type_candidates( + infcx, name, span, self_ty, param_env, candidates, + ) + }, + )?; + + self.check_assoc_ty(assoc_item, name, def_scope, block, span); + + // FIXME(fmease): Currently creating throwaway `parent_args` to please + // `lower_generic_args_of_assoc_item`. Modify the latter instead (or sth. similar) to + // not require the parent args logic. + let parent_args = ty::GenericArgs::identity_for_item(tcx, impl_); + let args = self.lower_generic_args_of_assoc_item(span, assoc_item, segment, parent_args); + let args = tcx.mk_args_from_iter( + std::iter::once(ty::GenericArg::from(self_ty)) + .chain(args.into_iter().skip(parent_args.len())), + ); + + let ty = Ty::new_alias(tcx, ty::Inherent, ty::AliasTy::new(tcx, assoc_item, args)); + + Ok(Some((ty, assoc_item))) + } + + fn select_inherent_assoc_type_candidates( + &self, + infcx: &InferCtxt<'tcx>, + name: Ident, + span: Span, + self_ty: Ty<'tcx>, + param_env: ParamEnv<'tcx>, + candidates: Vec<(DefId, (DefId, DefId))>, + ) -> Result<(DefId, (DefId, DefId)), ErrorGuaranteed> { + let tcx = self.tcx(); + let mut fulfillment_errors = Vec::new(); + + let applicable_candidates: Vec<_> = candidates + .iter() + .copied() + .filter(|&(impl_, _)| { + infcx.probe(|_| { + let ocx = ObligationCtxt::new(infcx); + let self_ty = ocx.normalize(&ObligationCause::dummy(), param_env, self_ty); + + let impl_args = infcx.fresh_args_for_item(span, impl_); + let impl_ty = tcx.type_of(impl_).instantiate(tcx, impl_args); + let impl_ty = ocx.normalize(&ObligationCause::dummy(), param_env, impl_ty); + + // Check that the self types can be related. + if ocx.eq(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() { + return false; + } + + // Check whether the impl imposes obligations we have to worry about. + let impl_bounds = tcx.predicates_of(impl_).instantiate(tcx, impl_args); + let impl_bounds = + ocx.normalize(&ObligationCause::dummy(), param_env, impl_bounds); + let impl_obligations = traits::predicates_for_generics( + |_, _| ObligationCause::dummy(), + param_env, + impl_bounds, + ); + ocx.register_obligations(impl_obligations); + + let mut errors = ocx.select_where_possible(); + if !errors.is_empty() { + fulfillment_errors.append(&mut errors); + return false; + } + + true + }) + }) + .collect(); + + match &applicable_candidates[..] { + &[] => Err(self.complain_about_inherent_assoc_ty_not_found( + name, + self_ty, + candidates, + fulfillment_errors, + span, + )), + + &[applicable_candidate] => Ok(applicable_candidate), + + &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc_ty( + name, + applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(), + span, + )), + } + } + + fn probe_assoc_ty(&self, name: Ident, block: HirId, span: Span, scope: DefId) -> Option { + let (item, def_scope) = self.probe_assoc_ty_unchecked(name, block, scope)?; + self.check_assoc_ty(item, name, def_scope, block, span); + Some(item) + } + + fn probe_assoc_ty_unchecked( + &self, + name: Ident, + block: HirId, + scope: DefId, + ) -> Option<(DefId, DefId)> { + let tcx = self.tcx(); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block); + + // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` + // instead of calling `filter_by_name_and_kind` which would needlessly normalize the + // `ident` again and again. + let item = tcx.associated_items(scope).in_definition_order().find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == ident + })?; + + Some((item.def_id, def_scope)) + } + + fn check_assoc_ty(&self, item: DefId, name: Ident, def_scope: DefId, block: HirId, span: Span) { + let tcx = self.tcx(); + let kind = DefKind::AssocTy; + + if !tcx.visibility(item).is_accessible_from(def_scope, tcx) { + let kind = tcx.def_kind_descr(kind, item); + let msg = format!("{kind} `{name}` is private"); + let def_span = tcx.def_span(item); + let reported = tcx + .dcx() + .struct_span_err(span, msg) + .with_code(E0624) + .with_span_label(span, format!("private {kind}")) + .with_span_label(def_span, format!("{kind} defined here")) + .emit(); + self.set_tainted_by_errors(reported); + } + tcx.check_stability(item, Some(block), span, None); + } + + fn probe_traits_that_match_assoc_ty( + &self, + qself_ty: Ty<'tcx>, + assoc_ident: Ident, + ) -> Vec { + let tcx = self.tcx(); + + // In contexts that have no inference context, just make a new one. + // We do need a local variable to store it, though. + let infcx_; + let infcx = if let Some(infcx) = self.infcx() { + infcx + } else { + assert!(!qself_ty.has_infer()); + infcx_ = tcx.infer_ctxt().build(); + &infcx_ + }; + + tcx.all_traits() + .filter(|trait_def_id| { + // Consider only traits with the associated type + tcx.associated_items(*trait_def_id) + .in_definition_order() + .any(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident + && matches!(i.kind, ty::AssocKind::Type) + }) + // Consider only accessible traits + && tcx.visibility(*trait_def_id) + .is_accessible_from(self.item_def_id(), tcx) + && tcx.all_impls(*trait_def_id) + .any(|impl_def_id| { + let impl_header = tcx.impl_trait_header(impl_def_id); + impl_header.is_some_and(|header| { + let trait_ref = header.trait_ref.instantiate( + tcx, + infcx.fresh_args_for_item(DUMMY_SP, impl_def_id), + ); + + let value = tcx.fold_regions(qself_ty, |_, _| tcx.lifetimes.re_erased); + // FIXME: Don't bother dealing with non-lifetime binders here... + if value.has_escaping_bound_vars() { + return false; + } + infcx + .can_eq( + ty::ParamEnv::empty(), + trait_ref.self_ty(), + value, + ) && header.polarity != ty::ImplPolarity::Negative + }) + }) + }) + .map(|trait_def_id| tcx.def_path_str(trait_def_id)) + .collect() + } + + /// Lower a qualified path to a type. + #[instrument(level = "debug", skip_all)] + fn lower_qpath( + &self, + span: Span, + opt_self_ty: Option>, + item_def_id: DefId, + trait_segment: &hir::PathSegment<'tcx>, + item_segment: &hir::PathSegment<'tcx>, + constness: ty::BoundConstness, + ) -> Ty<'tcx> { + let tcx = self.tcx(); + + let trait_def_id = tcx.parent(item_def_id); + debug!(?trait_def_id); + + let Some(self_ty) = opt_self_ty else { + let path_str = tcx.def_path_str(trait_def_id); + + let def_id = self.item_def_id(); + debug!(item_def_id = ?def_id); + + let parent_def_id = def_id + .as_local() + .map(|def_id| tcx.local_def_id_to_hir_id(def_id)) + .map(|hir_id| tcx.hir().get_parent_item(hir_id).to_def_id()); + debug!(?parent_def_id); + + // If the trait in segment is the same as the trait defining the item, + // use the `` syntax in the error. + let is_part_of_self_trait_constraints = def_id == trait_def_id; + let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id); + + let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { + vec!["Self".to_string()] + } else { + // Find all the types that have an `impl` for the trait. + tcx.all_impls(trait_def_id) + .filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id)) + .filter(|header| { + // Consider only accessible traits + tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx) + && header.polarity != ty::ImplPolarity::Negative + }) + .map(|header| header.trait_ref.instantiate_identity().self_ty()) + // We don't care about blanket impls. + .filter(|self_ty| !self_ty.has_non_region_param()) + .map(|self_ty| tcx.erase_regions(self_ty).to_string()) + .collect() + }; + // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that + // references the trait. Relevant for the first case in + // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` + let reported = self.report_ambiguous_assoc_ty( + span, + &type_names, + &[path_str], + item_segment.ident.name, + ); + return Ty::new_error(tcx, reported); + }; + debug!(?self_ty); + + let trait_ref = + self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false, constness); + debug!(?trait_ref); + + let item_args = + self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, trait_ref.args); + + Ty::new_projection(tcx, item_def_id, item_args) + } + + pub fn prohibit_generic_args<'a>( + &self, + segments: impl Iterator> + Clone, + err_extend: GenericsArgsErrExtend<'_>, + ) -> Result<(), ErrorGuaranteed> { + let args_visitors = segments.clone().flat_map(|segment| segment.args().args); + let mut result = Ok(()); + if let Some(_) = args_visitors.clone().next() { + result = Err(self.report_prohibit_generics_error( + segments.clone(), + args_visitors, + err_extend, + )); + } + + for segment in segments { + // Only emit the first error to avoid overloading the user with error messages. + if let Some(b) = segment.args().bindings.first() { + return Err(prohibit_assoc_item_binding(self.tcx(), b, None)); + } + } + + result + } + + /// Probe path segments that are semantically allowed to have generic arguments. + /// + /// ### Example + /// + /// ```ignore (illustrative) + /// Option::None::<()> + /// // ^^^^ permitted to have generic args + /// + /// // ==> [GenericPathSegment(Option_def_id, 1)] + /// + /// Option::<()>::None + /// // ^^^^^^ ^^^^ *not* permitted to have generic args + /// // permitted to have generic args + /// + /// // ==> [GenericPathSegment(Option_def_id, 0)] + /// ``` + // FIXME(eddyb, varkor) handle type paths here too, not just value ones. + pub fn probe_generic_path_segments( + &self, + segments: &[hir::PathSegment<'_>], + self_ty: Option>, + kind: DefKind, + def_id: DefId, + span: Span, + ) -> Vec { + // We need to extract the generic arguments supplied by the user in + // the path `path`. Due to the current setup, this is a bit of a + // tricky process; the problem is that resolve only tells us the + // end-point of the path resolution, and not the intermediate steps. + // Luckily, we can (at least for now) deduce the intermediate steps + // just from the end-point. + // + // There are basically five cases to consider: + // + // 1. Reference to a constructor of a struct: + // + // struct Foo(...) + // + // In this case, the generic arguments are declared in the type space. + // + // 2. Reference to a constructor of an enum variant: + // + // enum E { Foo(...) } + // + // In this case, the generic arguments are defined in the type space, + // but may be specified either on the type or the variant. + // + // 3. Reference to a free function or constant: + // + // fn foo() {} + // + // In this case, the path will again always have the form + // `a::b::foo::` where only the final segment should have generic + // arguments. However, in this case, those arguments are declared on + // a value, and hence are in the value space. + // + // 4. Reference to an associated function or constant: + // + // impl SomeStruct { + // fn foo(...) {} + // } + // + // Here we can have a path like `a::b::SomeStruct::::foo::`, + // in which case generic arguments may appear in two places. The + // penultimate segment, `SomeStruct::`, contains generic arguments + // in the type space, and the final segment, `foo::` contains + // generic arguments in value space. + // + // The first step then is to categorize the segments appropriately. + + let tcx = self.tcx(); + + assert!(!segments.is_empty()); + let last = segments.len() - 1; + + let mut generic_segments = vec![]; + + match kind { + // Case 1. Reference to a struct constructor. + DefKind::Ctor(CtorOf::Struct, ..) => { + // Everything but the final segment should have no + // parameters at all. + let generics = tcx.generics_of(def_id); + // Variant and struct constructors use the + // generics of their parent type definition. + let generics_def_id = generics.parent.unwrap_or(def_id); + generic_segments.push(GenericPathSegment(generics_def_id, last)); + } + + // Case 2. Reference to a variant constructor. + DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => { + let (generics_def_id, index) = if let Some(self_ty) = self_ty { + let adt_def = self.probe_adt(span, self_ty).unwrap(); + debug_assert!(adt_def.is_enum()); + (adt_def.did(), last) + } else if last >= 1 && segments[last - 1].args.is_some() { + // Everything but the penultimate segment should have no + // parameters at all. + let mut def_id = def_id; + + // `DefKind::Ctor` -> `DefKind::Variant` + if let DefKind::Ctor(..) = kind { + def_id = tcx.parent(def_id); + } + + // `DefKind::Variant` -> `DefKind::Enum` + let enum_def_id = tcx.parent(def_id); + (enum_def_id, last - 1) + } else { + // FIXME: lint here recommending `Enum::<...>::Variant` form + // instead of `Enum::Variant::<...>` form. + + // Everything but the final segment should have no + // parameters at all. + let generics = tcx.generics_of(def_id); + // Variant and struct constructors use the + // generics of their parent type definition. + (generics.parent.unwrap_or(def_id), last) + }; + generic_segments.push(GenericPathSegment(generics_def_id, index)); + } + + // Case 3. Reference to a top-level value. + DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } => { + generic_segments.push(GenericPathSegment(def_id, last)); + } + + // Case 4. Reference to a method or associated const. + DefKind::AssocFn | DefKind::AssocConst => { + if segments.len() >= 2 { + let generics = tcx.generics_of(def_id); + generic_segments.push(GenericPathSegment(generics.parent.unwrap(), last - 1)); + } + generic_segments.push(GenericPathSegment(def_id, last)); + } + + kind => bug!("unexpected definition kind {:?} for {:?}", kind, def_id), + } + + debug!(?generic_segments); + + generic_segments + } + + /// Lower a type `Path` to a type. + #[instrument(level = "debug", skip_all)] + pub fn lower_path( + &self, + opt_self_ty: Option>, + path: &hir::Path<'tcx>, + hir_id: HirId, + permit_variants: bool, + ) -> Ty<'tcx> { + debug!(?path.res, ?opt_self_ty, ?path.segments); + let tcx = self.tcx(); + + let span = path.span; + match path.res { + Res::Def(DefKind::OpaqueTy, did) => { + // Check for desugared `impl Trait`. + assert!(tcx.is_type_alias_impl_trait(did)); + let item_segment = path.segments.split_last().unwrap(); + let _ = self + .prohibit_generic_args(item_segment.1.iter(), GenericsArgsErrExtend::OpaqueTy); + let args = self.lower_generic_args_of_path_segment(span, did, item_segment.0); + Ty::new_opaque(tcx, did, args) + } + Res::Def( + DefKind::Enum + | DefKind::TyAlias + | DefKind::Struct + | DefKind::Union + | DefKind::ForeignTy, + did, + ) => { + assert_eq!(opt_self_ty, None); + let _ = self.prohibit_generic_args( + path.segments.split_last().unwrap().1.iter(), + GenericsArgsErrExtend::None, + ); + self.lower_path_segment(span, did, path.segments.last().unwrap()) + } + Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { + // Lower "variant type" as if it were a real type. + // The resulting `Ty` is type of the variant's enum for now. + assert_eq!(opt_self_ty, None); + + let generic_segments = + self.probe_generic_path_segments(path.segments, None, kind, def_id, span); + let indices: FxHashSet<_> = + generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); + let _ = self.prohibit_generic_args( + path.segments.iter().enumerate().filter_map(|(index, seg)| { + if !indices.contains(&index) { Some(seg) } else { None } + }), + GenericsArgsErrExtend::DefVariant, + ); + + let GenericPathSegment(def_id, index) = generic_segments.last().unwrap(); + self.lower_path_segment(span, *def_id, &path.segments[*index]) + } + Res::Def(DefKind::TyParam, def_id) => { + assert_eq!(opt_self_ty, None); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::TyParam(def_id), + ); + self.lower_ty_param(hir_id) + } + Res::SelfTyParam { .. } => { + // `Self` in trait or type alias. + assert_eq!(opt_self_ty, None); + let _ = self.prohibit_generic_args( + path.segments.iter(), + if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments { + GenericsArgsErrExtend::SelfTyParam( + ident.span.shrink_to_hi().to(args.span_ext), + ) + } else { + GenericsArgsErrExtend::None + }, + ); + tcx.types.self_param + } + Res::SelfTyAlias { alias_to: def_id, forbid_generic, .. } => { + // `Self` in impl (we know the concrete type). + assert_eq!(opt_self_ty, None); + // Try to evaluate any array length constants. + let ty = tcx.at(span).type_of(def_id).instantiate_identity(); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::SelfTyAlias { def_id, span }, + ); + // HACK(min_const_generics): Forbid generic `Self` types + // here as we can't easily do that during nameres. + // + // We do this before normalization as we otherwise allow + // ```rust + // trait AlwaysApplicable { type Assoc; } + // impl AlwaysApplicable for T { type Assoc = usize; } + // + // trait BindsParam { + // type ArrayTy; + // } + // impl BindsParam for ::Assoc { + // type ArrayTy = [u8; Self::MAX]; + // } + // ``` + // Note that the normalization happens in the param env of + // the anon const, which is empty. This is why the + // `AlwaysApplicable` impl needs a `T: ?Sized` bound for + // this to compile if we were to normalize here. + if forbid_generic && ty.has_param() { + let mut err = tcx.dcx().struct_span_err( + path.span, + "generic `Self` types are currently not permitted in anonymous constants", + ); + if let Some(hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Impl(impl_), + .. + })) = tcx.hir().get_if_local(def_id) + { + err.span_note(impl_.self_ty.span, "not a concrete type"); + } + let reported = err.emit(); + self.set_tainted_by_errors(reported); + Ty::new_error(tcx, reported) + } else { + ty + } + } + Res::Def(DefKind::AssocTy, def_id) => { + debug_assert!(path.segments.len() >= 2); + let _ = self.prohibit_generic_args( + path.segments[..path.segments.len() - 2].iter(), + GenericsArgsErrExtend::None, + ); + // HACK: until we support ``, assume all of them are. + let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) { + ty::BoundConstness::ConstIfConst + } else { + ty::BoundConstness::NotConst + }; + self.lower_qpath( + span, + opt_self_ty, + def_id, + &path.segments[path.segments.len() - 2], + path.segments.last().unwrap(), + constness, + ) + } + Res::PrimTy(prim_ty) => { + assert_eq!(opt_self_ty, None); + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::PrimTy(prim_ty), + ); + match prim_ty { + hir::PrimTy::Bool => tcx.types.bool, + hir::PrimTy::Char => tcx.types.char, + hir::PrimTy::Int(it) => Ty::new_int(tcx, ty::int_ty(it)), + hir::PrimTy::Uint(uit) => Ty::new_uint(tcx, ty::uint_ty(uit)), + hir::PrimTy::Float(ft) => Ty::new_float(tcx, ty::float_ty(ft)), + hir::PrimTy::Str => tcx.types.str_, + } + } + Res::Err => { + let e = self + .tcx() + .dcx() + .span_delayed_bug(path.span, "path with `Res::Err` but no error emitted"); + self.set_tainted_by_errors(e); + Ty::new_error(self.tcx(), e) + } + Res::Def(..) => { + assert_eq!( + path.segments.get(0).map(|seg| seg.ident.name), + Some(kw::SelfUpper), + "only expected incorrect resolution for `Self`" + ); + Ty::new_error( + self.tcx(), + self.tcx().dcx().span_delayed_bug(span, "incorrect resolution for `Self`"), + ) + } + _ => span_bug!(span, "unexpected resolution: {:?}", path.res), + } + } + + /// Lower a type parameter from the HIR to our internal notion of a type. + /// + /// Early-bound type parameters get lowered to [`ty::Param`] + /// and late-bound ones to [`ty::Bound`]. + pub(crate) fn lower_ty_param(&self, hir_id: HirId) -> Ty<'tcx> { + let tcx = self.tcx(); + match tcx.named_bound_var(hir_id) { + Some(rbv::ResolvedArg::LateBound(debruijn, index, def_id)) => { + let name = tcx.item_name(def_id); + let br = ty::BoundTy { + var: ty::BoundVar::from_u32(index), + kind: ty::BoundTyKind::Param(def_id, name), + }; + Ty::new_bound(tcx, debruijn, br) + } + Some(rbv::ResolvedArg::EarlyBound(def_id)) => { + let def_id = def_id.expect_local(); + let item_def_id = tcx.hir().ty_param_owner(def_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; + Ty::new_param(tcx, index, tcx.hir().ty_param_name(def_id)) + } + Some(rbv::ResolvedArg::Error(guar)) => Ty::new_error(tcx, guar), + arg => bug!("unexpected bound var resolution for {hir_id:?}: {arg:?}"), + } + } + + /// Lower a const parameter from the HIR to our internal notion of a constant. + /// + /// Early-bound const parameters get lowered to [`ty::ConstKind::Param`] + /// and late-bound ones to [`ty::ConstKind::Bound`]. + pub(crate) fn lower_const_param(&self, hir_id: HirId, param_ty: Ty<'tcx>) -> Const<'tcx> { + let tcx = self.tcx(); + match tcx.named_bound_var(hir_id) { + Some(rbv::ResolvedArg::EarlyBound(def_id)) => { + // Find the name and index of the const parameter by indexing the generics of + // the parent item and construct a `ParamConst`. + 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), param_ty) + } + Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => { + ty::Const::new_bound(tcx, debruijn, ty::BoundVar::from_u32(index), param_ty) + } + Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar, param_ty), + arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", hir_id), + } + } + + /// Lower a type from the HIR to our internal notion of a type. + pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + self.lower_ty_common(hir_ty, false, false) + } + + /// Lower a type inside of a path from the HIR to our internal notion of a type. + pub fn lower_ty_in_path(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + self.lower_ty_common(hir_ty, false, true) + } + + fn check_delegation_constraints(&self, sig_id: DefId, span: Span, emit: bool) -> bool { + let mut error_occured = false; + let sig_span = self.tcx().def_span(sig_id); + let mut try_emit = |descr| { + if emit { + self.tcx().dcx().emit_err(crate::errors::NotSupportedDelegation { + span, + descr, + callee_span: sig_span, + }); + } + error_occured = true; + }; + + if let Some(node) = self.tcx().hir().get_if_local(sig_id) + && let Some(decl) = node.fn_decl() + && let hir::FnRetTy::Return(ty) = decl.output + && let hir::TyKind::InferDelegation(_, _) = ty.kind + { + try_emit("recursive delegation"); + } + + let sig_generics = self.tcx().generics_of(sig_id); + let parent = self.tcx().parent(self.item_def_id()); + let parent_generics = self.tcx().generics_of(parent); + + let parent_is_trait = (self.tcx().def_kind(parent) == DefKind::Trait) as usize; + let sig_has_self = sig_generics.has_self as usize; + + if sig_generics.count() > sig_has_self || parent_generics.count() > parent_is_trait { + try_emit("delegation with early bound generics"); + } + + // There is no way to instantiate `Self` param for caller if + // 1. callee is a trait method + // 2. delegation item isn't an associative item + if let DefKind::AssocFn = self.tcx().def_kind(sig_id) + && let DefKind::Fn = self.tcx().def_kind(self.item_def_id()) + && self.tcx().associated_item(sig_id).container + == ty::AssocItemContainer::TraitContainer + { + try_emit("delegation to a trait method from a free function"); + } + + error_occured + } + + fn lower_delegation_ty( + &self, + sig_id: DefId, + idx: hir::InferDelegationKind, + span: Span, + ) -> Ty<'tcx> { + if self.check_delegation_constraints(sig_id, span, idx == hir::InferDelegationKind::Output) + { + let e = self.tcx().dcx().span_delayed_bug(span, "not supported delegation case"); + self.set_tainted_by_errors(e); + return Ty::new_error(self.tcx(), e); + }; + let sig = self.tcx().fn_sig(sig_id); + let sig_generics = self.tcx().generics_of(sig_id); + + let parent = self.tcx().parent(self.item_def_id()); + let parent_def_kind = self.tcx().def_kind(parent); + + let sig = if let DefKind::Impl { .. } = parent_def_kind + && sig_generics.has_self + { + // Generic params can't be here except the trait self type. + // They are not supported yet. + assert_eq!(sig_generics.count(), 1); + assert_eq!(self.tcx().generics_of(parent).count(), 0); + + let self_ty = self.tcx().type_of(parent).instantiate_identity(); + let generic_self_ty = ty::GenericArg::from(self_ty); + let args = self.tcx().mk_args_from_iter(std::iter::once(generic_self_ty)); + sig.instantiate(self.tcx(), args) + } else { + sig.instantiate_identity() + }; + + // Bound vars are also inherited from `sig_id`. + // They will be rebound later in `lower_fn_ty`. + let sig = sig.skip_binder(); + + match idx { + hir::InferDelegationKind::Input(id) => sig.inputs()[id], + hir::InferDelegationKind::Output => sig.output(), + } + } + + /// Lower a type from the HIR to our internal notion of a type given some extra data for diagnostics. + /// + /// Extra diagnostic data: + /// + /// 1. `borrowed`: Whether trait object types are borrowed like in `&dyn Trait`. + /// Used to avoid emitting redundant errors. + /// 2. `in_path`: Whether the type appears inside of a path. + /// Used to provide correct diagnostics for bare trait object types. + #[instrument(level = "debug", skip(self), ret)] + fn lower_ty_common(&self, hir_ty: &hir::Ty<'tcx>, borrowed: bool, in_path: bool) -> Ty<'tcx> { + let tcx = self.tcx(); + + let result_ty = match &hir_ty.kind { + hir::TyKind::InferDelegation(sig_id, idx) => { + self.lower_delegation_ty(*sig_id, *idx, hir_ty.span) + } + hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.lower_ty(ty)), + hir::TyKind::Ptr(mt) => Ty::new_ptr(tcx, self.lower_ty(mt.ty), mt.mutbl), + hir::TyKind::Ref(region, mt) => { + let r = self.lower_lifetime(region, None); + debug!(?r); + let t = self.lower_ty_common(mt.ty, true, false); + Ty::new_ref(tcx, r, t, mt.mutbl) + } + hir::TyKind::Never => tcx.types.never, + hir::TyKind::Tup(fields) => { + Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.lower_ty(t))) + } + hir::TyKind::AnonAdt(item_id) => { + let _guard = debug_span!("AnonAdt"); + + let did = item_id.owner_id.def_id; + let adt_def = tcx.adt_def(did); + + let args = ty::GenericArgs::for_item(tcx, did.to_def_id(), |param, _| { + tcx.mk_param_from_def(param) + }); + debug!(?args); + + Ty::new_adt(tcx, adt_def, tcx.mk_args(args)) + } + hir::TyKind::BareFn(bf) => { + require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, hir_ty.span); + + Ty::new_fn_ptr( + tcx, + self.lower_fn_ty( + hir_ty.hir_id, + bf.unsafety, + bf.abi, + bf.decl, + None, + Some(hir_ty), + ), + ) + } + hir::TyKind::TraitObject(bounds, lifetime, repr) => { + self.prohibit_or_lint_bare_trait_object_ty(hir_ty, in_path); + + let repr = match repr { + TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, + TraitObjectSyntax::DynStar => ty::DynStar, + }; + self.lower_trait_object_ty( + hir_ty.span, + hir_ty.hir_id, + bounds, + lifetime, + borrowed, + repr, + ) + } + hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => { + debug!(?maybe_qself, ?path); + let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); + self.lower_path(opt_self_ty, path, hir_ty.hir_id, false) + } + &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => { + let opaque_ty = tcx.hir().item(item_id); + + match opaque_ty.kind { + hir::ItemKind::OpaqueTy(&hir::OpaqueTy { .. }) => { + let local_def_id = item_id.owner_id.def_id; + // If this is an RPITIT and we are using the new RPITIT lowering scheme, we + // generate the def_id of an associated type for the trait and return as + // type a projection. + let def_id = if in_trait { + tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id() + } else { + local_def_id.to_def_id() + }; + self.lower_opaque_ty(def_id, lifetimes, in_trait) + } + ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), + } + } + hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => { + debug!(?qself, ?segment); + let ty = self.lower_ty_common(qself, false, true); + self.lower_assoc_path(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false) + .map(|(ty, _, _)| ty) + .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) + } + &hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => { + let def_id = tcx.require_lang_item(lang_item, Some(span)); + let (args, _) = self.lower_generic_args_of_path( + span, + def_id, + &[], + &hir::PathSegment::invalid(), + None, + ty::BoundConstness::NotConst, + ); + tcx.at(span).type_of(def_id).instantiate(tcx, args) + } + hir::TyKind::Array(ty, length) => { + let length = match length { + hir::ArrayLen::Infer(inf) => self.ct_infer(tcx.types.usize, None, inf.span), + hir::ArrayLen::Body(constant) => { + ty::Const::from_anon_const(tcx, constant.def_id) + } + }; + + Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) + } + hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(), + hir::TyKind::Infer => { + // Infer also appears as the type of arguments or return + // values in an ExprKind::Closure, or as + // the type of local variables. Both of these cases are + // handled specially and will not descend into this routine. + self.ty_infer(None, hir_ty.span) + } + hir::TyKind::Pat(ty, pat) => { + let ty = self.lower_ty(ty); + let pat_ty = match pat.kind { + hir::PatKind::Wild => { + let err = tcx.dcx().emit_err(WildPatTy { span: pat.span }); + Ty::new_error(tcx, err) + } + hir::PatKind::Range(start, end, include_end) => { + let expr_to_const = |expr: &'tcx hir::Expr<'tcx>| -> ty::Const<'tcx> { + let (expr, neg) = match expr.kind { + hir::ExprKind::Unary(hir::UnOp::Neg, negated) => { + (negated, Some((expr.hir_id, expr.span))) + } + _ => (expr, None), + }; + let c = match &expr.kind { + hir::ExprKind::Lit(lit) => { + let lit_input = + LitToConstInput { lit: &lit.node, ty, neg: neg.is_some() }; + match tcx.lit_to_const(lit_input) { + Ok(c) => c, + Err(LitToConstError::Reported(err)) => { + ty::Const::new_error(tcx, err, ty) + } + Err(LitToConstError::TypeError) => todo!(), + } + } + + hir::ExprKind::Path(hir::QPath::Resolved( + _, + &hir::Path { + res: Res::Def(DefKind::ConstParam, def_id), .. + }, + )) => { + let ty = tcx + .type_of(def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + self.lower_const_param(expr.hir_id, ty) + } + + _ => { + let err = tcx + .dcx() + .emit_err(crate::errors::NonConstRange { span: expr.span }); + ty::Const::new_error(tcx, err, ty) + } + }; + self.record_ty(expr.hir_id, c.ty(), expr.span); + if let Some((id, span)) = neg { + self.record_ty(id, c.ty(), span); + } + c + }; + + let start = start.map(expr_to_const); + let end = end.map(expr_to_const); + + let include_end = match include_end { + hir::RangeEnd::Included => true, + hir::RangeEnd::Excluded => false, + }; + + let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end }); + Ty::new_pat(tcx, ty, pat) + } + hir::PatKind::Err(e) => Ty::new_error(tcx, e), + _ => Ty::new_error_with_message( + tcx, + pat.span, + format!("unsupported pattern for pattern type: {pat:#?}"), + ), + }; + self.record_ty(pat.hir_id, ty, pat.span); + pat_ty + } + hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), + }; + + self.record_ty(hir_ty.hir_id, result_ty, hir_ty.span); + result_ty + } + + /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. + #[instrument(level = "debug", skip_all, ret)] + fn lower_opaque_ty( + &self, + def_id: DefId, + lifetimes: &[hir::GenericArg<'_>], + in_trait: bool, + ) -> Ty<'tcx> { + debug!(?def_id, ?lifetimes); + let tcx = self.tcx(); + + let generics = tcx.generics_of(def_id); + debug!(?generics); + + let args = ty::GenericArgs::for_item(tcx, def_id, |param, _| { + // We use `generics.count() - lifetimes.len()` here instead of `generics.parent_count` + // since return-position impl trait in trait squashes all of the generics from its source fn + // into its own generics, so the opaque's "own" params isn't always just lifetimes. + if let Some(i) = (param.index as usize).checked_sub(generics.count() - lifetimes.len()) + { + // Resolve our own lifetime parameters. + let GenericParamDefKind::Lifetime { .. } = param.kind else { + span_bug!( + tcx.def_span(param.def_id), + "only expected lifetime for opaque's own generics, got {:?}", + param.kind + ); + }; + let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else { + bug!( + "expected lifetime argument for param {param:?}, found {:?}", + &lifetimes[i] + ) + }; + self.lower_lifetime(lifetime, None).into() + } else { + tcx.mk_param_from_def(param) + } + }); + debug!(?args); + + if in_trait { + Ty::new_projection(tcx, def_id, args) + } else { + Ty::new_opaque(tcx, def_id, args) + } + } + + pub fn lower_arg_ty(&self, ty: &hir::Ty<'tcx>, expected_ty: Option>) -> Ty<'tcx> { + match ty.kind { + hir::TyKind::Infer if let Some(expected_ty) = expected_ty => { + self.record_ty(ty.hir_id, expected_ty, ty.span); + expected_ty + } + _ => self.lower_ty(ty), + } + } + + /// Lower a function type from the HIR to our internal notion of a function signature. + #[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)] + pub fn lower_fn_ty( + &self, + hir_id: HirId, + unsafety: hir::Unsafety, + abi: abi::Abi, + decl: &hir::FnDecl<'tcx>, + generics: Option<&hir::Generics<'_>>, + hir_ty: Option<&hir::Ty<'_>>, + ) -> ty::PolyFnSig<'tcx> { + let tcx = self.tcx(); + let bound_vars = tcx.late_bound_vars(hir_id); + debug!(?bound_vars); + + // We proactively collect all the inferred type params to emit a single error per fn def. + let mut visitor = HirPlaceholderCollector::default(); + let mut infer_replacements = vec![]; + + if let Some(generics) = generics { + walk_generics(&mut visitor, generics); + } + + let input_tys: Vec<_> = decl + .inputs + .iter() + .enumerate() + .map(|(i, a)| { + if let hir::TyKind::Infer = a.kind + && !self.allow_infer() + { + if let Some(suggested_ty) = + self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) + { + infer_replacements.push((a.span, suggested_ty.to_string())); + return Ty::new_error_with_message( + self.tcx(), + a.span, + suggested_ty.to_string(), + ); + } + } + + // Only visit the type looking for `_` if we didn't fix the type above + visitor.visit_ty(a); + self.lower_arg_ty(a, None) + }) + .collect(); + + let output_ty = match decl.output { + hir::FnRetTy::Return(output) => { + if let hir::TyKind::Infer = output.kind + && !self.allow_infer() + && let Some(suggested_ty) = + self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) + { + infer_replacements.push((output.span, suggested_ty.to_string())); + Ty::new_error_with_message(self.tcx(), output.span, suggested_ty.to_string()) + } else { + visitor.visit_ty(output); + self.lower_ty(output) + } + } + hir::FnRetTy::DefaultReturn(..) => tcx.types.unit, + }; + + debug!(?output_ty); + + let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi); + let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); + + if !self.allow_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) { + // We always collect the spans for placeholder types when evaluating `fn`s, but we + // only want to emit an error complaining about them if infer types (`_`) are not + // allowed. `allow_infer` gates this behavior. We check for the presence of + // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. + + let mut diag = crate::collect::placeholder_type_error_diag( + tcx, + generics, + visitor.0, + infer_replacements.iter().map(|(s, _)| *s).collect(), + true, + hir_ty, + "function", + ); + + if !infer_replacements.is_empty() { + diag.multipart_suggestion( + format!( + "try replacing `_` with the type{} in the corresponding trait method signature", + rustc_errors::pluralize!(infer_replacements.len()), + ), + infer_replacements, + Applicability::MachineApplicable, + ); + } + + self.set_tainted_by_errors(diag.emit()); + } + + // Find any late-bound regions declared in return type that do + // not appear in the arguments. These are not well-formed. + // + // Example: + // for<'a> fn() -> &'a str <-- 'a is bad + // for<'a> fn(&'a String) -> &'a str <-- 'a is ok + let inputs = bare_fn_ty.inputs(); + let late_bound_in_args = + tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned())); + let output = bare_fn_ty.output(); + let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output); + + self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { + struct_span_code_err!( + tcx.dcx(), + decl.output.span(), + E0581, + "return type references {}, which is not constrained by the fn input types", + br_name + ) + }); + + bare_fn_ty + } + + /// Given a fn_hir_id for a impl function, suggest the type that is found on the + /// corresponding function in the trait that the impl implements, if it exists. + /// If arg_idx is Some, then it corresponds to an input type index, otherwise it + /// corresponds to the return type. + fn suggest_trait_fn_ty_for_impl_fn_infer( + &self, + fn_hir_id: HirId, + arg_idx: Option, + ) -> Option> { + let tcx = self.tcx(); + let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) = + tcx.hir_node(fn_hir_id) + else { + return None; + }; + let i = tcx.parent_hir_node(fn_hir_id).expect_item().expect_impl(); + + let trait_ref = self.lower_impl_trait_ref(i.of_trait.as_ref()?, self.lower_ty(i.self_ty)); + + let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( + tcx, + *ident, + ty::AssocKind::Fn, + trait_ref.def_id, + )?; + + let fn_sig = tcx.fn_sig(assoc.def_id).instantiate( + tcx, + trait_ref.args.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)), + ); + let fn_sig = tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), fn_sig); + + Some(if let Some(arg_idx) = arg_idx { + *fn_sig.inputs().get(arg_idx)? + } else { + fn_sig.output() + }) + } + + #[instrument(level = "trace", skip(self, generate_err))] + fn validate_late_bound_regions( + &self, + constrained_regions: FxHashSet, + referenced_regions: FxHashSet, + generate_err: impl Fn(&str) -> Diag<'tcx>, + ) { + for br in referenced_regions.difference(&constrained_regions) { + let br_name = match *br { + ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon | ty::BrEnv => { + "an anonymous lifetime".to_string() + } + ty::BrNamed(_, name) => format!("lifetime `{name}`"), + }; + + let mut err = generate_err(&br_name); + + if let ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon = *br { + // The only way for an anonymous lifetime to wind up + // in the return type but **also** be unconstrained is + // if it only appears in "associated types" in the + // input. See #47511 and #62200 for examples. In this case, + // though we can easily give a hint that ought to be + // relevant. + err.note( + "lifetimes appearing in an associated or opaque type are not considered constrained", + ); + err.note("consider introducing a named lifetime parameter"); + } + + self.set_tainted_by_errors(err.emit()); + } + } + + /// Given the bounds on an object, determines what single region bound (if any) we can + /// use to summarize this type. + /// + /// The basic idea is that we will use the bound the user + /// provided, if they provided one, and otherwise search the supertypes of trait bounds + /// for region bounds. It may be that we can derive no bound at all, in which case + /// we return `None`. + #[instrument(level = "debug", skip(self, span), ret)] + fn compute_object_lifetime_bound( + &self, + span: Span, + existential_predicates: &'tcx ty::List>, + ) -> Option> // if None, use the default + { + let tcx = self.tcx(); + + // No explicit region bound specified. Therefore, examine trait + // bounds and see if we can derive region bounds from those. + let derived_region_bounds = object_region_bounds(tcx, existential_predicates); + + // If there are no derived region bounds, then report back that we + // can find no region bound. The caller will use the default. + if derived_region_bounds.is_empty() { + return None; + } + + // If any of the derived region bounds are 'static, that is always + // the best choice. + if derived_region_bounds.iter().any(|r| r.is_static()) { + return Some(tcx.lifetimes.re_static); + } + + // Determine whether there is exactly one unique region in the set + // of derived region bounds. If so, use that. Otherwise, report an + // error. + let r = derived_region_bounds[0]; + if derived_region_bounds[1..].iter().any(|r1| r != *r1) { + self.set_tainted_by_errors(tcx.dcx().emit_err(AmbiguousLifetimeBound { span })); + } + Some(r) + } +} + +fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { + match kind { + ty::AssocKind::Fn => "function", + ty::AssocKind::Const => "constant", + ty::AssocKind::Type => "type", + } +} diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs similarity index 79% rename from compiler/rustc_hir_analysis/src/astconv/object_safety.rs rename to compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index 70c60e3994188..97ba946b7e013 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -1,24 +1,26 @@ -use crate::astconv::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds}; use crate::bounds::Bounds; -use crate::errors::TraitObjectDeclaredWithNoTraits; +use crate::hir_ty_lowering::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{DynKind, ToPredicate}; -use rustc_span::Span; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; -use rustc_trait_selection::traits::{self, astconv_object_safety_violations}; +use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations}; use smallvec::{smallvec, SmallVec}; -use super::AstConv; +use super::HirTyLowerer; -impl<'tcx> dyn AstConv<'tcx> + '_ { - pub(super) fn conv_object_ty_poly_trait_ref( +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Lower a trait object type from the HIR to our internal notion of a type. + #[instrument(level = "debug", skip_all, ret)] + pub(super) fn lower_trait_object_ty( &self, span: Span, hir_id: hir::HirId, @@ -37,11 +39,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { correct: Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), .. - } = self.instantiate_poly_trait_ref( + } = self.lower_poly_trait_ref( &trait_bound.trait_ref, trait_bound.span, ty::BoundConstness::NotConst, - ty::ImplPolarity::Positive, + ty::PredicatePolarity::Positive, dummy_self, &mut bounds, // True so we don't populate `bounds` with associated type bounds, even @@ -58,7 +60,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let bound_pred = pred.kind(); match bound_pred.skip_binder() { ty::ClauseKind::Trait(trait_pred) => { - assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive); + assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); trait_bounds.push((bound_pred.rebind(trait_pred.trait_ref), span)); } ty::ClauseKind::Projection(proj) => { @@ -84,47 +86,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); if regular_traits.len() > 1 { - let first_trait = ®ular_traits[0]; - let additional_trait = ®ular_traits[1]; - let mut err = struct_span_code_err!( - tcx.dcx(), - additional_trait.bottom().1, - E0225, - "only auto traits can be used as additional traits in a trait object" - ); - additional_trait.label_with_exp_info( - &mut err, - "additional non-auto trait", - "additional use", - ); - first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); - err.help(format!( - "consider creating a new trait with all of these as supertraits and using that \ - trait here instead: `trait NewTrait: {} {{}}`", - regular_traits - .iter() - // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... - .map(|t| t.trait_ref().print_only_trait_path().to_string()) - .collect::>() - .join(" + "), - )); - err.note( - "auto-traits like `Send` and `Sync` are traits that have special properties; \ - for more information on them, visit \ - ", - ); - self.set_tainted_by_errors(err.emit()); - } - - if regular_traits.is_empty() && auto_traits.is_empty() { - let trait_alias_span = trait_bounds - .iter() - .map(|&(trait_ref, _)| trait_ref.def_id()) - .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) - .map(|trait_ref| tcx.def_span(trait_ref)); - let reported = - tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); - self.set_tainted_by_errors(reported); + let _ = self.report_trait_object_addition_traits_error(®ular_traits); + } else if regular_traits.is_empty() && auto_traits.is_empty() { + let reported = self.report_trait_object_with_no_traits_error(span, &trait_bounds); return Ty::new_error(tcx, reported); } @@ -133,7 +97,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // to avoid ICEs. for item in ®ular_traits { let object_safety_violations = - astconv_object_safety_violations(tcx, item.trait_ref().def_id()); + hir_ty_lowering_object_safety_violations(tcx, item.trait_ref().def_id()); if !object_safety_violations.is_empty() { let reported = report_object_safety_error( tcx, @@ -156,7 +120,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { for (base_trait_ref, span) in regular_traits_refs_spans { let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx); for pred in traits::elaborate(tcx, [base_pred]).filter_only_self() { - debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred); + debug!("observing object predicate `{pred:?}`"); let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { @@ -231,7 +195,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id)); } - self.complain_about_missing_associated_types( + self.complain_about_missing_assoc_tys( associated_types, potential_assoc_types, hir_trait_bounds, @@ -243,8 +207,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // the bounds let mut duplicates = FxHashSet::default(); auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id())); - debug!("regular_traits: {:?}", regular_traits); - debug!("auto_traits: {:?}", auto_traits); + debug!(?regular_traits); + debug!(?auto_traits); // Erase the `dummy_self` (`trait_object_dummy_self`) used above. let existential_trait_refs = regular_traits.iter().map(|i| { @@ -265,12 +229,17 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { if arg == dummy_self.into() { let param = &generics.params[index]; missing_type_params.push(param.name); - return Ty::new_misc_error(tcx).into(); + Ty::new_misc_error(tcx).into() } else if arg.walk().any(|arg| arg == dummy_self.into()) { references_self = true; - return Ty::new_misc_error(tcx).into(); + let guar = tcx.dcx().span_delayed_bug( + span, + "trait object trait bounds reference `Self`", + ); + replace_dummy_self_with_error(tcx, arg, guar) + } else { + arg } - arg }) .collect(); let args = tcx.mk_args(&args); @@ -325,18 +294,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let guar = tcx .dcx() .span_delayed_bug(span, "trait object projection bounds reference `Self`"); - let args: Vec<_> = b - .projection_ty - .args - .iter() - .map(|arg| { - if arg.walk().any(|arg| arg == dummy_self.into()) { - return Ty::new_error(tcx, guar).into(); - } - arg - }) - .collect(); - b.projection_ty.args = tcx.mk_args(&args); + b.projection_ty = replace_dummy_self_with_error(tcx, b.projection_ty, guar); } ty::ExistentialProjection::erase_self_ty(tcx, b) @@ -362,11 +320,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Use explicitly-specified region bound. let region_bound = if !lifetime.is_elided() { - self.ast_region_to_region(lifetime, None) + self.lower_lifetime(lifetime, None) } else { self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { if tcx.named_bound_var(lifetime.hir_id).is_some() { - self.ast_region_to_region(lifetime, None) + self.lower_lifetime(lifetime, None) } else { self.re_infer(None, span).unwrap_or_else(|| { let err = struct_span_code_err!( @@ -389,10 +347,23 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } }) }; - debug!("region_bound: {:?}", region_bound); + debug!(?region_bound); - let ty = Ty::new_dynamic(tcx, existential_predicates, region_bound, representation); - debug!("trait_object_type: {:?}", ty); - ty + Ty::new_dynamic(tcx, existential_predicates, region_bound, representation) } } + +fn replace_dummy_self_with_error<'tcx, T: TypeFoldable>>( + tcx: TyCtxt<'tcx>, + t: T, + guar: ErrorGuaranteed, +) -> T { + t.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| { + if ty == tcx.types.trait_object_dummy_self { Ty::new_error(tcx, guar) } else { ty } + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }) +} diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 2a9101b3808fa..7014b23ff07de 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -68,7 +68,7 @@ fn diagnostic_hir_wf_check<'tcx>( let infcx = self.tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(&infcx); - let tcx_ty = self.icx.to_ty(ty); + let tcx_ty = self.icx.lower_ty(ty); // This visitor can walk into binders, resulting in the `tcx_ty` to // potentially reference escaping bound variables. We simply erase // those here. @@ -163,6 +163,16 @@ fn diagnostic_hir_wf_check<'tcx>( kind: hir::GenericParamKind::Type { default: Some(ty), .. }, .. }) => vec![*ty], + hir::Node::AnonConst(_) + if let Some(const_param_id) = + tcx.hir().opt_const_param_default_param_def_id(hir_id) + && let hir::Node::GenericParam(hir::GenericParam { + kind: hir::GenericParamKind::Const { ty, .. }, + .. + }) = tcx.hir_node_by_def_id(const_param_id) => + { + vec![*ty] + } ref node => bug!("Unexpected node {:?}", node), }, WellFormedLoc::Param { function: _, param_idx } => { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 19ab2045cc553..66b86c9eb02a3 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -30,8 +30,8 @@ several major phases: The type checker is defined into various submodules which are documented independently: -- astconv: converts the AST representation of types - into the `ty` representation. +- hir_ty_lowering: lowers type-system entities from the [HIR][hir] to the + [`rustc_middle::ty`] representation. - collect: computes the types of each top-level item and enters them into the `tcx.types` table for later use. @@ -63,12 +63,10 @@ This API is completely unstable and subject to change. #![feature(rustdoc_internals)] #![allow(internal_features)] #![feature(control_flow_enum)] -#![feature(generic_nonzero)] #![feature(if_let_guard)] #![feature(is_sorted)] #![feature(iter_intersperse)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![feature(never_type)] #![feature(lazy_cell)] #![feature(slice_partition_dedup)] @@ -83,11 +81,11 @@ extern crate rustc_middle; // These are used by Clippy. pub mod check; -pub mod astconv; pub mod autoderef; mod bounds; mod check_unused; mod coherence; +pub mod hir_ty_lowering; // FIXME: This module shouldn't be public. pub mod collect; mod constrained_generic_params; @@ -98,13 +96,12 @@ mod outlives; pub mod structured_errors; mod variance; -use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_middle::middle; +use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; -use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_middle::util; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_span::{symbol::sym, Span}; use rustc_target::spec::abi::Abi; @@ -153,11 +150,11 @@ pub fn provide(providers: &mut Providers) { hir_wf_check::provide(providers); } -pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { +pub fn check_crate(tcx: TyCtxt<'_>) { let _prof_timer = tcx.sess.timer("type_check_crate"); if tcx.features().rustc_attrs { - tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx))?; + let _ = tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx)); } tcx.sess.time("coherence_checking", || { @@ -174,11 +171,11 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { }); if tcx.features().rustc_attrs { - tcx.sess.time("variance_testing", || variance::test::test_variance(tcx))?; + let _ = tcx.sess.time("variance_testing", || variance::test::test_variance(tcx)); } if tcx.features().rustc_attrs { - collect::test_opaque_hidden_types(tcx)?; + let _ = collect::test_opaque_hidden_types(tcx); } // Make sure we evaluate all static and (non-associated) const items, even if unused. @@ -187,7 +184,12 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { let def_kind = tcx.def_kind(item_def_id); match def_kind { DefKind::Static { .. } => tcx.ensure().eval_static_initializer(item_def_id), - DefKind::Const => tcx.ensure().const_eval_poly(item_def_id.into()), + DefKind::Const if tcx.generics_of(item_def_id).params.is_empty() => { + let instance = ty::Instance::new(item_def_id.into(), ty::GenericArgs::empty()); + let cid = GlobalId { instance, promoted: None }; + let param_env = ty::ParamEnv::reveal_all(); + tcx.ensure().eval_to_const_value_raw(param_env.and(cid)); + } _ => (), } }); @@ -208,16 +210,22 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { }); tcx.ensure().check_unused_traits(()); - - Ok(()) } -/// A quasi-deprecated helper used in rustdoc and clippy to get -/// the type from a HIR node. -pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { +/// Lower a [`hir::Ty`] to a [`Ty`]. +/// +///

+pub fn lower_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { // In case there are any projections, etc., find the "environment" // def-ID that will be used to determine the traits/predicates in // scope. This is derived from the enclosing item-like thing. let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id); - collect::ItemCtxt::new(tcx, env_def_id.def_id).to_ty(hir_ty) + collect::ItemCtxt::new(tcx, env_def_id.def_id).lower_ty(hir_ty) } diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 8077acea52e44..d3bb22d715d7b 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -1,14 +1,14 @@ +use rustc_data_structures::fx::FxIndexMap; use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_middle::ty::{self, Region, Ty, TyCtxt}; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::Span; use smallvec::smallvec; -use std::collections::BTreeMap; /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred /// must be added to the struct header. pub(crate) type RequiredPredicates<'tcx> = - BTreeMap, ty::Region<'tcx>>, Span>; + FxIndexMap, ty::Region<'tcx>>, Span>; /// Given a requirement `T: 'a` or `'b: 'a`, deduce the /// outlives_component and add it to `required_predicates` diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 0f5ba26337a15..dcab571eedf56 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -435,6 +435,22 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { &self, num_params_to_take: usize, ) -> String { + let is_in_a_method_call = self + .tcx + .hir() + .parent_iter(self.path_segment.hir_id) + .skip(1) + .find_map(|(_, node)| match node { + hir::Node::Expr(expr) => Some(expr), + _ => None, + }) + .is_some_and(|expr| { + matches!( + expr.kind, + hir::ExprKind::MethodCall(hir::PathSegment { args: Some(_), .. }, ..) + ) + }); + let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig); let is_used_in_input = |def_id| { fn_sig.is_some_and(|fn_sig| { @@ -453,14 +469,17 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { .skip(self.params_offset + self.num_provided_type_or_const_args()) .take(num_params_to_take) .map(|param| match param.kind { - // This is being inferred from the item's inputs, no need to set it. - ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => { - "_".to_string() + // If it's in method call (turbofish), it might be inferred from the expression (e.g. `.collect::>()`) + // If it is being inferred from the item's inputs, no need to set it. + ty::GenericParamDefKind::Type { .. } + if is_in_a_method_call || is_used_in_input(param.def_id) => + { + "_" } - _ => param.name.to_string(), + _ => param.name.as_str(), }) - .collect::>() - .join(", ") + .intersperse(", ") + .collect() } fn get_unbound_associated_types(&self) -> Vec { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 580cdb4a3a209..eeb8b02850521 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -1,6 +1,6 @@ //! Constraint construction and representation //! -//! The second pass over the AST determines the set of constraints. +//! The second pass over the HIR determines the set of constraints. //! We walk the set of items and, for each member, generate new constraints. use hir::def_id::{DefId, LocalDefId}; @@ -236,7 +236,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::FnDef(..) | ty::Coroutine(..) | ty::Closure(..) | ty::CoroutineClosure(..) => { - bug!("Unexpected coroutine/closure type in variance computation"); + bug!("Unexpected unnameable type in variance computation: {ty}"); } ty::Ref(region, ty, mutbl) => { @@ -249,12 +249,26 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_ty(current, typ, variance); } + ty::Pat(typ, pat) => { + match *pat { + ty::PatternKind::Range { start, end, include_end: _ } => { + if let Some(start) = start { + self.add_constraints_from_const(current, start, variance); + } + if let Some(end) = end { + self.add_constraints_from_const(current, end, variance); + } + } + } + self.add_constraints_from_ty(current, typ, variance); + } + ty::Slice(typ) => { self.add_constraints_from_ty(current, typ, variance); } - ty::RawPtr(ref mt) => { - self.add_constraints_from_mt(current, mt, variance); + ty::RawPtr(ty, mutbl) => { + self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance); } ty::Tuple(subtys) => { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index e070db9423df1..285b99c2c69d5 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -9,9 +9,10 @@ use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir as hir; -use rustc_hir::LifetimeParamKind; -use rustc_hir::{BindingAnnotation, ByRef, GenericArg, GenericParam, GenericParamKind, Node, Term}; -use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier}; +use rustc_hir::{ + BindingMode, ByRef, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, + LifetimeParamKind, Node, PatKind, RangeEnd, Term, TraitBoundModifier, +}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::FileName; @@ -20,7 +21,7 @@ use rustc_target::spec::abi::Abi; use std::cell::Cell; use std::vec; -pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: hir::HirId) -> String { +pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: HirId) -> String { to_string(&map, |s| s.print_node(map.hir_node(hir_id))) } @@ -28,7 +29,7 @@ pub enum AnnNode<'a> { Name(&'a Symbol), Block(&'a hir::Block<'a>), Item(&'a hir::Item<'a>), - SubItem(hir::HirId), + SubItem(HirId), Expr(&'a hir::Expr<'a>), Pat(&'a hir::Pat<'a>), Arm(&'a hir::Arm<'a>), @@ -49,10 +50,6 @@ pub trait PpAnn { fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {} } -pub struct NoAnn; - -impl PpAnn for NoAnn {} - impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> { fn nested(&self, state: &mut State<'_>, nested: Nested) { match nested { @@ -69,12 +66,12 @@ impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> { pub struct State<'a> { pub s: pp::Printer, comments: Option>, - attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute], + attrs: &'a dyn Fn(HirId) -> &'a [ast::Attribute], ann: &'a (dyn PpAnn + 'a), } impl<'a> State<'a> { - fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] { + fn attrs(&self, id: HirId) -> &'a [ast::Attribute] { (self.attrs)(id) } @@ -99,6 +96,7 @@ impl<'a> State<'a> { Node::PatField(a) => self.print_patfield(a), Node::Arm(a) => self.print_arm(a), Node::Infer(_) => self.word("_"), + Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident), Node::Block(a) => { // Containing cbox, will be closed by print-block at `}`. self.cbox(INDENT_UNIT); @@ -113,7 +111,7 @@ impl<'a> State<'a> { // `hir_map` to reconstruct their full structure for pretty // printing. Node::Ctor(..) => panic!("cannot print isolated Ctor"), - Node::Local(a) => self.print_local_decl(a), + Node::LetStmt(a) => self.print_local_decl(a), Node::Crate(..) => panic!("cannot print Crate"), Node::WhereBoundPredicate(pred) => { self.print_formal_generic_params(pred.bound_generic_params); @@ -121,7 +119,7 @@ impl<'a> State<'a> { self.print_bounds(":", pred.bounds); } Node::ArrayLenInfer(_) => self.word("_"), - Node::AssocOpaqueTy(..) => unreachable!(), + Node::Synthetic => unreachable!(), Node::Err(_) => self.word("/*ERROR*/"), } } @@ -163,7 +161,7 @@ pub fn print_crate<'a>( krate: &hir::Mod<'_>, filename: FileName, input: String, - attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute], + attrs: &'a dyn Fn(HirId) -> &'a [ast::Attribute], ann: &'a dyn PpAnn, ) -> String { let mut s = State { @@ -190,16 +188,16 @@ where printer.s.eof() } -pub fn ty_to_string(ty: &hir::Ty<'_>) -> String { - to_string(&NoAnn, |s| s.print_type(ty)) +pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String { + to_string(ann, |s| s.print_type(ty)) } -pub fn qpath_to_string(segment: &hir::QPath<'_>) -> String { - to_string(&NoAnn, |s| s.print_qpath(segment, false)) +pub fn qpath_to_string(ann: &dyn PpAnn, segment: &hir::QPath<'_>) -> String { + to_string(ann, |s| s.print_qpath(segment, false)) } -pub fn pat_to_string(pat: &hir::Pat<'_>) -> String { - to_string(&NoAnn, |s| s.print_pat(pat)) +pub fn pat_to_string(ann: &dyn PpAnn, pat: &hir::Pat<'_>) -> String { + to_string(ann, |s| s.print_pat(pat)) } impl<'a> State<'a> { @@ -330,6 +328,11 @@ impl<'a> State<'a> { self.word("_"); } hir::TyKind::AnonAdt(..) => self.word("/* anonymous adt */"), + hir::TyKind::Pat(ty, pat) => { + self.print_type(ty); + self.word(" is "); + self.print_pat(pat); + } } self.end() } @@ -1137,7 +1140,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(); @@ -1387,7 +1390,7 @@ impl<'a> State<'a> { // Print `}`: self.bclose_maybe_open(expr.span, true); } - hir::ExprKind::Let(&hir::Let { pat, ty, init, .. }) => { + hir::ExprKind::Let(&hir::LetExpr { pat, ty, init, .. }) => { self.print_let(pat, ty, init); } hir::ExprKind::If(test, blk, elseopt) => { @@ -1544,7 +1547,7 @@ impl<'a> State<'a> { self.end() } - fn print_local_decl(&mut self, loc: &hir::Local<'_>) { + fn print_local_decl(&mut self, loc: &hir::LetStmt<'_>) { self.print_pat(loc.pat); if let Some(ty) = loc.ty { self.word_space(":"); @@ -1720,13 +1723,16 @@ impl<'a> State<'a> { match pat.kind { PatKind::Wild => self.word("_"), PatKind::Never => self.word("!"), - PatKind::Binding(BindingAnnotation(by_ref, mutbl), _, ident, sub) => { - if by_ref == ByRef::Yes { - self.word_nbsp("ref"); - } + PatKind::Binding(BindingMode(by_ref, mutbl), _, ident, sub) => { if mutbl.is_mut() { self.word_nbsp("mut"); } + if let ByRef::Yes(rmutbl) = by_ref { + self.word_nbsp("ref"); + if rmutbl.is_mut() { + self.word_nbsp("mut"); + } + } self.print_ident(ident); if let Some(p) = sub { self.word("@"); @@ -1808,6 +1814,12 @@ impl<'a> State<'a> { self.pclose(); } } + PatKind::Deref(inner) => { + self.word("deref!"); + self.popen(); + self.print_pat(inner); + self.pclose(); + } PatKind::Ref(inner, mutbl) => { let is_range_inner = matches!(inner.kind, PatKind::Range(..)); self.word("&"); @@ -2314,33 +2326,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/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 220da19a29dc8..0560d0d902a3f 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -46,6 +46,10 @@ hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty` +hir_typeck_dereferencing_mut_binding = dereferencing `mut` binding + .label = `mut` dereferences the type of this binding + .help = this will change in edition 2024 + hir_typeck_expected_default_return_type = expected `()` because of default return type hir_typeck_expected_return_type = expected `{$expected}` because of return type @@ -86,23 +90,33 @@ hir_typeck_invalid_callee = expected function, found {$ty} hir_typeck_lossy_provenance_int2ptr = strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}` .suggestion = use `.with_addr()` to adjust a valid pointer in the same allocation, to this address - .help = if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead + .help = if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead hir_typeck_lossy_provenance_ptr2int = under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}` .suggestion = use `.addr()` to obtain the address of a pointer - .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead - -hir_typeck_method_call_on_unknown_raw_pointee = - cannot call a method on a raw pointer with an unknown pointee type + .help = if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` +hir_typeck_never_type_fallback_flowing_into_unsafe_call = never type fallback affects this call to an `unsafe` function + .help = specify the type explicitly +hir_typeck_never_type_fallback_flowing_into_unsafe_deref = never type fallback affects this raw pointer dereference + .help = specify the type explicitly +hir_typeck_never_type_fallback_flowing_into_unsafe_method = never type fallback affects this call to an `unsafe` method + .help = specify the type explicitly +hir_typeck_never_type_fallback_flowing_into_unsafe_path = never type fallback affects this `unsafe` function + .help = specify the type explicitly +hir_typeck_never_type_fallback_flowing_into_unsafe_union_field = never type fallback affects this union access + .help = specify the type explicitly + hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method -> [true] {""} *[other] {" "}in the current scope } +hir_typeck_note_caller_chooses_ty_for_ty_param = the caller chooses a type for `{$ty_param_name}` which can be different from `{$found_ty}` + hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expected_ty}` to `{$expr_ty}` @@ -120,6 +134,10 @@ hir_typeck_return_stmt_outside_of_fn_body = .encl_body_label = the {$statement_kind} is part of this body... .encl_fn_label = ...not the enclosing function body +hir_typeck_rpit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions + +hir_typeck_rpit_change_return_type = you could change the return type to be a boxed trait object + hir_typeck_rustcall_incorrect_args = functions with the "rust-call" ABI must take a single non-self tuple argument diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 4b3359858f15d..4ff6678fc9144 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -1,17 +1,13 @@ use crate::coercion::{AsCoercionSite, CoerceMany}; use crate::{Diverges, Expectation, FnCtxt, Needs}; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{ - self as hir, - def::{CtorOf, DefKind, Res}, - ExprKind, PatKind, -}; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{self as hir, ExprKind, PatKind}; use rustc_hir_pretty::ty_to_string; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::traits::Obligation; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, }; @@ -70,11 +66,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // us to give better error messages (pointing to a usually better // arm for inconsistent arms or to the whole match when a `()` type // is required). - Expectation::ExpectHasType(ety) if ety != Ty::new_unit(self.tcx) => ety, - _ => self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: expr.span, - }), + Expectation::ExpectHasType(ety) if ety != tcx.types.unit => ety, + _ => self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr.span }), }; CoerceMany::with_coercion_sites(coerce_first, arms) }; @@ -91,10 +84,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arm_ty = self.check_expr_with_expectation(arm.body, expected); all_arms_diverge &= self.diverges.get(); - - let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| { - self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected) - }); + let tail_defines_return_position_impl_trait = + self.return_position_impl_trait_from_match_expectation(orig_expected); let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind { (Some(blk.hir_id), self.find_block_span(blk)) @@ -120,7 +111,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { scrut_span: scrut.span, source: match_src, prior_non_diverging_arms: prior_non_diverging_arms.clone(), - opt_suggest_box_span, + tail_defines_return_position_impl_trait, })), ), }; @@ -243,7 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { Some(ret_coercion) => { let ret_ty = ret_coercion.borrow().expected_ty(); - let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); + let ret_ty = self.infcx.shallow_resolve(ret_ty); self.can_coerce(arm_ty, ret_ty) && prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty)) // The match arms need to unify for the case of `impl Trait`. @@ -317,7 +308,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.note("`if` expressions without `else` evaluate to `()`"); err.help("consider adding an `else` block that evaluates to the expected type"); *error = true; - if let ExprKind::Let(hir::Let { span, pat, init, .. }) = cond_expr.kind + if let ExprKind::Let(hir::LetExpr { span, pat, init, .. }) = cond_expr.kind && let ExprKind::Block(block, _) = then_expr.kind // Refutability checks occur on the MIR, so we approximate it here by checking // if we have an enum with a single variant or a struct in the pattern. @@ -401,14 +392,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return self.get_fn_decl(hir_id).map(|(_, fn_decl, _)| { let (ty, span) = match fn_decl.output { hir::FnRetTy::DefaultReturn(span) => ("()".to_string(), span), - hir::FnRetTy::Return(ty) => (ty_to_string(ty), ty.span), + hir::FnRetTy::Return(ty) => (ty_to_string(&self.tcx, ty), ty.span), }; (span, format!("expected `{ty}` because of this return type")) }); } } } - if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node { + if let hir::Node::LetStmt(hir::LetStmt { ty: Some(_), pat, .. }) = node { return Some((pat.span, "expected because of this assignment".to_string())); } None @@ -422,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else_expr: &'tcx hir::Expr<'tcx>, then_ty: Ty<'tcx>, else_ty: Ty<'tcx>, - opt_suggest_box_span: Option, + tail_defines_return_position_impl_trait: Option, ) -> ObligationCause<'tcx> { let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) { // The `if`/`else` isn't in one line in the output, include some context to make it @@ -513,7 +504,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { then_ty, else_ty, outer_span, - opt_suggest_box_span, + tail_defines_return_position_impl_trait, })), ) } @@ -584,105 +575,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ...but otherwise we want to use any supertype of the // scrutinee. This is sort of a workaround, see note (*) in // `check_pat` for some details. - let scrut_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: scrut.span, - }); + let scrut_ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: scrut.span }); self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {}); scrut_ty } } - /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` - /// we check if the different arms would work with boxed trait objects instead and - /// provide a structured suggestion in that case. - pub(crate) fn opt_suggest_box_span( + // Does the expectation of the match define an RPIT? + // (e.g. we're in the tail of a function body) + // + // Returns the `LocalDefId` of the RPIT, which is always identity-substituted. + pub fn return_position_impl_trait_from_match_expectation( &self, - first_ty: Ty<'tcx>, - second_ty: Ty<'tcx>, - orig_expected: Expectation<'tcx>, - ) -> Option { - // FIXME(compiler-errors): This really shouldn't need to be done during the - // "good" path of typeck, but here we are. - match orig_expected { - Expectation::ExpectHasType(expected) => { - let TypeVariableOrigin { - span, - kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id), - .. - } = self.type_var_origin(expected)? - else { - return None; - }; - - let Some(rpit_local_def_id) = rpit_def_id.as_local() else { - return None; - }; - if !matches!( - self.tcx.hir().expect_item(rpit_local_def_id).expect_opaque_ty().origin, - hir::OpaqueTyOrigin::FnReturn(..) - ) { - return None; - } - - let sig = self.body_fn_sig()?; - - let args = sig.output().walk().find_map(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *ty.kind() - && def_id == rpit_def_id - { - Some(args) - } else { - None - } - })?; - - if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) { - return None; - } - - for ty in [first_ty, second_ty] { - for (clause, _) in self - .tcx - .explicit_item_bounds(rpit_def_id) - .iter_instantiated_copied(self.tcx, args) - { - let pred = clause.kind().rebind(match clause.kind().skip_binder() { - ty::ClauseKind::Trait(trait_pred) => { - assert!(matches!( - *trait_pred.trait_ref.self_ty().kind(), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args: alias_args, .. }) - if def_id == rpit_def_id && args == alias_args - )); - ty::ClauseKind::Trait(trait_pred.with_self_ty(self.tcx, ty)) - } - ty::ClauseKind::Projection(mut proj_pred) => { - assert!(matches!( - *proj_pred.projection_ty.self_ty().kind(), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args: alias_args, .. }) - if def_id == rpit_def_id && args == alias_args - )); - proj_pred = proj_pred.with_self_ty(self.tcx, ty); - ty::ClauseKind::Projection(proj_pred) - } - _ => continue, - }); - if !self.predicate_must_hold_modulo_regions(&Obligation::new( - self.tcx, - ObligationCause::misc(span, self.body_id), - self.param_env, - pred, - )) { - return None; - } - } - } - - Some(span) - } - _ => None, + expectation: Expectation<'tcx>, + ) -> Option { + let expected_ty = expectation.to_option(self)?; + let (def_id, args) = match *expected_ty.kind() { + // FIXME: Could also check that the RPIT is not defined + ty::Alias(ty::Opaque, alias_ty) => (alias_ty.def_id.as_local()?, alias_ty.args), + // FIXME(-Znext-solver): Remove this branch once `replace_opaque_types_with_infer` is gone. + ty::Infer(ty::TyVar(_)) => self + .inner + .borrow() + .iter_opaque_types() + .find(|(_, v)| v.ty == expected_ty) + .map(|(k, _)| (k.def_id, k.args))?, + _ => return None, + }; + let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = self.tcx.opaque_type_origin(def_id) + else { + return None; + }; + if &args[0..self.tcx.generics_of(parent_def_id).count()] + != ty::GenericArgs::identity_for_item(self.tcx, parent_def_id).as_slice() + { + return None; } + Some(def_id) } } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 25d3453137988..dfd0b7c2945c4 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -13,10 +13,7 @@ use rustc_infer::{ infer, traits::{self, Obligation}, }; -use rustc_infer::{ - infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, - traits::ObligationCause, -}; +use rustc_infer::{infer::type_variable::TypeVariableOrigin, traits::ObligationCause}; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; @@ -41,8 +38,11 @@ pub fn check_legal_trait_for_method_call( receiver: Option, expr_span: Span, trait_id: DefId, + body_id: DefId, ) -> Result<(), ErrorGuaranteed> { - if tcx.lang_items().drop_trait() == Some(trait_id) { + if tcx.lang_items().drop_trait() == Some(trait_id) + && tcx.lang_items().fallback_surface_drop_fn() != Some(body_id) + { let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) { errors::ExplicitDestructorCallSugg::Snippet { lo: expr_span.shrink_to_lo(), @@ -180,20 +180,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infer::FnCall, closure_args.coroutine_closure_sig(), ); - let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: callee_expr.span, - }); + let tupled_upvars_ty = self + .next_ty_var(TypeVariableOrigin { param_def_id: None, span: callee_expr.span }); + // We may actually receive a coroutine back whose kind is different + // from the closure that this dispatched from. This is because when + // we have no captures, we automatically implement `FnOnce`. This + // impl forces the closure kind to `FnOnce` i.e. `u8`. + let kind_ty = self + .next_ty_var(TypeVariableOrigin { param_def_id: None, span: callee_expr.span }); let call_sig = self.tcx.mk_fn_sig( [coroutine_closure_sig.tupled_inputs_ty], coroutine_closure_sig.to_coroutine( self.tcx, closure_args.parent_args(), - // Inherit the kind ty of the closure, since we're calling this - // coroutine with the most relaxed `AsyncFn*` trait that we can. - // We don't necessarily need to do this here, but it saves us - // computing one more infer var that will get constrained later. - closure_args.kind_ty(), + kind_ty, self.tcx.coroutine_for_closure(def_id), tupled_upvars_ty, ), @@ -301,10 +301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_tup_from_iter( self.tcx, arg_exprs.iter().map(|e| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: e.span, - }) + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: e.span }) }), ) }); @@ -720,7 +717,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def::CtorOf::Variant => "enum variant", }; let removal_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); - unit_variant = Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath))); + unit_variant = + Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(&self.tcx, qpath))); } let callee_ty = self.resolve_vars_if_possible(callee_ty); @@ -755,7 +753,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = callee_expr.kind && let Res::Local(_) = path.res - && let [segment] = &path.segments[..] + && let [segment] = &path.segments { for id in self.tcx.hir().items() { if let Some(node) = self.tcx.hir().get_if_local(id.owner_id.into()) @@ -914,7 +912,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let param = callee_args.const_at(host_effect_index); let cause = self.misc(span); - match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::No, effect, param) { + // We know the type of `effect` to be `bool`, there will be no opaque type inference. + match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::Yes, effect, param) { Ok(infer::InferOk { obligations, value: () }) => { self.register_predicates(obligations); } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index b8d1eaee81260..92f74281ab982 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -40,17 +40,19 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_session::lint; use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtExt; /// Reifies a cast check to be checked once we have full type information for /// a function context. #[derive(Debug)] -pub struct CastCheck<'tcx> { +pub(crate) struct CastCheck<'tcx> { /// The expression whose value is being casted expr: &'tcx hir::Expr<'tcx>, /// The source type for the cast expression @@ -60,8 +62,6 @@ pub struct CastCheck<'tcx> { cast_ty: Ty<'tcx>, cast_span: Span, span: Span, - /// whether the cast is made in a const context or not. - pub constness: hir::Constness, } /// The kind of pointer and associated metadata (thin, length or vtable) - we @@ -128,8 +128,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Array(..) | ty::CoroutineWitness(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) + | ty::Pat(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) @@ -194,18 +195,45 @@ fn make_invalid_casting_error<'a, 'tcx>( ) } +/// If a cast from `from_ty` to `to_ty` is valid, returns a `Some` containing the kind +/// of the cast. +/// +/// This is a helper used from clippy. +pub fn check_cast<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + e: &'tcx hir::Expr<'tcx>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> Option { + let hir_id = e.hir_id; + let local_def_id = hir_id.owner.def_id; + + let root_ctxt = crate::TypeckRootCtxt::new(tcx, local_def_id); + let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, local_def_id); + + if let Ok(check) = CastCheck::new( + &fn_ctxt, e, from_ty, to_ty, + // We won't show any errors to the user, so the span is irrelevant here. + DUMMY_SP, DUMMY_SP, + ) { + check.do_check(&fn_ctxt).ok() + } else { + None + } +} + impl<'a, 'tcx> CastCheck<'tcx> { - pub fn new( + pub(crate) fn new( fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, cast_span: Span, span: Span, - constness: hir::Constness, ) -> Result, ErrorGuaranteed> { let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); - let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness }; + let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once @@ -332,13 +360,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { let mut sugg = None; let mut sugg_mutref = false; if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() { - if let ty::RawPtr(TypeAndMut { ty: expr_ty, .. }) = *self.expr_ty.kind() + if let ty::RawPtr(expr_ty, _) = *self.expr_ty.kind() && fcx.can_coerce( - Ty::new_ref( - fcx.tcx, - fcx.tcx.lifetimes.re_erased, - TypeAndMut { ty: expr_ty, mutbl }, - ), + Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, expr_ty, mutbl), self.cast_ty, ) { @@ -346,14 +370,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind() && expr_mutbl == Mutability::Not && mutbl == Mutability::Mut - && fcx.can_coerce( - Ty::new_ref( - fcx.tcx, - expr_reg, - TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut }, - ), - self.cast_ty, - ) + && fcx.can_coerce(Ty::new_mut_ref(fcx.tcx, expr_reg, expr_ty), self.cast_ty) { sugg_mutref = true; } @@ -361,19 +378,15 @@ impl<'a, 'tcx> CastCheck<'tcx> { if !sugg_mutref && sugg == None && fcx.can_coerce( - Ty::new_ref(fcx.tcx, reg, TypeAndMut { ty: self.expr_ty, mutbl }), + Ty::new_ref(fcx.tcx, reg, self.expr_ty, mutbl), self.cast_ty, ) { sugg = Some((format!("&{}", mutbl.prefix_str()), false)); } - } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind() + } else if let ty::RawPtr(_, mutbl) = *self.cast_ty.kind() && fcx.can_coerce( - Ty::new_ref( - fcx.tcx, - fcx.tcx.lifetimes.re_erased, - TypeAndMut { ty: self.expr_ty, mutbl }, - ), + Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, self.expr_ty, mutbl), self.cast_ty, ) { @@ -659,7 +672,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { /// Checks a cast, and report an error if one exists. In some cases, this /// can return Ok and create type errors in the fcx rather than returning /// directly. coercion-cast is handled in check instead of here. - pub fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { + fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { use rustc_middle::ty::cast::CastTy::*; use rustc_middle::ty::cast::IntTy::*; @@ -868,7 +881,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // from a region pointer to a vector. // Coerce to a raw pointer so that we generate AddressOf in MIR. - let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr); + let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr.ty, m_expr.mutbl); fcx.coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None) .unwrap_or_else(|_| { bug!( diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 5841392dbcf16..b106eca59c473 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -8,8 +8,9 @@ use rustc_hir::def::DefKind; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir_analysis::check::{check_function_signature, forbid_intrinsic_abi}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::RegionVariableOrigin; +use rustc_infer::traits::WellFormedLoc; use rustc_middle::ty::{self, Binder, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; @@ -71,6 +72,18 @@ pub(super) fn check_fn<'a, 'tcx>( let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); let inputs_fn = fn_sig.inputs().iter().copied(); for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { + // We checked the root's signature during wfcheck, but not the child. + if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) { + fcx.register_wf_obligation( + param_ty.into(), + param.span, + traits::WellFormed(Some(WellFormedLoc::Param { + function: fn_def_id, + param_idx: idx, + })), + ); + } + // Check the pattern. let ty: Option<&hir::Ty<'_>> = inputs_hir.and_then(|h| h.get(idx)); let ty_span = ty.map(|ty| ty.span); @@ -108,7 +121,13 @@ pub(super) fn check_fn<'a, 'tcx>( hir::FnRetTy::DefaultReturn(_) => body.value.span, hir::FnRetTy::Return(ty) => ty.span, }; + fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::SizedReturnType); + // We checked the root's signature during wfcheck, but not the child. + if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) { + fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::WellFormed(None)); + } + fcx.is_whole_body.set(true); fcx.check_return_expr(body.value, false); @@ -123,8 +142,7 @@ pub(super) fn check_fn<'a, 'tcx>( // We have special-cased the case where the function is declared // `-> dyn Foo` and we don't actually relate it to the // `fcx.ret_coercion`, so just instantiate a type variable. - actual_return_ty = - fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span }); + actual_return_ty = fcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span }); debug!("actual_return_ty replaced with {:?}", actual_return_ty); } @@ -182,7 +200,7 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_> ty::Region::new_bound( tcx, ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }, ), panic_info_ty, ); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 4bea4bb3e8262..4883c7aff8bc4 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -5,8 +5,8 @@ use super::{check_fn, CoroutineTypes, Expectation, FnCtxt}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; -use rustc_hir_analysis::astconv::AstConv; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes}; use rustc_infer::infer::{InferOk, InferResult}; use rustc_macros::{TypeFoldable, TypeVisitable}; @@ -72,10 +72,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let parent_args = GenericArgs::identity_for_item(tcx, tcx.typeck_root_def_id(expr_def_id.to_def_id())); - let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); + let tupled_upvars_ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }); // FIXME: We could probably actually just unify this further -- // instead of having a `FnSig` and a `Option`, @@ -102,11 +100,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.next_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish closure kind inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }), + None => { + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }) + } }; let closure_args = ty::ClosureArgs::new( @@ -126,7 +122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) | hir::CoroutineKind::Coroutine(_) => { let yield_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, + param_def_id: None, span: expr_span, }); self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType); @@ -138,7 +134,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // not a problem. hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => { let yield_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, + param_def_id: None, span: expr_span, }); self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType); @@ -166,10 +162,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Resume type defaults to `()` if the coroutine has no argument. let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit); - let interior = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); + let interior = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }); self.deferred_coroutine_interiors.borrow_mut().push(( expr_def_id, body.id(), @@ -181,11 +175,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // later during upvar analysis. Regular coroutines always have the kind // ty of `().` let kind_ty = match kind { - hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) => self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }), + hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) => { + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }) + } _ => tcx.types.unit, }; @@ -219,23 +211,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; // Compute all of the variables that will be used to populate the coroutine. - let resume_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); - let interior = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); - let closure_kind_ty = self.next_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish closure kind inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); - let coroutine_captures_by_ref_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); + let resume_ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }); + let interior = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }); + + let closure_kind_ty = match expected_kind { + Some(kind) => Ty::from_closure_kind(tcx, kind), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + None => { + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }) + } + }; + + let coroutine_captures_by_ref_ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }); let closure_args = ty::CoroutineClosureArgs::new( tcx, ty::CoroutineClosureArgsParts { @@ -262,10 +254,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - let coroutine_upvars_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr_span, - }); + let coroutine_kind_ty = match expected_kind { + Some(kind) => Ty::from_coroutine_closure_kind(tcx, kind), + + // Create a type variable (for now) to represent the closure kind. + // It will be unified during the upvar inference phase (`upvar.rs`) + None => { + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }) + } + }; + + let coroutine_upvars_ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr_span }); // We need to turn the liberated signature that we got from HIR, which // looks something like `|Args...| -> T`, into a signature that is suitable @@ -279,7 +279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sig.to_coroutine( tcx, parent_args, - closure_kind_ty, + coroutine_kind_ty, tcx.coroutine_for_closure(expr_def_id), coroutine_upvars_ty, ) @@ -325,7 +325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty, closure_kind, self.tcx - .explicit_item_bounds(def_id) + .explicit_item_super_predicates(def_id) .iter_instantiated_copied(self.tcx, args) .map(|(c, s)| (c.as_predicate(), s)), ), @@ -342,7 +342,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates( Ty::new_var(self.tcx, self.root_var(vid)), closure_kind, - self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)), + self.obligations_for_self_ty(vid) + .into_iter() + .map(|obl| (obl.predicate, obl.cause.span)), ), ty::FnPtr(sig) => match closure_kind { hir::ClosureKind::Closure => { @@ -780,7 +782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl: &hir::FnDecl<'tcx>, closure_kind: hir::ClosureKind, ) -> ty::PolyFnSig<'tcx> { - let astconv = self.astconv(); + let lowerer = self.lowerer(); trace!("decl = {:#?}", decl); debug!(?closure_kind); @@ -789,9 +791,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bound_vars = self.tcx.late_bound_vars(hir_id); // First, convert the types that the user supplied (if any). - let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a)); + let supplied_arguments = decl.inputs.iter().map(|a| lowerer.lower_ty(a)); let supplied_return = match decl.output { - hir::FnRetTy::Return(ref output) => astconv.ast_ty_to_ty(output), + hir::FnRetTy::Return(ref output) => lowerer.lower_ty(output), hir::FnRetTy::DefaultReturn(_) => match closure_kind { // In the case of the async block that we create for a function body, // we expect the return type of the block to match that of the enclosing @@ -809,7 +811,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // easily (and locally) prove that we // *have* reported an // error. --nikomatsakis - astconv.ty_infer(None, decl.output.span()) + lowerer.ty_infer(None, decl.output.span()) }) } // All `gen {}` and `async gen {}` must return unit. @@ -828,7 +830,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) | hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { - astconv.ty_infer(None, decl.output.span()) + lowerer.ty_infer(None, decl.output.span()) } }, }; @@ -844,7 +846,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_vars, ); - let c_result = self.inh.infcx.canonicalize_response(result); + let c_result = self.infcx.canonicalize_response(result); self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); // Normalize only after registering in `user_provided_sigs`. @@ -889,7 +891,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let output_ty = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => { - self.obligations_for_self_ty(ret_vid).find_map(|obligation| { + self.obligations_for_self_ty(ret_vid).into_iter().find_map(|obligation| { get_future_output(obligation.predicate, obligation.cause.span) })? } @@ -902,7 +904,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self .tcx - .explicit_item_bounds(def_id) + .explicit_item_super_predicates(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return Some(ret_ty), @@ -985,17 +987,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl: &hir::FnDecl<'tcx>, guar: ErrorGuaranteed, ) -> ty::PolyFnSig<'tcx> { - let astconv = self.astconv(); + let lowerer = self.lowerer(); let err_ty = Ty::new_error(self.tcx, guar); let supplied_arguments = decl.inputs.iter().map(|a| { // Convert the types that the user supplied (if any), but ignore them. - astconv.ast_ty_to_ty(a); + lowerer.lower_ty(a); err_ty }); if let hir::FnRetTy::Return(ref output) = decl.output { - astconv.ast_ty_to_ty(output); + lowerer.lower_ty(output); } let result = ty::Binder::dummy(self.tcx.mk_fn_sig( diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 792359c9dda1b..825276aef421c 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -35,17 +35,18 @@ //! // and are then unable to coerce `&7i32` to `&mut i32`. //! ``` +use crate::errors::SuggestBoxingForReturnImplTrait; use crate::FnCtxt; use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Expr; -use rustc_hir_analysis::astconv::AstConv; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; -use rustc_infer::traits::TraitEngine; use rustc_infer::traits::TraitEngineExt as _; +use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause, TraitEngine}; use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_middle::lint::in_external_macro; use rustc_middle::traits::BuiltinImplSource; @@ -55,12 +56,13 @@ use rustc_middle::ty::adjustment::{ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeAndMut}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; -use rustc_span::DesugaringKind; +use rustc_span::{BytePos, DesugaringKind, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::TraitEngineExt as _; @@ -222,8 +224,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Examine the supertype and consider auto-borrowing. match *b.kind() { - ty::RawPtr(mt_b) => { - return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); + ty::RawPtr(_, b_mutbl) => { + return self.coerce_unsafe_ptr(a, b, b_mutbl); } ty::Ref(r_b, _, mutbl_b) => { return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); @@ -277,10 +279,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { if b.is_ty_var() { // Two unresolved type variables: create a `Coerce` predicate. let target_ty = if self.use_lub { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::LatticeVariable, - span: self.cause.span, - }) + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: self.cause.span }) } else { b }; @@ -440,10 +439,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let derefd_ty_a = Ty::new_ref( self.tcx, r, - TypeAndMut { - ty: referent_ty, - mutbl: mutbl_b, // [1] above - }, + referent_ty, + mutbl_b, // [1] above ); match self.unify(derefd_ty_a, b) { Ok(ok) => { @@ -558,22 +555,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)), - target: Ty::new_ref( - self.tcx, - r_borrow, - ty::TypeAndMut { mutbl: mutbl_b, ty: ty_a }, - ), + target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), }, )) } - (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(ty::TypeAndMut { mutbl: mt_b, .. })) => { + (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(_, mt_b)) => { coerce_mutbls(mt_a, mt_b)?; Some(( Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), - target: Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: mt_b, ty: ty_a }), + target: Ty::new_ptr(self.tcx, ty_a, mt_b), }, )) } @@ -585,10 +578,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // the `CoerceUnsized` target type and the expected type. // We only have the latter, so we use an inference variable // for the former and let type inference do the rest. - let origin = TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.cause.span, - }; + let origin = TypeVariableOrigin { param_def_id: None, span: self.cause.span }; let coerce_target = self.next_ty_var(origin); let mut coercion = self.unify_and(coerce_target, target, |target| { let unsize = Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), target }; @@ -984,13 +974,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let (is_ref, mt_a) = match *a.kind() { ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), - ty::RawPtr(mt) => (false, mt), + ty::RawPtr(ty, mutbl) => (false, ty::TypeAndMut { ty, mutbl }), _ => return self.unify_and(a, b, identity), }; coerce_mutbls(mt_a.mutbl, mutbl_b)?; // Check that the types which they point at are compatible. - let a_unsafe = Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty }); + let a_unsafe = Ty::new_ptr(self.tcx, mt_a.ty, mutbl_b); // Although references and unsafe ptrs have the same // representation, we still register an Adjust::DerefRef so that // regionck knows that the region for `a` must be valid here. @@ -1054,7 +1044,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let source = self.resolve_vars_with_obligations(expr_ty); debug!("coercion::can_with_predicates({:?} -> {:?})", source, target); - let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable); + let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); // We don't ever need two-phase here since we throw out the result of the coercion let coerce = Coerce::new(self, cause, AllowTwoPhase::No); self.probe(|_| { @@ -1071,11 +1061,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// how many dereference steps needed to achieve `expr_ty <: target`. If /// it's not possible, return `None`. pub fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option { - let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable); + let cause = self.cause(DUMMY_SP, ObligationCauseCode::ExprAssignable); // We don't ever need two-phase here since we throw out the result of the coercion let coerce = Coerce::new(self, cause, AllowTwoPhase::No); coerce - .autoderef(rustc_span::DUMMY_SP, expr_ty) + .autoderef(DUMMY_SP, expr_ty) .find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps)) } @@ -1086,7 +1076,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// trait or region sub-obligations. (presumably we could, but it's not /// particularly important for diagnostics...) pub fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Option> { - self.autoderef(rustc_span::DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| { + self.autoderef(DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| { self.infcx .type_implements_trait( self.tcx.lang_items().deref_mut_trait()?, @@ -1149,7 +1139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // are the same function and their parameters have a LUB. match self.commit_if_ok(|_| { self.at(cause, self.param_env).lub( - DefineOpaqueTypes::No, + DefineOpaqueTypes::Yes, prev_ty, new_ty, ) @@ -1322,6 +1312,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +/// Check whether `ty` can be coerced to `output_ty`. +/// Used from clippy. +pub fn can_coerce<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + ty: Ty<'tcx>, + output_ty: Ty<'tcx>, +) -> bool { + let root_ctxt = crate::typeck_root_ctxt::TypeckRootCtxt::new(tcx, body_id); + let fn_ctxt = FnCtxt::new(&root_ctxt, param_env, body_id); + fn_ctxt.can_coerce(ty, output_ty) +} + /// CoerceMany encapsulates the pattern you should use when you have /// many expressions that are all getting coerced to a common /// type. This arises, for example, when you have a match (the result @@ -1458,7 +1462,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx, cause, None, - Ty::new_unit(fcx.tcx), + fcx.tcx.types.unit, augment_error, label_unit_as_expected, ) @@ -1606,6 +1610,15 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { E0069, "`return;` in a function whose return type is not `()`" ); + if let Some(value) = fcx.err_ctxt().ty_kind_suggestion(fcx.param_env, found) + { + err.span_suggestion_verbose( + cause.span.shrink_to_hi(), + "give the `return` a value of the expected type", + format!(" {value}"), + Applicability::HasPlaceholders, + ); + } err.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id, ..) => { @@ -1644,6 +1657,77 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { unsized_return = self.is_return_ty_definitely_unsized(fcx); } } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + arm_span, + arm_ty, + prior_arm_ty, + ref prior_non_diverging_arms, + tail_defines_return_position_impl_trait: Some(rpit_def_id), + .. + }) => { + err = fcx.err_ctxt().report_mismatched_types( + cause, + expected, + found, + coercion_error, + ); + // Check that we're actually in the second or later arm + if prior_non_diverging_arms.len() > 0 { + self.suggest_boxing_tail_for_return_position_impl_trait( + fcx, + &mut err, + rpit_def_id, + arm_ty, + prior_arm_ty, + prior_non_diverging_arms + .iter() + .chain(std::iter::once(&arm_span)) + .copied(), + ); + } + } + ObligationCauseCode::IfExpression(box IfExpressionCause { + then_id, + else_id, + then_ty, + else_ty, + tail_defines_return_position_impl_trait: Some(rpit_def_id), + .. + }) => { + err = fcx.err_ctxt().report_mismatched_types( + cause, + expected, + found, + coercion_error, + ); + let then_span = fcx.find_block_span_from_hir_id(then_id); + let else_span = fcx.find_block_span_from_hir_id(else_id); + // don't suggest wrapping either blocks in `if .. {} else {}` + let is_empty_arm = |id| { + let hir::Node::Block(blk) = fcx.tcx.hir_node(id) else { + return false; + }; + if blk.expr.is_some() || !blk.stmts.is_empty() { + return false; + } + let Some((_, hir::Node::Expr(expr))) = + fcx.tcx.hir().parent_iter(id).nth(1) + else { + return false; + }; + matches!(expr.kind, hir::ExprKind::If(..)) + }; + if !is_empty_arm(then_id) && !is_empty_arm(else_id) { + self.suggest_boxing_tail_for_return_position_impl_trait( + fcx, + &mut err, + rpit_def_id, + then_ty, + else_ty, + [then_span, else_span].into_iter(), + ); + } + } _ => { err = fcx.err_ctxt().report_mismatched_types( cause, @@ -1683,6 +1767,70 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } + fn suggest_boxing_tail_for_return_position_impl_trait( + &self, + fcx: &FnCtxt<'_, 'tcx>, + err: &mut Diag<'_>, + rpit_def_id: LocalDefId, + a_ty: Ty<'tcx>, + b_ty: Ty<'tcx>, + arm_spans: impl Iterator, + ) { + let compatible = |ty: Ty<'tcx>| { + fcx.probe(|_| { + let ocx = ObligationCtxt::new(fcx); + ocx.register_obligations( + fcx.tcx + .item_super_predicates(rpit_def_id) + .instantiate_identity_iter() + .filter_map(|clause| { + let predicate = clause + .kind() + .map_bound(|clause| match clause { + ty::ClauseKind::Trait(trait_pred) => Some( + ty::ClauseKind::Trait(trait_pred.with_self_ty(fcx.tcx, ty)), + ), + ty::ClauseKind::Projection(proj_pred) => { + Some(ty::ClauseKind::Projection( + proj_pred.with_self_ty(fcx.tcx, ty), + )) + } + _ => None, + }) + .transpose()?; + Some(Obligation::new( + fcx.tcx, + ObligationCause::dummy(), + fcx.param_env, + predicate, + )) + }), + ); + ocx.select_where_possible().is_empty() + }) + }; + + if !compatible(a_ty) || !compatible(b_ty) { + return; + } + + let rpid_def_span = fcx.tcx.def_span(rpit_def_id); + err.subdiagnostic( + fcx.tcx.dcx(), + SuggestBoxingForReturnImplTrait::ChangeReturnType { + start_sp: rpid_def_span.with_hi(rpid_def_span.lo() + BytePos(4)), + end_sp: rpid_def_span.shrink_to_hi(), + }, + ); + + let (starts, ends) = + arm_spans.map(|span| (span.shrink_to_lo(), span.shrink_to_hi())).unzip(); + err.subdiagnostic( + fcx.tcx.dcx(), + SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends }, + ); + } + fn note_unreachable_loop_return( &self, err: &mut Diag<'_>, @@ -1859,16 +2007,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, @@ -1878,13 +2027,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/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 67ff412651c20..618d90a07ec29 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -299,8 +299,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; }; let (init_ty_hir_id, init) = match self.tcx.parent_hir_node(pat.hir_id) { - hir::Node::Local(hir::Local { ty: Some(ty), init, .. }) => (ty.hir_id, *init), - hir::Node::Local(hir::Local { init: Some(init), .. }) => (init.hir_id, Some(*init)), + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), init, .. }) => (ty.hir_id, *init), + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => (init.hir_id, Some(*init)), _ => return false, }; let Some(init_ty) = self.node_ty_opt(init_ty_hir_id) else { @@ -337,10 +337,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty_op: |ty| { if let ty::Infer(infer) = ty.kind() { match infer { - ty::TyVar(_) => self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }), + ty::TyVar(_) => self + .next_ty_var(TypeVariableOrigin { param_def_id: None, span: DUMMY_SP }), ty::IntVar(_) => self.next_int_var(), ty::FloatVar(_) => self.next_float_var(), ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { @@ -356,10 +354,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::ConstKind::Infer(_) = ct.kind() { self.next_const_var( ct.ty(), - ConstVariableOrigin { - kind: ConstVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }, + ConstVariableOrigin { param_def_id: None, span: DUMMY_SP }, ) } else { ct @@ -400,7 +395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // what our ideal rcvr ty would look like. let _ = self .at(&ObligationCause::dummy(), self.param_env) - .eq(DefineOpaqueTypes::No, method.sig.inputs()[idx + 1], arg_ty) + .eq(DefineOpaqueTypes::Yes, method.sig.inputs()[idx + 1], arg_ty) .ok()?; self.select_obligations_where_possible(|errs| { // Yeet the errors, we're already reporting errors. @@ -479,7 +474,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .and_then(|method| { let _ = self .at(&ObligationCause::dummy(), self.param_env) - .eq(DefineOpaqueTypes::No, ideal_rcvr_ty, expected_ty) + .eq(DefineOpaqueTypes::Yes, ideal_rcvr_ty, expected_ty) .ok()?; Some(method) }); @@ -678,7 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: Option>, ) { match (self.tcx.parent_hir_node(expr.hir_id), error) { - (hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. }), _) + (hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), init: Some(init), .. }), _) if init.hir_id == expr.hir_id => { // Point at `let` assignment type. @@ -724,11 +719,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { primary_span = pat.span; secondary_span = pat.span; match self.tcx.parent_hir_node(pat.hir_id) { - hir::Node::Local(hir::Local { ty: Some(ty), .. }) => { + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => { primary_span = ty.span; post_message = " type"; } - hir::Node::Local(hir::Local { init: Some(init), .. }) => { + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => { primary_span = init.span; post_message = " value"; } @@ -894,7 +889,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [candidate] => format!( "the method of the same name on {} `{}`", match candidate.kind { - probe::CandidateKind::InherentImplCandidate(..) => "the inherent impl for", + probe::CandidateKind::InherentImplCandidate(_) => "the inherent impl for", _ => "trait", }, self.tcx.def_path_str(candidate.item.container_id(self.tcx)) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index df21b84f92ef3..ba8f246fd8d24 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::Ty; use rustc_span::{ edition::{Edition, LATEST_STABLE_EDITION}, symbol::Ident, - Span, + Span, Symbol, }; #[derive(Diagnostic)] @@ -76,13 +76,6 @@ pub struct StructExprNonExhaustive { pub what: &'static str, } -#[derive(Diagnostic)] -#[diag(hir_typeck_method_call_on_unknown_raw_pointee, code = E0699)] -pub struct MethodCallOnUnknownRawPointee { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(hir_typeck_functional_record_update_on_non_struct, code = E0436)] pub struct FunctionalRecordUpdateOnNonStruct { @@ -171,6 +164,25 @@ pub struct MissingParenthesesInRange { pub add_missing_parentheses: Option, } +#[derive(LintDiagnostic)] +pub enum NeverTypeFallbackFlowingIntoUnsafe { + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_call)] + Call, + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_method)] + Method, + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_path)] + Path, + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_union_field)] + UnionField, + #[help] + #[diag(hir_typeck_never_type_fallback_flowing_into_unsafe_deref)] + Deref, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( hir_typeck_add_missing_parentheses_in_range, @@ -198,7 +210,7 @@ impl Subdiagnostic for TypeMismatchFruTypo { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { diag.arg("expr", self.expr.as_deref().unwrap_or("NONE")); @@ -377,7 +389,7 @@ impl Subdiagnostic for RemoveSemiForCoerce { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { let mut multispan: MultiSpan = self.semi.into(); multispan.push_span_label(self.expr, fluent::hir_typeck_remove_semi_for_coerce_expr); @@ -553,7 +565,7 @@ impl rustc_errors::Subdiagnostic for CastUnknownPointerSub { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - f: F, + f: &F, ) { match self { CastUnknownPointerSub::To(span) => { @@ -614,3 +626,35 @@ pub struct SuggestConvertViaMethod<'tcx> { pub expected: Ty<'tcx>, pub found: Ty<'tcx>, } + +#[derive(Subdiagnostic)] +#[note(hir_typeck_note_caller_chooses_ty_for_ty_param)] +pub struct NoteCallerChoosesTyForTyParam<'tcx> { + pub ty_param_name: Symbol, + pub found_ty: Ty<'tcx>, +} + +#[derive(Subdiagnostic)] +pub enum SuggestBoxingForReturnImplTrait { + #[multipart_suggestion(hir_typeck_rpit_change_return_type, applicability = "maybe-incorrect")] + ChangeReturnType { + #[suggestion_part(code = "Box, + #[suggestion_part(code = ")")] + ends: Vec, + }, +} +#[derive(LintDiagnostic)] +#[diag(hir_typeck_dereferencing_mut_binding)] +pub struct DereferencingMutBinding { + #[label] + #[help] + pub span: Span, +} diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index ff84e753d70df..5106d29091a17 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -1,4 +1,4 @@ -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -110,8 +110,7 @@ impl<'a, 'tcx> Expectation<'tcx> { /// Like `only_has_type`, but instead of returning `None` if no /// hard constraint exists, creates a fresh type variable. pub(super) fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> { - self.only_has_type(fcx).unwrap_or_else(|| { - fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }) - }) + self.only_has_type(fcx) + .unwrap_or_else(|| fcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span })) } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 1a142f27809e1..8b1ea7c952cb1 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1,6 +1,6 @@ //! Type checking expressions. //! -//! See `mod.rs` for more context on type checking in general. +//! See [`rustc_hir_analysis::check`] for more context on type checking in general. use crate::cast; use crate::coercion::CoerceMany; @@ -35,19 +35,15 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath}; -use rustc_hir_analysis::astconv::AstConv as _; -use rustc_hir_analysis::check::ty_kind_suggestion; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::InferOk; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::{ - ExpectedFound, - TypeError::{FieldMisMatch, Sorts}, -}; +use rustc_middle::ty::error::{ExpectedFound, TypeError::Sorts}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt}; use rustc_session::errors::ExprParenthesesNeeded; @@ -58,8 +54,8 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; -use rustc_target::spec::abi::Abi::RustIntrinsic; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; @@ -84,10 +80,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ty::new_error(self.tcx(), reported); } - let adj_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AdjustmentType, - span: expr.span, - }); + let adj_ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr.span }); self.apply_adjustments( expr, vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], @@ -333,7 +327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), ExprKind::Type(e, t) => { - let ascribed_ty = self.to_ty_saving_user_provided_ty(t); + let ascribed_ty = self.lower_ty_saving_user_provided_ty(t); let ty = self.check_expr_with_hint(e, ascribed_ty); self.demand_eqtype(e.span, ascribed_ty, ty); ascribed_ty @@ -426,7 +420,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { match ty.kind() { - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => { if oprnd.is_syntactic_place_expr() { // Places may legitimately have unsized types. // For example, dereferences of a fat pointer and @@ -442,12 +436,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.check_expr_with_expectation_and_needs(oprnd, hint, Needs::maybe_mut_place(mutbl)); - let tm = ty::TypeAndMut { ty, mutbl }; match kind { - _ if tm.ty.references_error() => Ty::new_misc_error(self.tcx), + _ if ty.references_error() => Ty::new_misc_error(self.tcx), hir::BorrowKind::Raw => { self.check_named_place_expr(oprnd); - Ty::new_ptr(self.tcx, tm) + Ty::new_ptr(self.tcx, ty, mutbl) } hir::BorrowKind::Ref => { // Note: at this point, we cannot say what the best lifetime @@ -465,7 +458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // whose address was taken can actually be made to live as long // as it needs to live. let region = self.next_region_var(infer::AddrOfRegion(expr.span)); - Ty::new_ref(self.tcx, region, tm) + Ty::new_ref(self.tcx, region, ty, mutbl) } } } @@ -545,16 +538,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::FnDef(did, _) = *ty.kind() { let fn_sig = ty.fn_sig(tcx); - if tcx.fn_sig(did).skip_binder().abi() == RustIntrinsic - && tcx.item_name(did) == sym::transmute - { - let from = fn_sig.inputs().skip_binder()[0]; + if tcx.is_intrinsic(did, sym::transmute) { + let Some(from) = fn_sig.inputs().skip_binder().get(0) else { + span_bug!( + tcx.def_span(did), + "intrinsic fn `transmute` defined with no parameters" + ); + }; let to = fn_sig.output().skip_binder(); // We defer the transmute to the end of typeck, once all inference vars have // been resolved or we errored. This is important as we can only check transmute // on concrete types, but the output type may not be known yet (it would only // be known if explicitly specified via turbofish). - self.deferred_transmute_checks.borrow_mut().push((from, to, expr.hir_id)); + self.deferred_transmute_checks.borrow_mut().push((*from, to, expr.hir_id)); } if !tcx.features().unsized_fn_params { // We want to remove some Sized bounds from std functions, @@ -658,7 +654,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Otherwise, this is a break *without* a value. That's // always legal, and is equivalent to `break ()`. - e_ty = Ty::new_unit(tcx); + e_ty = tcx.types.unit; cause = self.misc(expr.span); } @@ -691,10 +687,12 @@ 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) = + self.err_ctxt().ty_kind_suggestion(self.param_env, ty) + { 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, ); @@ -1089,7 +1087,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let else_ty = self.check_expr_with_expectation(else_expr, expected); let else_diverges = self.diverges.get(); - let opt_suggest_box_span = self.opt_suggest_box_span(then_ty, else_ty, orig_expected); + let tail_defines_return_position_impl_trait = + self.return_position_impl_trait_from_match_expectation(orig_expected); let if_cause = self.if_cause( sp, cond_expr.span, @@ -1097,7 +1096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else_expr, then_ty, else_ty, - opt_suggest_box_span, + tail_defines_return_position_impl_trait, ); coerce.coerce(self, &if_cause, else_expr, else_ty); @@ -1134,7 +1133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The expected type is `bool` but this will result in `()` so we can reasonably // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. // The likely cause of this is `if foo = bar { .. }`. - let actual_ty = Ty::new_unit(self.tcx); + let actual_ty = self.tcx.types.unit; let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap(); let lhs_ty = self.check_expr(lhs); let rhs_ty = self.check_expr(rhs); @@ -1257,11 +1256,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Err(guar) = (lhs_ty, rhs_ty).error_reported() { Ty::new_error(self.tcx, guar) } else { - Ty::new_unit(self.tcx) + self.tcx.types.unit } } - pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>, hir_id: HirId) -> Ty<'tcx> { + pub(super) fn check_expr_let( + &self, + let_expr: &'tcx hir::LetExpr<'tcx>, + hir_id: HirId, + ) -> Ty<'tcx> { // for let statements, this is done in check_stmt let init = let_expr.init; self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression"); @@ -1316,7 +1319,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ctxt.coerce.is_none() && !ctxt.may_break { self.dcx().span_bug(body.span, "no coercion, but loop may not break"); } - ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| Ty::new_unit(self.tcx)) + ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.types.unit) } /// Checks a method call. @@ -1371,7 +1374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { // Find the type of `e`. Supply hints based on the type we are casting to, // if appropriate. - let t_cast = self.to_ty_saving_user_provided_ty(t); + let t_cast = self.lower_ty_saving_user_provided_ty(t); let t_cast = self.resolve_vars_if_possible(t_cast); let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); let t_expr = self.resolve_vars_if_possible(t_expr); @@ -1382,15 +1385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::CastCheck::new( - self, - e, - t_expr, - t_cast, - t.span, - expr.span, - hir::Constness::NotConst, - ) { + match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { Ok(cast_check) => { debug!( "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", @@ -1418,10 +1413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }) .unwrap_or_else(|| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, - }) + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr.span }) }); let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); assert_eq!(self.diverges.get(), Diverges::Maybe); @@ -1432,10 +1424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } coerce.complete(self) } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, - }) + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: expr.span }) }; let array_len = args.len() as u64; self.suggest_array_len(expr, array_len); @@ -1448,7 +1437,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let Some(( _, - hir::Node::Local(hir::Local { ty: Some(ty), .. }) + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }), )) = parent_node else { @@ -1499,7 +1488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; - let count = self.array_length_to_const(count); + let count = self.lower_array_length(count); if let Some(count) = count.try_eval_target_usize(tcx, self.param_env) { self.suggest_array_len(expr, count); } @@ -1518,10 +1507,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (uty, uty) } None => { - let ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: element.span, - }); + let ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: element.span }); let element_ty = self.check_expr_has_type_or_error(element, ty, |_| {}); (element_ty, ty) } @@ -1675,7 +1662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, span: Span, variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::ExprField<'tcx>], + hir_fields: &'tcx [hir::ExprField<'tcx>], base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, ) { let tcx = self.tcx; @@ -1706,7 +1693,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut error_happened = false; // Type-check each field. - for (idx, field) in ast_fields.iter().enumerate() { + for (idx, field) in hir_fields.iter().enumerate() { let ident = tcx.adjust_ident(field.ident, variant.def_id); let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { seen_fields.insert(ident, field.span); @@ -1735,7 +1722,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant, expr, field, - ast_fields, + hir_fields, adt.variant_descr(), ) }; @@ -1750,7 +1737,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No); if let Some(diag) = diag { - if idx == ast_fields.len() - 1 { + if idx == hir_fields.len() - 1 { if remaining_fields.is_empty() { self.suggest_fru_from_range_and_emit(field, variant, args, diag); } else { @@ -1764,7 +1751,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Make sure the programmer specified correct number of fields. if adt_kind == AdtKind::Union { - if ast_fields.len() != 1 { + if hir_fields.len() != 1 { struct_span_code_err!( tcx.dcx(), span, @@ -1807,7 +1794,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let target_ty = self.field_ty(base_expr.span, f, args); let cause = self.misc(base_expr.span); match self.at(&cause, self.param_env).sup( - DefineOpaqueTypes::No, + // We're already using inference variables for any params, and don't allow converting + // between different structs, so there is no way this ever actually defines an opaque type. + // Thus choosing `Yes` is fine. + DefineOpaqueTypes::Yes, target_ty, fru_ty, ) { @@ -1815,16 +1805,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations) } Err(_) => { - // This should never happen, since we're just subtyping the - // remaining_fields, but it's fine to emit this, I guess. - self.err_ctxt() - .report_mismatched_types( - &cause, - target_ty, - fru_ty, - FieldMisMatch(variant.name, ident.name), - ) - .emit(); + span_bug!( + cause.span(), + "subtyping remaining fields of type changing FRU failed: {target_ty} != {fru_ty}: {}::{}", + variant.name, + ident.name, + ); } } } @@ -1901,14 +1887,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); if !private_fields.is_empty() { - self.report_private_fields(adt_ty, span, expr.span, private_fields, ast_fields); + self.report_private_fields(adt_ty, span, expr.span, private_fields, hir_fields); } else { self.report_missing_fields( adt_ty, span, remaining_fields, variant, - ast_fields, + hir_fields, args, ); } @@ -1945,7 +1931,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, remaining_fields: UnordMap, variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::ExprField<'tcx>], + hir_fields: &'tcx [hir::ExprField<'tcx>], args: GenericArgsRef<'tcx>, ) { let len = remaining_fields.len(); @@ -1982,8 +1968,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); - if let Some(last) = ast_fields.last() { - self.suggest_fru_from_range_and_emit(last, variant, args, err); + if let Some(hir_field) = hir_fields.last() { + self.suggest_fru_from_range_and_emit(hir_field, variant, args, err); } else { err.emit(); } @@ -2024,8 +2010,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .sess .source_map() - .span_extend_while(range_start.span, |c| c.is_whitespace()) - .unwrap_or(range_start.span) + .span_extend_while_whitespace(range_start.span) .shrink_to_hi() .to(range_end.span); @@ -2682,8 +2667,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr, Some(span), ); - } else if let ty::RawPtr(ty_and_mut) = expr_t.kind() - && let ty::Adt(adt_def, _) = ty_and_mut.ty.kind() + } else if let ty::RawPtr(ptr_ty, _) = expr_t.kind() + && let ty::Adt(adt_def, _) = ptr_ty.kind() && let ExprKind::Field(base_expr, _) = expr.kind && adt_def.variants().len() == 1 && adt_def @@ -2712,7 +2697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn point_at_param_definition(&self, err: &mut Diag<'_>, param: ty::ParamTy) { let generics = self.tcx.generics_of(self.body_id); - let generic_param = generics.type_param(¶m, self.tcx); + let generic_param = generics.type_param(param, self.tcx); if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind { return; } @@ -2866,7 +2851,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, base_ty: Ty<'tcx>, mod_id: DefId, - hir_id: hir::HirId, + hir_id: HirId, ) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> { debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty); @@ -3096,7 +3081,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { cause.clone().derived_cause( ty::Binder::dummy(ty::TraitPredicate { trait_ref: impl_trait_ref, - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }), |derived| { traits::ImplDerivedObligation(Box::new( @@ -3189,7 +3174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx().emit_err(YieldExprOutsideOfCoroutine { span: expr.span }); // Avoid expressions without types during writeback (#78653). self.check_expr(value); - Ty::new_unit(self.tcx) + self.tcx.types.unit } } } @@ -3221,7 +3206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No); } ty::Ref(_, base_ty, mutbl) => { - let ptr_ty = Ty::new_ptr(self.tcx, ty::TypeAndMut { ty: base_ty, mutbl }); + let ptr_ty = Ty::new_ptr(self.tcx, base_ty, mutbl); self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No); } _ => {} @@ -3279,7 +3264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &[Ident], expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let container = self.to_ty(container).normalized; + let container = self.lower_ty(container).normalized; if let Some(ident_2) = fields.get(1) && !self.tcx.features().offset_of_nested diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 43e9554459439..cc42e69f53827 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; -use rustc_hir::PatKind; +use rustc_hir::{HirId, PatKind}; use rustc_infer::infer::InferCtxt; use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::FakeReadCause; @@ -39,20 +39,20 @@ pub trait Delegate<'tcx> { /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic /// id will be the id of the expression `expr` but the place itself will have /// the id of the binding in the pattern `pat`. - fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId); + fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId); /// The value found at `place` is being borrowed with kind `bk`. /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details). fn borrow( &mut self, place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, + diag_expr_id: HirId, bk: ty::BorrowKind, ); /// The value found at `place` is being copied. /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details). - fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { + fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { // In most cases, copying data from `x` is equivalent to doing `*&x`, so by default // we treat a copy of `x` as a borrow of `x`. self.borrow(place_with_id, diag_expr_id, ty::BorrowKind::ImmBorrow) @@ -60,12 +60,12 @@ pub trait Delegate<'tcx> { /// The path at `assignee_place` is being assigned to. /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details). - fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId); + fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId); /// The path at `binding_place` is a binding that is being initialized. /// /// This covers cases such as `let x = 42;` - fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { + fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { // Bindings can normally be treated as a regular assignment, so by default we // forward this to the mutate callback. self.mutate(binding_place, diag_expr_id) @@ -76,7 +76,7 @@ pub trait Delegate<'tcx> { &mut self, place_with_id: &PlaceWithHirId<'tcx>, cause: FakeReadCause, - diag_expr_id: hir::HirId, + diag_expr_id: HirId, ); } @@ -154,7 +154,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.mc.tcx() } - fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { + fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { delegate_consume(&self.mc, self.delegate, place_with_id, diag_expr_id) } @@ -245,7 +245,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - hir::ExprKind::Let(hir::Let { pat, init, .. }) => { + hir::ExprKind::Let(hir::LetExpr { pat, init, .. }) => { self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow)) } @@ -371,7 +371,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { match stmt.kind { - hir::StmtKind::Let(hir::Local { pat, init: Some(expr), els, .. }) => { + hir::StmtKind::Let(hir::LetStmt { pat, init: Some(expr), els, .. }) => { self.walk_local(expr, pat, *els, |_| {}) } @@ -463,6 +463,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } PatKind::Or(_) | PatKind::Box(_) + | PatKind::Deref(_) | PatKind::Ref(..) | PatKind::Wild | PatKind::Err(_) => { @@ -738,17 +739,26 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // In a cases of pattern like `let pat = upvar`, don't use the span // of the pattern, as this just looks confusing, instead use the span // of the discriminant. - match bm { - ty::BindByReference(m) => { + match bm.0 { + hir::ByRef::Yes(m) => { let bk = ty::BorrowKind::from_mutbl(m); delegate.borrow(place, discr_place.hir_id, bk); } - ty::BindByValue(..) => { + hir::ByRef::No => { debug!("walk_pat binding consuming pat"); delegate_consume(mc, *delegate, place, discr_place.hir_id); } } } + } else if let PatKind::Deref(subpattern) = pat.kind { + // A deref pattern is a bit special: the binding mode of its inner bindings + // determines whether to borrow *at the level of the deref pattern* rather than + // borrowing the bound place (since that inner place is inside the temporary that + // stores the result of calling `deref()`/`deref_mut()` so can't be captured). + let mutable = mc.typeck_results.pat_has_ref_mut_binding(subpattern); + let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; + let bk = ty::BorrowKind::from_mutbl(mutability); + delegate.borrow(place, discr_place.hir_id, bk); } })); } @@ -774,8 +784,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// closure as the DefId. fn walk_captures(&mut self, closure_expr: &hir::Closure<'_>) { fn upvar_is_local_variable( - upvars: Option<&FxIndexMap>, - upvar_id: hir::HirId, + upvars: Option<&FxIndexMap>, + upvar_id: HirId, body_owner_is_closure: bool, ) -> bool { upvars.map(|upvars| !upvars.contains_key(&upvar_id)).unwrap_or(body_owner_is_closure) @@ -901,7 +911,7 @@ fn delegate_consume<'a, 'tcx>( mc: &mc::MemCategorizationContext<'a, 'tcx>, delegate: &mut (dyn Delegate<'tcx> + 'a), place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, + diag_expr_id: HirId, ) { debug!("delegate_consume(place_with_id={:?})", place_with_id); diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c16e941d4c5b7..f240a53a67974 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,15 +1,21 @@ -use crate::FnCtxt; +use std::cell::OnceCell; + +use crate::{errors, FnCtxt, TypeckRootCtxt}; use rustc_data_structures::{ - graph::WithSuccessors, - graph::{iterate::DepthFirstSearch, vec_graph::VecGraph}, + graph::{self, iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; -use rustc_hir::def_id::CRATE_DEF_ID; +use rustc_hir as hir; +use rustc_hir::intravisit::Visitor; +use rustc_hir::HirId; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; -use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; +use rustc_session::lint; +use rustc_span::DUMMY_SP; +use rustc_span::{def_id::LocalDefId, Span}; -enum DivergingFallbackBehavior { +#[derive(Copy, Clone)] +pub enum DivergingFallbackBehavior { /// Always fallback to `()` (aka "always spontaneous decay") FallbackToUnit, /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken. @@ -78,9 +84,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { return false; } - let diverging_behavior = self.diverging_fallback_behavior(); - let diverging_fallback = - self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior); + let diverging_fallback = self + .calculate_diverging_fallback(&unresolved_variables, self.diverging_fallback_behavior); // We do fallback in two passes, to try to generate // better error messages. @@ -94,32 +99,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fallback_occurred } - fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior { - let Some((mode, span)) = self - .tcx - .get_attr(CRATE_DEF_ID, sym::rustc_never_type_mode) - .map(|attr| (attr.value_str().unwrap(), attr.span)) - else { - if self.tcx.features().never_type_fallback { - return DivergingFallbackBehavior::FallbackToNiko; - } - - return DivergingFallbackBehavior::FallbackToUnit; - }; - - match mode { - sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit, - sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko, - sym::fallback_to_never => DivergingFallbackBehavior::FallbackToNever, - sym::no_fallback => DivergingFallbackBehavior::NoFallback, - _ => { - self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, `fallback_to_never` and `no_fallback`)")); - - DivergingFallbackBehavior::FallbackToUnit - } - } - } - fn fallback_effects(&self) -> bool { let unsolved_effects = self.unsolved_effects(); @@ -131,7 +110,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // that field is only used for type fallback diagnostics. for effect in unsolved_effects { let expected = self.tcx.consts.true_; - let cause = self.misc(rustc_span::DUMMY_SP); + let cause = self.misc(DUMMY_SP); match self.at(&cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, effect) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); @@ -194,11 +173,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { }; debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback); - let span = self - .infcx - .type_var_origin(ty) - .map(|origin| origin.span) - .unwrap_or(rustc_span::DUMMY_SP); + let span = self.infcx.type_var_origin(ty).map(|origin| origin.span).unwrap_or(DUMMY_SP); self.demand_eqtype(span, ty, fallback); self.fallback_has_occurred.set(true); true @@ -328,7 +303,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { debug!( "calculate_diverging_fallback: root_vid={:?} reaches {:?}", root_vid, - coercion_graph.depth_first_search(root_vid).collect::>() + graph::depth_first_search(&coercion_graph, root_vid).collect::>() ); // drain the iterator to visit all nodes reachable from this node @@ -367,15 +342,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // reach a member of N. If so, it falls back to `()`. Else // `!`. let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len()); + let unsafe_infer_vars = OnceCell::new(); for &diverging_vid in &diverging_vids { let diverging_ty = Ty::new_var(self.tcx, diverging_vid); let root_vid = self.root_var(diverging_vid); - let can_reach_non_diverging = coercion_graph - .depth_first_search(root_vid) + let can_reach_non_diverging = graph::depth_first_search(&coercion_graph, root_vid) .any(|n| roots_reachable_from_non_diverging.visited(n)); let infer_var_infos: UnordBag<_> = self - .inh .infer_var_info .borrow() .items() @@ -388,11 +362,51 @@ impl<'tcx> FnCtxt<'_, 'tcx> { output: infer_var_infos.items().any(|info| info.output), }; + let mut fallback_to = |ty| { + let unsafe_infer_vars = unsafe_infer_vars.get_or_init(|| { + let unsafe_infer_vars = compute_unsafe_infer_vars(self.root_ctxt, self.body_id); + debug!(?unsafe_infer_vars); + unsafe_infer_vars + }); + + let affected_unsafe_infer_vars = + graph::depth_first_search_as_undirected(&coercion_graph, root_vid) + .filter_map(|x| unsafe_infer_vars.get(&x).copied()) + .collect::>(); + + for (hir_id, span, reason) in affected_unsafe_infer_vars { + self.tcx.emit_node_span_lint( + lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, + hir_id, + span, + match reason { + UnsafeUseReason::Call => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Call + } + UnsafeUseReason::Method => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Method + } + UnsafeUseReason::Path => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Path + } + UnsafeUseReason::UnionField => { + errors::NeverTypeFallbackFlowingIntoUnsafe::UnionField + } + UnsafeUseReason::Deref => { + errors::NeverTypeFallbackFlowingIntoUnsafe::Deref + } + }, + ); + } + + diverging_fallback.insert(diverging_ty, ty); + }; + use DivergingFallbackBehavior::*; match behavior { FallbackToUnit => { debug!("fallback to () - legacy: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.unit); + fallback_to(self.tcx.types.unit); } FallbackToNiko => { if found_infer_var_info.self_in_trait && found_infer_var_info.output { @@ -421,13 +435,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // set, see the relationship finding module in // compiler/rustc_trait_selection/src/traits/relationships.rs. debug!("fallback to () - found trait and projection: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.unit); + fallback_to(self.tcx.types.unit); } else if can_reach_non_diverging { debug!("fallback to () - reached non-diverging: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.unit); + fallback_to(self.tcx.types.unit); } else { debug!("fallback to ! - all diverging: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.never); + fallback_to(self.tcx.types.never); } } FallbackToNever => { @@ -435,7 +449,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { "fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}", diverging_vid ); - diverging_fallback.insert(diverging_ty, self.tcx.types.never); + fallback_to(self.tcx.types.never); } NoFallback => { debug!( @@ -451,7 +465,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// Returns a graph whose nodes are (unresolved) inference variables and where /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`. - fn create_coercion_graph(&self) -> VecGraph { + fn create_coercion_graph(&self) -> VecGraph { let pending_obligations = self.fulfillment_cx.borrow_mut().pending_obligations(); debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations); let coercion_edges: Vec<(ty::TyVid, ty::TyVid)> = pending_obligations @@ -470,17 +484,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // // In practice currently the two ways that this happens is // coercion and subtyping. - let (a, b) = if let ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) = atom { - (a, b) - } else if let ty::PredicateKind::Subtype(ty::SubtypePredicate { - a_is_expected: _, - a, - b, - }) = atom - { - (a, b) - } else { - return None; + let (a, b) = match atom { + ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b), + ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { + (a, b) + } + _ => return None, }; let a_vid = self.root_vid(a)?; @@ -490,6 +499,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .collect(); debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges); let num_ty_vars = self.num_ty_vars(); + VecGraph::new(num_ty_vars, coercion_edges) } @@ -498,3 +508,166 @@ impl<'tcx> FnCtxt<'_, 'tcx> { Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) } } + +#[derive(Debug, Copy, Clone)] +pub(crate) enum UnsafeUseReason { + Call, + Method, + Path, + UnionField, + Deref, +} + +/// Finds all type variables which are passed to an `unsafe` operation. +/// +/// For example, for this function `f`: +/// ```ignore (demonstrative) +/// fn f() { +/// unsafe { +/// let x /* ?X */ = core::mem::zeroed(); +/// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason +/// +/// let y = core::mem::zeroed::>(); +/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason +/// } +/// } +/// ``` +/// +/// `compute_unsafe_infer_vars` will return `{ id(?X) -> (hir_id, span, Call) }` +fn compute_unsafe_infer_vars<'a, 'tcx>( + root_ctxt: &'a TypeckRootCtxt<'tcx>, + body_id: LocalDefId, +) -> UnordMap { + let body_id = + root_ctxt.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); + let body = root_ctxt.tcx.hir().body(body_id); + let mut res = UnordMap::default(); + + struct UnsafeInferVarsVisitor<'a, 'tcx, 'r> { + root_ctxt: &'a TypeckRootCtxt<'tcx>, + res: &'r mut UnordMap, + } + + impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_, '_> { + fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) { + let typeck_results = self.root_ctxt.typeck_results.borrow(); + + match ex.kind { + hir::ExprKind::MethodCall(..) => { + if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id) + && let method_ty = self.root_ctxt.tcx.type_of(def_id).instantiate_identity() + && let sig = method_ty.fn_sig(self.root_ctxt.tcx) + && let hir::Unsafety::Unsafe = sig.unsafety() + { + let mut collector = InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::Method), + res: self.res, + }; + + // Collect generic arguments (incl. `Self`) of the method + typeck_results + .node_args(ex.hir_id) + .types() + .for_each(|t| t.visit_with(&mut collector)); + } + } + + hir::ExprKind::Call(func, ..) => { + let func_ty = typeck_results.expr_ty(func); + + if func_ty.is_fn() + && let sig = func_ty.fn_sig(self.root_ctxt.tcx) + && let hir::Unsafety::Unsafe = sig.unsafety() + { + let mut collector = InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::Call), + res: self.res, + }; + + // Try collecting generic arguments of the function. + // Note that we do this below for any paths (that don't have to be called), + // but there we do it with a different span/reason. + // This takes priority. + typeck_results + .node_args(func.hir_id) + .types() + .for_each(|t| t.visit_with(&mut collector)); + + // Also check the return type, for cases like `returns_unsafe_fn_ptr()()` + sig.output().visit_with(&mut collector); + } + } + + // Check paths which refer to functions. + // We do this, instead of only checking `Call` to make sure the lint can't be + // avoided by storing unsafe function in a variable. + hir::ExprKind::Path(_) => { + let ty = typeck_results.expr_ty(ex); + + // If this path refers to an unsafe function, collect inference variables which may affect it. + // `is_fn` excludes closures, but those can't be unsafe. + if ty.is_fn() + && let sig = ty.fn_sig(self.root_ctxt.tcx) + && let hir::Unsafety::Unsafe = sig.unsafety() + { + let mut collector = InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::Path), + res: self.res, + }; + + // Collect generic arguments of the function + typeck_results + .node_args(ex.hir_id) + .types() + .for_each(|t| t.visit_with(&mut collector)); + } + } + + hir::ExprKind::Unary(hir::UnOp::Deref, pointer) => { + if let ty::RawPtr(pointee, _) = typeck_results.expr_ty(pointer).kind() { + pointee.visit_with(&mut InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::Deref), + res: self.res, + }); + } + } + + hir::ExprKind::Field(base, _) => { + let base_ty = typeck_results.expr_ty(base); + + if base_ty.is_union() { + typeck_results.expr_ty(ex).visit_with(&mut InferVarCollector { + value: (ex.hir_id, ex.span, UnsafeUseReason::UnionField), + res: self.res, + }); + } + } + + _ => (), + }; + + hir::intravisit::walk_expr(self, ex); + } + } + + struct InferVarCollector<'r, V> { + value: V, + res: &'r mut UnordMap, + } + + impl<'tcx, V: Copy> ty::TypeVisitor> for InferVarCollector<'_, V> { + fn visit_ty(&mut self, t: Ty<'tcx>) { + if let Some(vid) = t.ty_vid() { + _ = self.res.try_insert(vid, self.value); + } else { + t.super_visit_with(self) + } + } + } + + UnsafeInferVarsVisitor { root_ctxt, res: &mut res }.visit_expr(&body.value); + + debug!(?res, "collected the following unsafe vars for {body_id:?}"); + + res +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index dd44fdd889328..2060e08aacf97 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -3,20 +3,20 @@ use crate::errors::CtorIsPrivate; use crate::method::{self, MethodCallee, SelfSource}; use crate::rvalue_scopes; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; -use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, GenericArg, Node, QPath}; -use rustc_hir_analysis::astconv::generics::{ - check_generic_arg_count_for_call, create_args_for_parent_generic_args, +use rustc_hir::{ExprKind, GenericArg, HirId, Node, QPath}; +use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend; +use rustc_hir_analysis::hir_ty_lowering::generics::{ + check_generic_arg_count_for_call, lower_generic_args, }; -use rustc_hir_analysis::astconv::{ - AstConv, CreateInstantiationsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, IsMethodCall, PathSeg, +use rustc_hir_analysis::hir_ty_lowering::{ + ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgsLowerer, + GenericPathSegment, HirTyLowerer, IsMethodCall, }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; @@ -46,7 +46,7 @@ use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. - pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) { + pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { // FIXME: Combine these two 'if' expressions into one once // let chains are implemented if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { @@ -86,7 +86,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(-Znext-solver): A lot of the calls to this method should // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead. #[instrument(skip(self), level = "debug", ret)] - pub(in super::super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { + pub(crate) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { // No Infer()? Nothing needs doing. if !ty.has_non_region_infer() { debug!("no inference var, nothing needs doing"); @@ -108,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_vars_if_possible(ty) } - pub(in super::super) fn record_deferred_call_resolution( + pub(crate) fn record_deferred_call_resolution( &self, closure_def_id: LocalDefId, r: DeferredCallResolution<'tcx>, @@ -117,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deferred_call_resolutions.entry(closure_def_id).or_default().push(r); } - pub(in super::super) fn remove_deferred_call_resolutions( + pub(crate) fn remove_deferred_call_resolutions( &self, closure_def_id: LocalDefId, ) -> Vec> { @@ -129,16 +129,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{self:p}") } - pub fn local_ty(&self, span: Span, nid: hir::HirId) -> Ty<'tcx> { + pub fn local_ty(&self, span: Span, nid: HirId) -> Ty<'tcx> { self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| { span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid)) }) } #[inline] - pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { + pub fn write_ty(&self, id: HirId, ty: Ty<'tcx>) { debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag()); - self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); + let mut typeck = self.typeck_results.borrow_mut(); + let mut node_ty = typeck.node_types_mut(); + if let Some(ty) = node_ty.get(id) + && let Err(e) = ty.error_reported() + { + // Do not overwrite nodes that were already marked as `{type error}`. This allows us to + // silence unnecessary errors from obligations that were set earlier than a type error + // was produced, but that is overwritten by later analysis. This happens in particular + // for `Sized` obligations introduced in gather_locals. (#117846) + self.set_tainted_by_errors(e); + return; + } + + node_ty.insert(id, ty); if let Err(e) = ty.error_reported() { self.set_tainted_by_errors(e); @@ -147,7 +160,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn write_field_index( &self, - hir_id: hir::HirId, + hir_id: HirId, index: FieldIdx, nested_fields: Vec<(Ty<'tcx>, FieldIdx)>, ) { @@ -158,9 +171,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - pub(in super::super) fn write_resolution( + pub(crate) fn write_resolution( &self, - hir_id: hir::HirId, + hir_id: HirId, r: Result<(DefKind, DefId), ErrorGuaranteed>, ) { self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); @@ -169,7 +182,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self))] pub fn write_method_call_and_enforce_effects( &self, - hir_id: hir::HirId, + hir_id: HirId, span: Span, method: MethodCallee<'tcx>, ) { @@ -178,7 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_args(hir_id, method.args); } - pub fn write_args(&self, node_id: hir::HirId, args: GenericArgsRef<'tcx>) { + pub fn write_args(&self, node_id: HirId, args: GenericArgsRef<'tcx>) { if !args.is_empty() { debug!("write_args({:?}, {:?}) in fcx {}", node_id, args, self.tag()); @@ -196,7 +209,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub fn write_user_type_annotation_from_args( &self, - hir_id: hir::HirId, + hir_id: HirId, def_id: DefId, args: GenericArgsRef<'tcx>, user_self_ty: Option>, @@ -216,7 +229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub fn write_user_type_annotation( &self, - hir_id: hir::HirId, + hir_id: HirId, canonical_user_type_annotation: CanonicalUserType<'tcx>, ) { debug!("fcx {}", self.tag()); @@ -265,13 +278,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Entry::Occupied(mut entry) => { debug!(" - composing on top of {:?}", entry.get()); - match (&entry.get()[..], &adj[..]) { - // Applying any adjustment on top of a NeverToAny - // is a valid NeverToAny adjustment, because it can't - // be reached. - (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return, + match (&mut entry.get_mut()[..], &adj[..]) { ( - &[ + [Adjustment { kind: Adjust::NeverToAny, target }], + &[.., Adjustment { target: new_target, .. }], + ) => { + // NeverToAny coercion can target any type, so instead of adding a new + // adjustment on top we can change the target. + // + // This is required for things like `a == a` (where `a: !`) to produce + // valid MIR -- we need borrow adjustment from things like `==` to change + // the type to `&!` (or `&()` depending on the fallback). This might be + // relevant even in unreachable code. + *target = new_target; + } + + ( + &mut [ Adjustment { kind: Adjust::Deref(_), .. }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, ], @@ -280,11 +303,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .., // Any following adjustments are allowed. ], ) => { - // A reborrow has no effect before a dereference. + // A reborrow has no effect before a dereference, so we can safely replace adjustments. + *entry.get_mut() = adj; } - // FIXME: currently we never try to compose autoderefs - // and ReifyFnPointer/UnsafeFnPointer, but we could. + _ => { + // FIXME: currently we never try to compose autoderefs + // and ReifyFnPointer/UnsafeFnPointer, but we could. self.dcx().span_delayed_bug( expr.span, format!( @@ -294,9 +319,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adj ), ); + + *entry.get_mut() = adj; } } - *entry.get_mut() = adj; } } @@ -309,7 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Instantiates and normalizes the bounds for a given item - pub(in super::super) fn instantiate_bounds( + pub(crate) fn instantiate_bounds( &self, span: Span, def_id: DefId, @@ -322,7 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { result } - pub(in super::super) fn normalize(&self, span: Span, value: T) -> T + pub(crate) fn normalize(&self, span: Span, value: T) -> T where T: TypeFoldable>, { @@ -381,20 +407,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn to_ty(&self, ast_t: &hir::Ty<'tcx>) -> LoweredTy<'tcx> { - let t = self.astconv().ast_ty_to_ty(ast_t); - self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); - LoweredTy::from_raw(self, ast_t.span, t) + pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> LoweredTy<'tcx> { + let ty = self.lowerer().lower_ty(hir_ty); + self.register_wf_obligation(ty.into(), hir_ty.span, traits::WellFormed(None)); + LoweredTy::from_raw(self, hir_ty.span, ty) } - pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - let ty = self.to_ty(ast_ty); - debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); + #[instrument(level = "debug", skip_all)] + pub fn lower_ty_saving_user_provided_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + let ty = self.lower_ty(hir_ty); + debug!(?ty); if Self::can_contain_user_lifetime_bounds(ty.raw) { let c_ty = self.canonicalize_response(UserType::Ty(ty.raw)); - debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); - self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); + debug!(?c_ty); + self.typeck_results.borrow_mut().user_provided_types_mut().insert(hir_ty.hir_id, c_ty); } ty.normalized @@ -411,7 +438,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { + pub fn lower_array_length(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { match length { hir::ArrayLen::Infer(inf) => self.ct_infer(self.tcx.types.usize, None, inf.span), hir::ArrayLen::Body(anon_const) => { @@ -423,20 +450,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn const_arg_to_const( - &self, - ast_c: &hir::AnonConst, - param_def_id: DefId, - ) -> ty::Const<'tcx> { - let did = ast_c.def_id; + pub fn lower_const_arg(&self, hir_ct: &hir::AnonConst, param_def_id: DefId) -> ty::Const<'tcx> { + let did = hir_ct.def_id; self.tcx.feed_anon_const_type(did, self.tcx.type_of(param_def_id)); - let c = ty::Const::from_anon_const(self.tcx, did); + let ct = ty::Const::from_anon_const(self.tcx, did); self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), + ct.into(), + self.tcx.hir().span(hir_ct.hir_id), ObligationCauseCode::WellFormed(None), ); - c + ct } // If the type given by the user has free regions, save it for later, since @@ -450,10 +473,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { where T: TypeVisitable>, { - t.has_free_regions() || t.has_projections() || t.has_infer_types() + t.has_free_regions() || t.has_aliases() || t.has_infer_types() } - pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { + pub fn node_ty(&self, id: HirId) -> Ty<'tcx> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => t, None if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e), @@ -467,7 +490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn node_ty_opt(&self, id: hir::HirId) -> Option> { + pub fn node_ty_opt(&self, id: HirId) -> Option> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => Some(t), None if let Some(e) = self.tainted_by_errors() => Some(Ty::new_error(self.tcx, e)), @@ -513,10 +536,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.normalize(span, field.ty(self.tcx, args)) } - pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) { + pub(crate) fn resolve_rvalue_scopes(&self, def_id: DefId) { let scope_tree = self.tcx.region_scope_tree(def_id); let rvalue_scopes = { rvalue_scopes::resolve_rvalue_scopes(self, scope_tree, def_id) }; - let mut typeck_results = self.inh.typeck_results.borrow_mut(); + let mut typeck_results = self.typeck_results.borrow_mut(); typeck_results.rvalue_scopes = rvalue_scopes; } @@ -529,7 +552,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// We must not attempt to select obligations after this method has run, or risk query cycle /// ICE. #[instrument(level = "debug", skip(self))] - pub(in super::super) fn resolve_coroutine_interiors(&self) { + pub(crate) fn resolve_coroutine_interiors(&self) { // Try selecting all obligations that are not blocked on inference variables. // Once we start unifying coroutine witnesses, trying to select obligations on them will // trigger query cycle ICEs, as doing so requires MIR. @@ -550,9 +573,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; @@ -560,17 +587,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligations .extend(self.fulfillment_cx.borrow_mut().drain_unstalled_obligations(&self.infcx)); - let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause)).collect(); - debug!(?obligations); - self.typeck_results - .borrow_mut() - .coroutine_interior_predicates - .insert(expr_def_id, obligations); + let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause)); + self.typeck_results.borrow_mut().coroutine_stalled_predicates.extend(obligations); } } #[instrument(skip(self), level = "debug")] - pub(in super::super) fn report_ambiguity_errors(&self) { + pub(crate) fn report_ambiguity_errors(&self) { let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(self); if !errors.is_empty() { @@ -585,7 +608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Select as many obligations as we can at present. - pub(in super::super) fn select_obligations_where_possible( + pub(crate) fn select_obligations_where_possible( &self, mutate_fulfillment_errors: impl Fn(&mut Vec>), ) { @@ -601,7 +624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// returns a type of `&T`, but the actual type we assign to the /// *expression* is `T`. So this function just peels off the return /// type by one layer to yield `T`. - pub(in super::super) fn make_overloaded_place_return_type( + pub(crate) fn make_overloaded_place_return_type( &self, method: MethodCallee<'tcx>, ) -> ty::TypeAndMut<'tcx> { @@ -612,67 +635,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ret_ty.builtin_deref(true).unwrap() } - #[instrument(skip(self), level = "debug")] - fn self_type_matches_expected_vid(&self, self_ty: Ty<'tcx>, expected_vid: ty::TyVid) -> bool { - let self_ty = self.shallow_resolve(self_ty); - debug!(?self_ty); - - match *self_ty.kind() { - ty::Infer(ty::TyVar(found_vid)) => { - let found_vid = self.root_var(found_vid); - debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); - expected_vid == found_vid - } - _ => false, - } - } - - #[instrument(skip(self), level = "debug")] - pub(in super::super) fn obligations_for_self_ty<'b>( - &'b self, - self_ty: ty::TyVid, - ) -> impl DoubleEndedIterator> + Captures<'tcx> + 'b - { - let ty_var_root = self.root_var(self_ty); - trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations()); - - self.fulfillment_cx.borrow().pending_obligations().into_iter().filter_map( - move |obligation| match &obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) - if self.self_type_matches_expected_vid( - data.projection_ty.self_ty(), - ty_var_root, - ) => - { - Some(obligation) - } - ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) - if self.self_type_matches_expected_vid(data.self_ty(), ty_var_root) => - { - Some(obligation) - } - - ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::NormalizesTo(..) - | ty::PredicateKind::AliasRelate(..) - | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Ambiguous => None, - }, - ) - } - - pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { + pub(crate) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { let sized_did = self.tcx.lang_items().sized_trait(); - self.obligations_for_self_ty(self_ty).any(|obligation| { + self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { Some(data.def_id()) == sized_did @@ -682,7 +647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } - pub(in super::super) fn err_args(&self, len: usize) -> Vec> { + pub(crate) fn err_args(&self, len: usize) -> Vec> { let ty_error = Ty::new_misc_error(self.tcx); vec![ty_error; len] } @@ -690,7 +655,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Unifies the output type with the expected type early, for more coercions /// and forward type information on the input expressions. #[instrument(skip(self, call_span), level = "debug")] - pub(in super::super) fn expected_inputs_for_expected_output( + pub(crate) fn expected_inputs_for_expected_output( &self, call_span: Span, expected_ret: Expectation<'tcx>, @@ -700,33 +665,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let formal_ret = self.resolve_vars_with_obligations(formal_ret); let ret_ty = expected_ret.only_has_type(self)?; - // HACK(oli-obk): This is a hack to keep RPIT and TAIT in sync wrt their behaviour. - // Without it, the inference - // variable will get instantiated with the opaque type. The inference variable often - // has various helpful obligations registered for it that help closures figure out their - // signature. If we infer the inference var to the opaque type, the closure won't be able - // to find those obligations anymore, and it can't necessarily find them from the opaque - // type itself. We could be more powerful with inference if we *combined* the obligations - // so that we got both the obligations from the opaque type and the ones from the inference - // variable. That will accept more code than we do right now, so we need to carefully consider - // the implications. - // Note: this check is pessimistic, as the inference type could be matched with something other - // than the opaque type, but then we need a new `TypeRelation` just for this specific case and - // can't re-use `sup` below. - // See tests/ui/impl-trait/hidden-type-is-opaque.rs and - // tests/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path. - if formal_ret.has_infer_types() { - for ty in ret_ty.walk() { - if let ty::GenericArgKind::Type(ty) = ty.unpack() - && let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *ty.kind() - && let Some(def_id) = def_id.as_local() - && self.opaque_type_origin(def_id).is_some() - { - return None; - } - } - } - let expect_args = self .fudge_inference_if_ok(|| { let ocx = ObligationCtxt::new(self); @@ -750,11 +688,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expect_args } - pub(in super::super) fn resolve_lang_item_path( + pub(crate) fn resolve_lang_item_path( &self, lang_item: hir::LangItem, span: Span, - hir_id: hir::HirId, + hir_id: HirId, ) -> (Res, Ty<'tcx>) { let def_id = self.tcx.require_lang_item(lang_item, Some(span)); let def_kind = self.tcx.def_kind(def_id); @@ -802,7 +740,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn resolve_ty_and_res_fully_qualified_call( &self, qpath: &'tcx QPath<'tcx>, - hir_id: hir::HirId, + hir_id: HirId, span: Span, args: Option<&'tcx [hir::Expr<'tcx>]>, ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { @@ -814,12 +752,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { QPath::Resolved(ref opt_qself, path) => { return ( path.res, - opt_qself.as_ref().map(|qself| self.to_ty(qself)), + opt_qself.as_ref().map(|qself| self.lower_ty(qself)), path.segments, ); } QPath::TypeRelative(ref qself, ref segment) => { - // Don't use `self.to_ty`, since this will register a WF obligation. + // Don't use `self.lower_ty`, since this will register a WF obligation. // If we're trying to call a nonexistent method on a trait // (e.g. `MyTrait::missing_method`), then resolution will // give us a `QPath::TypeRelative` with a trait object as @@ -828,7 +766,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to be object-safe. // We manually call `register_wf_obligation` in the success path // below. - let ty = self.astconv().ast_ty_to_ty_in_path(qself); + let ty = self.lowerer().lower_ty_in_path(qself); (LoweredTy::from_raw(self, span, ty), qself, segment) } QPath::LangItem(..) => { @@ -929,10 +867,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// but we often want access to the parent function's signature. /// /// Otherwise, return false. - pub(in super::super) fn get_node_fn_decl( + pub(crate) 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, @@ -943,25 +881,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: @@ -991,12 +924,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, } @@ -1006,8 +934,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// suggestion can be made, `None` otherwise. pub fn get_fn_decl( &self, - blk_id: hir::HirId, - ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, bool)> { + blk_id: HirId, + ) -> 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| { @@ -1017,7 +945,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } - pub(in super::super) fn note_internal_mutation_in_method( + pub(crate) fn note_internal_mutation_in_method( &self, err: &mut Diag<'_>, expr: &hir::Expr<'_>, @@ -1102,13 +1030,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res: Res, span: Span, path_span: Span, - hir_id: hir::HirId, + hir_id: HirId, ) -> (Ty<'tcx>, Res) { let tcx = self.tcx; - let path_segs = match res { + let generic_segments = match res { Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => self.astconv().def_ids_for_value_path_segments( + Res::Def(kind, def_id) => self.lowerer().probe_generic_path_segments( segments, self_ty.map(|ty| ty.raw), kind, @@ -1140,6 +1068,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, span, container_id, + self.body_id.to_def_id(), ) { self.set_tainted_by_errors(e); } @@ -1165,16 +1094,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // provided (if any) into their appropriate spaces. We'll also report // errors if type parameters are provided in an inappropriate place. - let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = self.astconv().prohibit_generics( + let indices: FxHashSet<_> = + generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); + let generics_err = self.lowerer().prohibit_generic_args( segments.iter().enumerate().filter_map(|(index, seg)| { - if !generic_segs.contains(&index) || is_alias_variant_ctor { - Some(seg) - } else { - None - } + if !indices.contains(&index) || is_alias_variant_ctor { Some(seg) } else { None } }), - |_| {}, + GenericsArgsErrExtend::None, ); if let Res::Local(hid) = res { @@ -1184,7 +1110,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return (ty, res); } - if generics_has_err { + if let Err(_) = generics_err { // Don't try to infer type parameters when prohibited generic arguments were given. user_self_ty = None; } @@ -1199,7 +1125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut infer_args_for_err = FxHashSet::default(); let mut explicit_late_bound = ExplicitLateBound::No; - for &PathSeg(def_id, index) in &path_segs { + for &GenericPathSegment(def_id, index) in &generic_segments { let seg = &segments[index]; let generics = tcx.generics_of(def_id); @@ -1220,8 +1146,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let has_self = - path_segs.last().is_some_and(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self); + let has_self = generic_segments + .last() + .is_some_and(|GenericPathSegment(def_id, _)| tcx.generics_of(*def_id).has_self); let (res, self_ctor_args) = if let Res::SelfCtor(impl_def_id) = res { let ty = LoweredTy::from_raw( @@ -1281,22 +1208,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, }; - struct CreateCtorInstantiationsContext<'a, 'tcx> { + struct CtorGenericArgsCtxt<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, - path_segs: &'a [PathSeg], + generic_segments: &'a [GenericPathSegment], infer_args_for_err: &'a FxHashSet, segments: &'tcx [hir::PathSegment<'tcx>], } - impl<'tcx, 'a> CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> - for CreateCtorInstantiationsContext<'a, 'tcx> - { + impl<'tcx, 'a> GenericArgsLowerer<'a, 'tcx> for CtorGenericArgsCtxt<'a, 'tcx> { fn args_for_def_id( &mut self, def_id: DefId, ) -> (Option<&'a hir::GenericArgs<'tcx>>, bool) { - if let Some(&PathSeg(_, index)) = - self.path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) + if let Some(&GenericPathSegment(_, index)) = + self.generic_segments.iter().find(|&GenericPathSegment(did, _)| *did == def_id) { // If we've encountered an `impl Trait`-related error, we're just // going to infer the arguments for better error messages. @@ -1319,13 +1244,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.fcx.astconv().ast_region_to_region(lt, Some(param)).into() + self.fcx.lowerer().lower_lifetime(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.fcx.to_ty(ty).raw.into() + self.fcx.lower_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.fcx.const_arg_to_const(&ct.value, param.def_id).into() + self.fcx.lower_const_arg(&ct.value, param.def_id).into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.fcx.ty_infer(Some(param), inf.span).into() @@ -1407,17 +1332,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let args_raw = self_ctor_args.unwrap_or_else(|| { - create_args_for_parent_generic_args( + lower_generic_args( tcx, def_id, &[], has_self, self_ty.map(|s| s.raw), &arg_count, - &mut CreateCtorInstantiationsContext { + &mut CtorGenericArgsCtxt { fcx: self, span, - path_segs: &path_segs, + generic_segments: &generic_segments, infer_args_for_err: &infer_args_for_err, segments, }, @@ -1476,7 +1401,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, def_id: DefId, args: GenericArgsRef<'tcx>, - hir_id: hir::HirId, + hir_id: HirId, ) { self.add_required_obligations_with_code(span, def_id, args, |idx, span| { if span.is_dummy() { @@ -1565,9 +1490,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(in super::super) fn with_breakable_ctxt R, R>( + pub(crate) fn with_breakable_ctxt R, R>( &self, - id: hir::HirId, + id: HirId, ctxt: BreakableCtxt<'tcx>, f: F, ) -> (BreakableCtxt<'tcx>, R) { @@ -1591,7 +1516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Instantiate a QueryResponse in a probe context, without a /// good ObligationCause. - pub(in super::super) fn probe_instantiate_query_response( + pub(crate) fn probe_instantiate_query_response( &self, span: Span, original_values: &OriginalQueryValues<'tcx>, @@ -1606,7 +1531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Returns `true` if an expression is contained inside the LHS of an assignment expression. - pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { + pub(crate) fn expr_in_place(&self, mut expr_id: HirId) -> bool { let mut contained_in_place = false; while let hir::Node::Expr(parent_expr) = self.tcx.parent_hir_node(expr_id) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index a9a5a89a41363..a718760f4d895 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -2,7 +2,7 @@ use crate::FnCtxt; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_infer::{infer::type_variable::TypeVariableOriginKind, traits::ObligationCauseCode}; +use rustc_infer::traits::ObligationCauseCode; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::{symbol::kw, Span}; use rustc_trait_selection::traits; @@ -340,7 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { type Result = ControlFlow>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { if let Some(origin) = self.0.type_var_origin(ty) - && let TypeVariableOriginKind::TypeParameterDefinition(_, def_id) = origin.kind + && let Some(def_id) = origin.param_def_id && let generics = self.0.tcx.generics_of(self.1) && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id) && let Some(arg) = @@ -367,7 +367,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }), ) = error.code && let ty::Closure(def_id, _) | ty::Coroutine(def_id, ..) = - expected_trait_ref.skip_binder().self_ty().kind() + expected_trait_ref.self_ty().kind() && span.overlaps(self.tcx.def_span(*def_id)) { true diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 536d44a0ccb85..e45e0884affb1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -17,20 +17,21 @@ use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ - codes::*, pluralize, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, + a_or_an, codes::*, display_list_with_comma_and, pluralize, Applicability, Diag, + ErrorGuaranteed, MultiSpan, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, Node, QPath}; -use rustc_hir_analysis::astconv::AstConv; +use rustc_hir::{ExprKind, HirId, Node, QPath}; use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_hir_analysis::structured_errors::StructuredDiag; use rustc_index::IndexVec; use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::TypeTrace; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::traits::ObligationCauseCode::ExprBindingObligation; @@ -45,6 +46,29 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext} use std::iter; use std::mem; +#[derive(Clone, Copy, Default)] +pub enum DivergingBlockBehavior { + /// This is the current stable behavior: + /// + /// ```rust + /// { + /// return; + /// } // block has type = !, even though we are supposedly dropping it with `;` + /// ``` + #[default] + Never, + + /// Alternative behavior: + /// + /// ```ignore (very-unstable-new-attribute) + /// #![rustc_never_type_options(diverging_block_default = "unit")] + /// { + /// return; + /// } // block has type = (), since we are dropping `!` from `return` with `;` + /// ``` + Unit, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { // don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors @@ -273,22 +297,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 3. Check if the formal type is a supertype of the checked one // and register any such obligations for future type checks let supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup( - DefineOpaqueTypes::No, + DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty, ); - let subtyping_error = match supertype_error { + + // If neither check failed, the types are compatible + match supertype_error { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); - None + Compatibility::Compatible } - Err(err) => Some(err), - }; - - // If neither check failed, the types are compatible - match subtyping_error { - None => Compatibility::Compatible, - Some(_) => Compatibility::Incompatible(subtyping_error), + Err(err) => Compatibility::Incompatible(Some(err)), } }; @@ -663,7 +683,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Using probe here, since we don't want this subtyping to affect inference. let subtyping_error = self.probe(|_| { self.at(&self.misc(arg_span), self.param_env) - .sup(DefineOpaqueTypes::No, formal_input_ty, coerced_ty) + .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty) .err() }); @@ -795,6 +815,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr, None, Some(mismatch_idx), + &matched_inputs, + &formal_and_expected_inputs, is_method, ); suggest_confusable(&mut err); @@ -881,6 +903,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect")); + self.label_generic_mismatches( + &mut err, + fn_def_id, + &matched_inputs, + &provided_arg_tys, + &formal_and_expected_inputs, + is_method, + ); + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind && provided_idx.as_usize() == expected_idx.as_usize() { @@ -909,6 +940,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr, Some(expected_ty), Some(expected_idx.as_usize()), + &matched_inputs, + &formal_and_expected_inputs, is_method, ); suggest_confusable(&mut err); @@ -1247,6 +1280,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + self.label_generic_mismatches( + &mut err, + fn_def_id, + &matched_inputs, + &provided_arg_tys, + &formal_and_expected_inputs, + is_method, + ); + // Incorporate the argument changes in the removal suggestion. // When a type is *missing*, and the rest are additional, we want to suggest these with a // multipart suggestion, but in order to do so we need to figure out *where* the arg that @@ -1294,7 +1336,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty, call_expr, None, None, is_method); + self.label_fn_like( + &mut err, + fn_def_id, + callee_ty, + call_expr, + None, + None, + &matched_inputs, + &formal_and_expected_inputs, + is_method, + ); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -1371,9 +1423,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg: &hir::Expr<'tcx>, err: &mut Diag<'tcx>, ) { - if let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Mut, .. }) = expected_ty.kind() - && let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Not, .. }) = - provided_ty.kind() + if let ty::RawPtr(_, hir::Mutability::Mut) = expected_ty.kind() + && let ty::RawPtr(_, hir::Mutability::Not) = provided_ty.kind() && let hir::ExprKind::Call(callee, _) = arg.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = callee.kind && let Res::Def(_, def_id) = path.res @@ -1438,7 +1489,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_struct_path( &self, qpath: &QPath<'tcx>, - hir_id: hir::HirId, + hir_id: HirId, ) -> Result<(&'tcx ty::VariantDef, Ty<'tcx>), ErrorGuaranteed> { let path_span = qpath.span(); let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); @@ -1503,7 +1554,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_decl_initializer( &self, - hir_id: hir::HirId, + hir_id: HirId, pat: &'tcx hir::Pat<'tcx>, init: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { @@ -1579,7 +1630,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + pub fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) { self.check_decl(local.into()); if local.pat.is_never_pattern() { self.diverges.set(Diverges::Always { @@ -1609,7 +1660,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::StmtKind::Item(_) => {} hir::StmtKind::Expr(ref expr) => { // Check with expected type of `()`. - self.check_expr_has_type_or_error(expr, Ty::new_unit(self.tcx), |err| { + self.check_expr_has_type_or_error(expr, self.tcx.types.unit, |err| { if expr.can_have_side_effects() { self.suggest_semicolon_at_end(expr.span, err); } @@ -1625,7 +1676,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { - let unit = Ty::new_unit(self.tcx); + let unit = self.tcx.types.unit; let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); // if the block produces a `!` value, that can always be @@ -1710,7 +1761,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // #41425 -- label the implicit `()` as being the // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { + if !self.diverges.get().is_always() + || matches!(self.diverging_block_behavior, DivergingBlockBehavior::Unit) + { // #50009 -- Do not point at the entire fn block span, point at the return type // span, as it is the cause of the requirement, and // `consider_hint_about_removing_semicolon` will point at the last expression @@ -1741,7 +1794,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { blk.span, blk.hir_id, expected_ty, - Ty::new_unit(self.tcx), + self.tcx.types.unit, ); } if !self.err_ctxt().consider_removing_semicolon( @@ -1765,7 +1818,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [ hir::Stmt { kind: - hir::StmtKind::Let(hir::Local { + hir::StmtKind::Let(hir::LetStmt { source: hir::LocalSource::AssignDesugar(_), .. @@ -1826,7 +1879,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } - fn parent_item_span(&self, id: hir::HirId) -> Option { + fn parent_item_span(&self, id: HirId) -> Option { let node = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(id).def_id); match node { Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) @@ -1844,7 +1897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. pub(crate) fn get_parent_fn_decl( &self, - blk_id: hir::HirId, + blk_id: HirId, ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { let parent = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id); self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident)) @@ -1886,17 +1939,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr.span } - fn overwrite_local_ty_if_err( - &self, - hir_id: hir::HirId, - pat: &'tcx hir::Pat<'tcx>, - ty: Ty<'tcx>, - ) { + fn overwrite_local_ty_if_err(&self, hir_id: HirId, pat: &'tcx hir::Pat<'tcx>, ty: Ty<'tcx>) { if let Err(guar) = ty.error_reported() { + struct OverwritePatternsWithError { + pat_hir_ids: Vec, + } + impl<'tcx> Visitor<'tcx> for OverwritePatternsWithError { + fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { + self.pat_hir_ids.push(p.hir_id); + hir::intravisit::walk_pat(self, p); + } + } // Override the types everywhere with `err()` to avoid knock on errors. let err = Ty::new_error(self.tcx, guar); self.write_ty(hir_id, err); self.write_ty(pat.hir_id, err); + let mut visitor = OverwritePatternsWithError { pat_hir_ids: vec![] }; + hir::intravisit::walk_pat(&mut visitor, pat); + // Mark all the subpatterns as `{type error}` as well. This allows errors for specific + // subpatterns to be silenced. + for hir_id in visitor.pat_hir_ids { + self.write_ty(hir_id, err); + } self.locals.borrow_mut().insert(hir_id, err); self.locals.borrow_mut().insert(pat.hir_id, err); } @@ -1908,20 +1972,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, qpath: &QPath<'tcx>, path_span: Span, - hir_id: hir::HirId, + hir_id: HirId, ) -> (Res, LoweredTy<'tcx>) { match *qpath { QPath::Resolved(ref maybe_qself, path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw); - let ty = self.astconv().res_to_ty(self_ty, path, hir_id, true); + let self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself).raw); + let ty = self.lowerer().lower_path(self_ty, path, hir_id, true); (path.res, LoweredTy::from_raw(self, path_span, ty)) } QPath::TypeRelative(qself, segment) => { - let ty = self.to_ty(qself); + let ty = self.lower_ty(qself); let result = self - .astconv() - .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true); + .lowerer() + .lower_assoc_path(hir_id, path_span, ty.raw, qself, segment, true); let ty = result .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); @@ -1947,12 +2011,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (span, code) in errors_causecode { self.dcx().try_steal_modify_and_emit_err(span, StashKey::MaybeForgetReturn, |err| { if let Some(fn_sig) = self.body_fn_sig() - && let ExprBindingObligation(_, _, hir_id, ..) = code + && let ExprBindingObligation(_, _, binding_hir_id, ..) = code && !fn_sig.output().is_unit() { let mut block_num = 0; let mut found_semi = false; - for (_, node) in self.tcx.hir().parent_iter(hir_id) { + for (hir_id, node) in self.tcx.hir().parent_iter(binding_hir_id) { + // Don't proceed into parent bodies + if hir_id.owner != binding_hir_id.owner { + break; + } match node { hir::Node::Stmt(stmt) => { if let hir::StmtKind::Semi(expr) = stmt.kind { @@ -2054,6 +2122,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Option>, // A specific argument should be labeled, instead of all of them expected_idx: Option, + matched_inputs: &IndexVec>, + formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, is_method: bool, ) { let Some(mut def_id) = callable_def_id else { @@ -2074,7 +2144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let callee_ty = callee_ty.peel_refs(); match *callee_ty.kind() { ty::Param(param) => { - let param = self.tcx.generics_of(self.body_id).type_param(¶m, self.tcx); + let param = self.tcx.generics_of(self.body_id).type_param(param, self.tcx); if param.kind.is_synthetic() { // if it's `impl Fn() -> ..` then just fall down to the def-id based logic def_id = param.def_id; @@ -2113,7 +2183,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [ callee_ty, self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, + param_def_id: None, span: rustc_span::DUMMY_SP, }), ], @@ -2145,21 +2215,164 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let mut spans: MultiSpan = def_span.into(); - let params = self + let params_with_generics = self.get_hir_params_with_generics(def_id, is_method); + let mut generics_with_unmatched_params = Vec::new(); + + let check_for_matched_generics = || { + if matched_inputs.iter().any(|x| x.is_some()) + && params_with_generics.iter().any(|x| x.0.is_some()) + { + for (idx, (generic, _)) in params_with_generics.iter().enumerate() { + // Param has to have a generic and be matched to be relevant + if matched_inputs[idx.into()].is_none() { + continue; + } + + let Some(generic) = generic else { + continue; + }; + + for unmatching_idx in idx + 1..params_with_generics.len() { + if matched_inputs[unmatching_idx.into()].is_none() + && let Some(unmatched_idx_param_generic) = + params_with_generics[unmatching_idx].0 + && unmatched_idx_param_generic.name.ident() == generic.name.ident() + { + // We found a parameter that didn't match that needed to + return true; + } + } + } + } + false + }; + + let check_for_matched_generics = check_for_matched_generics(); + + for (idx, (generic_param, param)) in + params_with_generics.iter().enumerate().filter(|(idx, _)| { + check_for_matched_generics + || expected_idx.map_or(true, |expected_idx| expected_idx == *idx) + }) + { + let Some(generic_param) = generic_param else { + spans.push_span_label(param.span, ""); + continue; + }; + + let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics + .iter() + .enumerate() + .filter(|(other_idx, (other_generic_param, _))| { + if *other_idx == idx { + return false; + } + let Some(other_generic_param) = other_generic_param else { + return false; + }; + if matched_inputs[idx.into()].is_none() + && matched_inputs[(*other_idx).into()].is_none() + { + return false; + } + if matched_inputs[idx.into()].is_some() + && matched_inputs[(*other_idx).into()].is_some() + { + return false; + } + other_generic_param.name.ident() == generic_param.name.ident() + }) + .map(|(other_idx, (_, other_param))| (other_idx, *other_param)) + .collect(); + + if !other_params_matched.is_empty() { + let other_param_matched_names: Vec = other_params_matched + .iter() + .map(|(_, other_param)| { + if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind { + format!("`{ident}`") + } else { + "{unknown}".to_string() + } + }) + .collect(); + + let matched_ty = self + .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) + .sort_string(self.tcx); + + if matched_inputs[idx.into()].is_some() { + spans.push_span_label( + param.span, + format!( + "{} {} to match the {} type of this parameter", + display_list_with_comma_and(&other_param_matched_names), + format!( + "need{}", + pluralize!(if other_param_matched_names.len() == 1 { + 0 + } else { + 1 + }) + ), + matched_ty, + ), + ); + } else { + spans.push_span_label( + param.span, + format!( + "this parameter needs to match the {} type of {}", + matched_ty, + display_list_with_comma_and(&other_param_matched_names), + ), + ); + } + generics_with_unmatched_params.push(generic_param); + } else { + spans.push_span_label(param.span, ""); + } + } + + for generic_param in self .tcx .hir() .get_if_local(def_id) - .and_then(|node| node.body_id()) + .and_then(|node| node.generics()) .into_iter() - .flat_map(|id| self.tcx.hir().body(id).params) - .skip(if is_method { 1 } else { 0 }); - - for (_, param) in params - .into_iter() - .enumerate() - .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx)) + .flat_map(|x| x.params) + .filter(|x| { + generics_with_unmatched_params.iter().any(|y| x.name.ident() == y.name.ident()) + }) { - spans.push_span_label(param.span, ""); + let param_idents_matching: Vec = params_with_generics + .iter() + .filter(|(generic, _)| { + if let Some(generic) = generic { + generic.name.ident() == generic_param.name.ident() + } else { + false + } + }) + .map(|(_, param)| { + if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind { + format!("`{ident}`") + } else { + "{unknown}".to_string() + } + }) + .collect(); + + if !param_idents_matching.is_empty() { + spans.push_span_label( + generic_param.span, + format!( + "{} all reference this parameter {}", + display_list_with_comma_and(¶m_idents_matching), + generic_param.name.ident().name, + ), + ); + } } err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id))); @@ -2220,6 +2433,115 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } + + fn label_generic_mismatches( + &self, + err: &mut Diag<'_>, + callable_def_id: Option, + matched_inputs: &IndexVec>, + provided_arg_tys: &IndexVec, Span)>, + formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, + is_method: bool, + ) { + let Some(def_id) = callable_def_id else { + return; + }; + + let params_with_generics = self.get_hir_params_with_generics(def_id, is_method); + + for (idx, (generic_param, _)) in params_with_generics.iter().enumerate() { + if matched_inputs[idx.into()].is_none() { + continue; + } + + let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.into()) else { + continue; + }; + + let Some(generic_param) = generic_param else { + continue; + }; + + let mut idxs_matched: Vec = vec![]; + for (other_idx, (_, _)) in params_with_generics.iter().enumerate().filter( + |(other_idx, (other_generic_param, _))| { + if *other_idx == idx { + return false; + } + let Some(other_generic_param) = other_generic_param else { + return false; + }; + if matched_inputs[(*other_idx).into()].is_some() { + return false; + } + other_generic_param.name.ident() == generic_param.name.ident() + }, + ) { + idxs_matched.push(other_idx.into()); + } + + if idxs_matched.is_empty() { + continue; + } + + let expected_display_type = self + .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) + .sort_string(self.tcx); + let label = if idxs_matched.len() == params_with_generics.len() - 1 { + format!( + "expected all arguments to be this {} type because they need to match the type of this parameter", + expected_display_type + ) + } else { + format!( + "expected some other arguments to be {} {} type to match the type of this parameter", + a_or_an(&expected_display_type), + expected_display_type, + ) + }; + + err.span_label(*matched_arg_span, label); + } + } + + fn get_hir_params_with_generics( + &self, + def_id: DefId, + is_method: bool, + ) -> Vec<(Option<&hir::GenericParam<'_>>, &hir::Param<'_>)> { + let fn_node = self.tcx.hir().get_if_local(def_id); + + let generic_params: Vec>> = fn_node + .and_then(|node| node.fn_decl()) + .into_iter() + .flat_map(|decl| decl.inputs) + .skip(if is_method { 1 } else { 0 }) + .map(|param| { + if let hir::TyKind::Path(QPath::Resolved( + _, + hir::Path { res: Res::Def(_, res_def_id), .. }, + )) = param.kind + { + fn_node + .and_then(|node| node.generics()) + .into_iter() + .flat_map(|generics| generics.params) + .find(|gen| &gen.def_id.to_def_id() == res_def_id) + } else { + None + } + }) + .collect(); + + let params: Vec<&hir::Param<'_>> = fn_node + .and_then(|node| node.body_id()) + .into_iter() + .flat_map(|id| self.tcx.hir().body(id).params) + .skip(if is_method { 1 } else { 0 }) + .collect(); + + generic_params.into_iter().zip(params).collect() + } } struct FindClosureArg<'tcx> { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs new file mode 100644 index 0000000000000..c2068d9f66b8f --- /dev/null +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -0,0 +1,140 @@ +//! A utility module to inspect currently ambiguous obligations in the current context. +use crate::rustc_middle::ty::TypeVisitableExt; +use crate::FnCtxt; +use rustc_infer::traits::solve::Goal; +use rustc_infer::traits::{self, ObligationCause}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; +use rustc_trait_selection::solve::inspect::ProofTreeInferCtxtExt; +use rustc_trait_selection::solve::inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Returns a list of all obligations whose self type has been unified + /// with the unconstrained type `self_ty`. + #[instrument(skip(self), level = "debug")] + pub(crate) fn obligations_for_self_ty( + &self, + self_ty: ty::TyVid, + ) -> Vec> { + if self.next_trait_solver() { + self.obligations_for_self_ty_next(self_ty) + } else { + let ty_var_root = self.root_var(self_ty); + let mut obligations = self.fulfillment_cx.borrow().pending_obligations(); + trace!("pending_obligations = {:#?}", obligations); + obligations + .retain(|obligation| self.predicate_has_self_ty(obligation.predicate, ty_var_root)); + obligations + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn predicate_has_self_ty( + &self, + predicate: ty::Predicate<'tcx>, + expected_vid: ty::TyVid, + ) -> bool { + match predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { + self.type_matches_expected_vid(expected_vid, data.self_ty()) + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + self.type_matches_expected_vid(expected_vid, data.projection_ty.self_ty()) + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous => false, + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn type_matches_expected_vid(&self, expected_vid: ty::TyVid, ty: Ty<'tcx>) -> bool { + let ty = self.shallow_resolve(ty); + debug!(?ty); + + match *ty.kind() { + ty::Infer(ty::TyVar(found_vid)) => { + self.root_var(expected_vid) == self.root_var(found_vid) + } + _ => false, + } + } + + pub(crate) fn obligations_for_self_ty_next( + &self, + self_ty: ty::TyVid, + ) -> Vec> { + let obligations = self.fulfillment_cx.borrow().pending_obligations(); + debug!(?obligations); + let mut obligations_for_self_ty = vec![]; + for obligation in obligations { + let mut visitor = NestedObligationsForSelfTy { + fcx: self, + self_ty, + obligations_for_self_ty: &mut obligations_for_self_ty, + root_cause: &obligation.cause, + }; + + let goal = Goal::new(self.tcx, obligation.param_env, obligation.predicate); + self.visit_proof_tree(goal, &mut visitor); + } + + obligations_for_self_ty.retain_mut(|obligation| { + obligation.predicate = self.resolve_vars_if_possible(obligation.predicate); + !obligation.predicate.has_placeholders() + }); + obligations_for_self_ty + } +} + +struct NestedObligationsForSelfTy<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + self_ty: ty::TyVid, + root_cause: &'a ObligationCause<'tcx>, + obligations_for_self_ty: &'a mut Vec>, +} + +impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> { + type Result = (); + + fn span(&self) -> Span { + self.root_cause.span + } + + fn config(&self) -> InspectConfig { + // Using an intentionally low depth to minimize the chance of future + // breaking changes in case we adapt the approach later on. This also + // avoids any hangs for exponentially growing proof trees. + InspectConfig { max_depth: 5 } + } + + fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { + let tcx = self.fcx.tcx; + let goal = inspect_goal.goal(); + if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { + self.obligations_for_self_ty.push(traits::Obligation::new( + tcx, + self.root_cause.clone(), + goal.param_env, + goal.predicate, + )); + } + + // If there's a unique way to prove a given goal, recurse into + // that candidate. This means that for `impl Trait for () {}` + // and a `(): Trait` goal we recurse into the impl and look at + // the nested `?0: FnOnce(u32)` goal. + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } + } +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 685b1af931e34..794b854ca5f95 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -2,23 +2,29 @@ mod _impl; mod adjust_fulfillment_errors; mod arg_matrix; mod checks; +mod inspect_obligations; mod suggestions; +use rustc_errors::ErrorGuaranteed; + use crate::coercion::DynamicCoerceMany; -use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited}; -use rustc_errors::{DiagCtxt, ErrorGuaranteed}; +use crate::fallback::DivergingFallbackBehavior; +use crate::fn_ctxt::checks::DivergingBlockBehavior; +use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; +use hir::def_id::CRATE_DEF_ID; +use rustc_errors::DiagCtxt; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir_analysis::astconv::AstConv; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer; use rustc_infer::infer::error_reporting::sub_relations::SubRelations; use rustc_infer::infer::error_reporting::TypeErrCtxt; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; +use rustc_middle::infer::unify_key::ConstVariableOrigin; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span, DUMMY_SP}; +use rustc_span::{self, sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; @@ -35,7 +41,7 @@ use std::ops::Deref; /// /// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt /// [`InferCtxt`]: infer::InferCtxt -pub struct FnCtxt<'a, 'tcx> { +pub(crate) struct FnCtxt<'a, 'tcx> { pub(super) body_id: LocalDefId, /// The parameter environment used for proving trait obligations @@ -105,17 +111,22 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) enclosing_breakables: RefCell>, - pub(super) inh: &'a Inherited<'tcx>, + pub(super) root_ctxt: &'a TypeckRootCtxt<'tcx>, pub(super) fallback_has_occurred: Cell, + + pub(super) diverging_fallback_behavior: DivergingFallbackBehavior, + pub(super) diverging_block_behavior: DivergingBlockBehavior, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn new( - inh: &'a Inherited<'tcx>, + root_ctxt: &'a TypeckRootCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx> { + let (diverging_fallback_behavior, diverging_block_behavior) = + parse_never_type_options_attr(root_ctxt.tcx); FnCtxt { body_id, param_env, @@ -129,8 +140,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { stack: Vec::new(), by_id: Default::default(), }), - inh, + root_ctxt, fallback_has_occurred: Cell::new(false), + diverging_fallback_behavior, + diverging_block_behavior, } } @@ -196,13 +209,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { - type Target = Inherited<'tcx>; + type Target = TypeckRootCtxt<'tcx>; fn deref(&self) -> &Self::Target { - self.inh + self.root_ctxt } } -impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { +impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> { fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.tcx } @@ -211,31 +224,8 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { self.body_id.to_def_id() } - fn get_type_parameter_bounds( - &self, - _: Span, - def_id: LocalDefId, - _: Ident, - ) -> ty::GenericPredicates<'tcx> { - let tcx = self.tcx; - let item_def_id = tcx.hir().ty_param_owner(def_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id.to_def_id()]; - // HACK(eddyb) should get the original `Span`. - let span = tcx.def_span(def_id); - ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter( - self.param_env.caller_bounds().iter().filter_map(|predicate| { - match predicate.kind().skip_binder() { - ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => { - Some((predicate, span)) - } - _ => None, - } - }), - ), - } + fn allow_infer(&self) -> bool { + true } fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option> { @@ -246,17 +236,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { Some(self.next_region_var(v)) } - fn allow_ty_infer(&self) -> bool { - true - } - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { match param { Some(param) => self.var_for_def(span, param).as_type().unwrap(), - None => self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), + None => self.next_ty_var(TypeVariableOrigin { param_def_id: None, span }), } } @@ -275,14 +258,38 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { }, ) => self.var_for_effect(param).as_const().unwrap(), Some(param) => self.var_for_def(span, param).as_const().unwrap(), - None => self.next_const_var( - ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, + None => self.next_const_var(ty, ConstVariableOrigin { span, param_def_id: None }), + } + } + + fn probe_ty_param_bounds( + &self, + _: Span, + def_id: LocalDefId, + _: Ident, + ) -> ty::GenericPredicates<'tcx> { + let tcx = self.tcx; + let item_def_id = tcx.hir().ty_param_owner(def_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; + // HACK(eddyb) should get the original `Span`. + let span = tcx.def_span(def_id); + ty::GenericPredicates { + parent: None, + predicates: tcx.arena.alloc_from_iter( + self.param_env.caller_bounds().iter().filter_map(|predicate| { + match predicate.kind().skip_binder() { + ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => { + Some((predicate, span)) + } + _ => None, + } + }), ), } } - fn projected_ty_from_poly_trait_ref( + fn lower_assoc_ty( &self, span: Span, item_def_id: DefId, @@ -295,7 +302,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { poly_trait_ref, ); - let item_args = self.astconv().create_args_for_associated_item( + let item_args = self.lowerer().lower_generic_args_of_assoc_item( span, item_def_id, item_segment, @@ -318,10 +325,6 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { } } - fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { - self.infcx.set_tainted_by_errors(e) - } - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) { // FIXME: normalization and escaping regions let ty = if !ty.has_escaping_bound_vars() { @@ -345,13 +348,17 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> { Some(&self.infcx) } + + fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + self.infcx.set_tainted_by_errors(e) + } } /// The `ty` representation of a user-provided type. Depending on the use-site /// we want to either use the unnormalized or the normalized form of this type. /// -/// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`, -/// and the API in this module, which expect `Ty` to be fully normalized. +/// This is a bridge between the interface of HIR ty lowering, which outputs a raw +/// `Ty`, and the API in this module, which expect `Ty` to be fully normalized. #[derive(Clone, Copy, Debug)] pub struct LoweredTy<'tcx> { /// The unnormalized type provided by the user. @@ -374,3 +381,64 @@ impl<'tcx> LoweredTy<'tcx> { LoweredTy { raw, normalized } } } + +fn parse_never_type_options_attr( + tcx: TyCtxt<'_>, +) -> (DivergingFallbackBehavior, DivergingBlockBehavior) { + use DivergingFallbackBehavior::*; + + // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute. + // Just don't write incorrect attributes <3 + + let mut fallback = None; + let mut block = None; + + let items = tcx + .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) + .map(|attr| attr.meta_item_list().unwrap()) + .unwrap_or_default(); + + for item in items { + if item.has_name(sym::fallback) && fallback.is_none() { + let mode = item.value_str().unwrap(); + match mode { + sym::unit => fallback = Some(FallbackToUnit), + sym::niko => fallback = Some(FallbackToNiko), + sym::never => fallback = Some(FallbackToNever), + sym::no => fallback = Some(NoFallback), + _ => { + tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)")); + } + }; + continue; + } + + if item.has_name(sym::diverging_block_default) && block.is_none() { + let default = item.value_str().unwrap(); + match default { + sym::unit => block = Some(DivergingBlockBehavior::Unit), + sym::never => block = Some(DivergingBlockBehavior::Never), + _ => { + tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{default}` (supported: `unit` and `never`)")); + } + }; + continue; + } + + tcx.dcx().span_err( + item.span(), + format!( + "unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)", + item.name_or_empty() + ), + ); + } + + let fallback = fallback.unwrap_or_else(|| { + if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit } + }); + + let block = block.unwrap_or_default(); + + (fallback, block) +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 3f6f4cccba796..133778a416cc2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -7,9 +7,9 @@ use crate::hir::is_range_literal; use crate::method::probe; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use crate::rustc_middle::ty::Article; -use crate::ty::TypeAndMut; 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}; @@ -21,8 +21,9 @@ use rustc_hir::{ CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; -use rustc_hir_analysis::astconv::AstConv; -use rustc_infer::traits::{self}; +use rustc_hir_analysis::collect::suggest_impl_trait; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_infer::traits; use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -70,7 +71,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, - blk_id: hir::HirId, + blk_id: HirId, ) -> bool { let expr = expr.peel_drop_temps(); let mut pointing_at_return_type = false; @@ -318,7 +319,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); expr_id = parent_id; } - Node::Local(local) => { + Node::LetStmt(local) => { if let Some(mut ty) = local.ty { while let Some(index) = tuple_indexes.pop() { match ty.kind { @@ -796,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)); @@ -809,23 +810,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { - if let Some(found) = found.make_suggestable(self.tcx, false) { + if let Some(found) = found.make_suggestable(self.tcx, false, None) { err.subdiagnostic( self.dcx(), 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 { @@ -877,7 +871,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only point to return type if the expected type is the return type, as if they // are not, the expectation must have been caused by something else. debug!("return type {:?}", hir_ty); - let ty = self.astconv().ast_ty_to_ty(hir_ty); + let ty = self.lowerer().lower_ty(hir_ty); debug!("return type {:?}", ty); debug!("expected type {:?}", expected); let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); @@ -889,7 +883,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx(), errors::ExpectedReturnTypeLabel::Other { span: hir_ty.span, expected }, ); - self.try_suggest_return_impl_trait(err, expected, ty, fn_id); + self.try_suggest_return_impl_trait(err, expected, found, fn_id); + self.note_caller_chooses_ty_for_ty_param(err, expected, found); return true; } } @@ -899,6 +894,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + fn note_caller_chooses_ty_for_ty_param( + &self, + diag: &mut Diag<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if let ty::Param(expected_ty_as_param) = expected.kind() { + diag.subdiagnostic( + self.dcx(), + errors::NoteCallerChoosesTyForTyParam { + ty_param_name: expected_ty_as_param.name, + found_ty: found, + }, + ); + } + } + /// check whether the return type is a generic type with a trait bound /// only suggest this if the generic param is not present in the arguments /// if this is true, hint them towards changing the return type to `impl Trait` @@ -912,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 @@ -926,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: @@ -957,8 +969,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bounded_ty, .. }) => { - // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) - let ty = self.astconv().ast_ty_to_ty(bounded_ty); + // FIXME: Maybe these calls to `lower_ty` can be removed (and the ones below) + let ty = self.lowerer().lower_ty(bounded_ty); Some((ty, bounds)) } _ => None, @@ -996,7 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let all_bounds_str = all_matching_bounds_strs.join(" + "); let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { - let ty = self.astconv().ast_ty_to_ty( param); + let ty = self.lowerer().lower_ty( param); matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) }); @@ -1019,8 +1031,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_decl: &hir::FnDecl<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, - id: hir::HirId, - fn_id: hir::HirId, + id: HirId, + fn_id: LocalDefId, ) { if !expected.is_unit() { return; @@ -1071,12 +1083,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let can_return = match fn_decl.output { hir::FnRetTy::Return(ty) => { - let ty = self.astconv().ast_ty_to_ty(ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); + let ty = self.lowerer().lower_ty(ty); + 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(), @@ -1097,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", @@ -1274,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())] @@ -1331,7 +1339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ++++++++++ // since the user probably just misunderstood how `let else` // and `&&` work together. - if let Some((_, hir::Node::Local(local))) = cond_parent + if let Some((_, hir::Node::LetStmt(local))) = cond_parent && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind && let hir::QPath::Resolved(None, path) = qpath @@ -1479,7 +1487,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Ty<'tcx>, ) -> bool { // Expected type needs to be a raw pointer. - let ty::RawPtr(ty::TypeAndMut { mutbl, .. }) = expected_ty.kind() else { + let ty::RawPtr(_, mutbl) = expected_ty.kind() else { return false; }; @@ -1592,12 +1600,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn is_loop(&self, id: hir::HirId) -> bool { + fn is_loop(&self, id: HirId) -> bool { let node = self.tcx.hir_node(id); matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) } - fn is_local_statement(&self, id: hir::HirId) -> bool { + fn is_local_statement(&self, id: HirId) -> bool { let node = self.tcx.hir_node(id); matches!(node, Node::Stmt(Stmt { kind: StmtKind::Let(..), .. })) } @@ -1731,7 +1739,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match self.tcx.parent_hir_node(*hir_id) { // foo.clone() - hir::Node::Local(hir::Local { init: Some(init), .. }) => { + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => { self.note_type_is_not_clone_inner_expr(init) } // When `expr` is more complex like a tuple @@ -1740,7 +1748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: hir::PatKind::Tuple(pats, ..), .. }) => { - let hir::Node::Local(hir::Local { init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) = self.tcx.parent_hir_node(*pat_hir_id) else { return expr; @@ -1774,7 +1782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } = call_expr_path && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) - && let hir::Node::Local(hir::Local { init: Some(init), .. }) = + && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) = self.tcx.parent_hir_node(*hir_id) && let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), @@ -1884,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; } @@ -2509,11 +2513,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return make_sugg(sp, expr.span.lo()); } } - ( - _, - &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }), - &ty::Ref(_, ty_a, mutbl_a), - ) => { + (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => { if let Some(steps) = self.deref_steps(ty_a, ty_b) // Only suggest valid if dereferencing needed. && steps > 0 @@ -3134,7 +3134,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { return; }; - let hir::Node::Local(hir::Local { ty: None, init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) = self.tcx.parent_hir_node(pat.hir_id) else { return; diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 4d37f725c9400..fe0a46924de46 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -1,8 +1,8 @@ use crate::FnCtxt; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::PatKind; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_hir::{HirId, PatKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_middle::ty::Ty; use rustc_middle::ty::UserType; use rustc_span::def_id::LocalDefId; @@ -29,11 +29,11 @@ impl<'a> DeclOrigin<'a> { } } -/// A declaration is an abstraction of [hir::Local] and [hir::Let]. +/// A declaration is an abstraction of [hir::LetStmt] and [hir::LetExpr]. /// /// It must have a hir_id, as this is how we connect gather_locals to the check functions. pub(super) struct Declaration<'a> { - pub hir_id: hir::HirId, + pub hir_id: HirId, pub pat: &'a hir::Pat<'a>, pub ty: Option<&'a hir::Ty<'a>>, pub span: Span, @@ -41,16 +41,16 @@ pub(super) struct Declaration<'a> { pub origin: DeclOrigin<'a>, } -impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> { - fn from(local: &'a hir::Local<'a>) -> Self { - let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local; +impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> { + fn from(local: &'a hir::LetStmt<'a>) -> Self { + let hir::LetStmt { hir_id, pat, ty, span, init, els, source: _ } = *local; Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } } } } -impl<'a> From<(&'a hir::Let<'a>, hir::HirId)> for Declaration<'a> { - fn from((let_expr, hir_id): (&'a hir::Let<'a>, hir::HirId)) -> Self { - let hir::Let { pat, ty, span, init, is_recovered: _ } = *let_expr; +impl<'a> From<(&'a hir::LetExpr<'a>, HirId)> for Declaration<'a> { + fn from((let_expr, hir_id): (&'a hir::LetExpr<'a>, HirId)) -> Self { + let hir::LetExpr { pat, ty, span, init, is_recovered: _ } = *let_expr; Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr } } } @@ -60,7 +60,7 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> { // parameters are special cases of patterns, but we want to handle them as // *distinct* cases. so track when we are hitting a pattern *within* an fn // parameter. - outermost_fn_param_pat: Option<(Span, hir::HirId)>, + outermost_fn_param_pat: Option<(Span, HirId)>, } impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { @@ -68,14 +68,11 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { Self { fcx, outermost_fn_param_pat: None } } - fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option>) -> Ty<'tcx> { + fn assign(&mut self, span: Span, nid: HirId, ty_opt: Option>) -> Ty<'tcx> { match ty_opt { None => { // Infer the variable's type. - let var_ty = self.fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); + let var_ty = self.fcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span }); self.fcx.locals.borrow_mut().insert(nid, var_ty); var_ty } @@ -93,10 +90,9 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { fn declare(&mut self, decl: Declaration<'tcx>) { let local_ty = match decl.ty { Some(ref ty) => { - let o_ty = self.fcx.to_ty(ty); + let o_ty = self.fcx.lower_ty(ty); - let c_ty = - self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw)); + let c_ty = self.fcx.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw)); debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty); self.fcx .typeck_results @@ -120,7 +116,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add explicitly-declared locals. - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.declare(local.into()); intravisit::walk_local(self, local) } diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs deleted file mode 100644 index b0950ed280084..0000000000000 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ /dev/null @@ -1,169 +0,0 @@ -use super::callee::DeferredCallResolution; - -use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::HirIdMap; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; -use rustc_middle::traits::DefiningAnchor; -use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::def_id::LocalDefIdMap; -use rustc_span::Span; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{self, PredicateObligation, TraitEngine, TraitEngineExt as _}; - -use std::cell::RefCell; -use std::ops::Deref; - -/// Closures defined within the function. For example: -/// ```ignore (illustrative) -/// fn foo() { -/// bar(move|| { ... }) -/// } -/// ``` -/// Here, the function `foo()` and the closure passed to -/// `bar()` will each have their own `FnCtxt`, but they will -/// share the inherited fields. -pub struct Inherited<'tcx> { - pub(super) infcx: InferCtxt<'tcx>, - - pub(super) typeck_results: RefCell>, - - pub(super) locals: RefCell>>, - - pub(super) fulfillment_cx: RefCell>>, - - /// Some additional `Sized` obligations badly affect type inference. - /// These obligations are added in a later stage of typeck. - /// Removing these may also cause additional complications, see #101066. - pub(super) deferred_sized_obligations: - RefCell, Span, traits::ObligationCauseCode<'tcx>)>>, - - /// When we process a call like `c()` where `c` is a closure type, - /// we may not have decided yet whether `c` is a `Fn`, `FnMut`, or - /// `FnOnce` closure. In that case, we defer full resolution of the - /// call until upvar inference can kick in and make the - /// decision. We keep these deferred resolutions grouped by the - /// def-id of the closure, so that once we decide, we can easily go - /// back and process them. - pub(super) deferred_call_resolutions: RefCell>>>, - - pub(super) deferred_cast_checks: RefCell>>, - - pub(super) deferred_transmute_checks: RefCell, Ty<'tcx>, hir::HirId)>>, - - pub(super) deferred_asm_checks: RefCell, hir::HirId)>>, - - pub(super) deferred_coroutine_interiors: RefCell)>>, - - /// Whenever we introduce an adjustment from `!` into a type variable, - /// we record that type variable here. This is later used to inform - /// fallback. See the `fallback` module for details. - pub(super) diverging_type_vars: RefCell>>, - - pub(super) infer_var_info: RefCell>, -} - -impl<'tcx> Deref for Inherited<'tcx> { - type Target = InferCtxt<'tcx>; - fn deref(&self) -> &Self::Target { - &self.infcx - } -} - -impl<'tcx> Inherited<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { - let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; - - let infcx = tcx - .infer_ctxt() - .ignoring_regions() - .with_opaque_type_inference(DefiningAnchor::bind(tcx, def_id)) - .build(); - let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); - - Inherited { - typeck_results, - fulfillment_cx: RefCell::new(>::new(&infcx)), - infcx, - locals: RefCell::new(Default::default()), - deferred_sized_obligations: RefCell::new(Vec::new()), - deferred_call_resolutions: RefCell::new(Default::default()), - deferred_cast_checks: RefCell::new(Vec::new()), - deferred_transmute_checks: RefCell::new(Vec::new()), - deferred_asm_checks: RefCell::new(Vec::new()), - deferred_coroutine_interiors: RefCell::new(Vec::new()), - diverging_type_vars: RefCell::new(Default::default()), - infer_var_info: RefCell::new(Default::default()), - } - } - - #[instrument(level = "debug", skip(self))] - pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { - if obligation.has_escaping_bound_vars() { - span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); - } - - self.update_infer_var_info(&obligation); - - self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); - } - - pub(super) fn register_predicates(&self, obligations: I) - where - I: IntoIterator>, - { - for obligation in obligations { - self.register_predicate(obligation); - } - } - - pub(super) fn register_infer_ok_obligations(&self, infer_ok: InferOk<'tcx, T>) -> T { - self.register_predicates(infer_ok.obligations); - infer_ok.value - } - - pub fn update_infer_var_info(&self, obligation: &PredicateObligation<'tcx>) { - let infer_var_info = &mut self.infer_var_info.borrow_mut(); - - // (*) binder skipped - if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(tpred)) = - obligation.predicate.kind().skip_binder() - && let Some(ty) = - self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t)) - && self.tcx.lang_items().sized_trait().is_some_and(|st| st != tpred.trait_ref.def_id) - { - let new_self_ty = self.tcx.types.unit; - - // Then construct a new obligation with Self = () added - // to the ParamEnv, and see if it holds. - let o = obligation.with( - self.tcx, - obligation.predicate.kind().rebind( - // (*) binder moved here - ty::PredicateKind::Clause(ty::ClauseKind::Trait( - tpred.with_self_ty(self.tcx, new_self_ty), - )), - ), - ); - // Don't report overflow errors. Otherwise equivalent to may_hold. - if let Ok(result) = self.probe(|_| self.evaluate_obligation(&o)) - && result.may_apply() - { - infer_var_info.entry(ty).or_default().self_in_trait = true; - } - } - - if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) = - obligation.predicate.kind().skip_binder() - { - // If the projection predicate (Foo::Bar == X) has X as a non-TyVid, - // we need to make it into one. - if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) { - debug!("infer_var_info: {:?}.output = true", vid); - infer_var_info.entry(vid).or_default().output = true; - } - } - } -} diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 9e3867e630d48..62711e40049a4 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -17,7 +17,7 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { let data_idx; let one = VariantIdx::new(1); - let zero = VariantIdx::new(0); + let zero = VariantIdx::ZERO; if def.variant(zero).fields.is_empty() { data_idx = one; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index d86b4912c89bd..8bc070bcd36cd 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -5,7 +5,6 @@ #![feature(try_blocks)] #![feature(never_type)] #![feature(box_patterns)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![feature(control_flow_enum)] #[macro_use] @@ -32,7 +31,6 @@ pub mod expr_use_visitor; mod fallback; mod fn_ctxt; mod gather_locals; -mod inherited; mod intrinsicck; mod mem_categorization; mod method; @@ -40,11 +38,13 @@ mod op; mod pat; mod place_op; mod rvalue_scopes; +mod typeck_root_ctxt; mod upvar; mod writeback; -pub use fn_ctxt::FnCtxt; -pub use inherited::Inherited; +pub use coercion::can_coerce; +use fn_ctxt::FnCtxt; +use typeck_root_ctxt::TypeckRootCtxt; use crate::check::check_fn; use crate::coercion::DynamicCoerceMany; @@ -57,16 +57,16 @@ 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::{HirIdMap, Node}; -use rustc_hir_analysis::astconv::AstConv; +use rustc_hir::{HirId, HirIdMap, Node}; use rustc_hir_analysis::check::check_abi; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; use rustc_middle::traits; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config; -use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::def_id::LocalDefId; use rustc_span::Span; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -84,57 +84,6 @@ macro_rules! type_error_struct { }) } -/// If this `DefId` is a "primary tables entry", returns -/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`. -/// -/// If this function returns `Some`, then `typeck_results(def_id)` will -/// succeed; if it returns `None`, then `typeck_results(def_id)` may or -/// may not succeed. In some cases where this function returns `None` -/// (notably closures), `typeck_results(def_id)` would wind up -/// redirecting to the owning function. -fn primary_body_of( - node: Node<'_>, -) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { - match node { - Node::Item(item) => match item.kind { - hir::ItemKind::Const(ty, _, body) | hir::ItemKind::Static(ty, _, body) => { - Some((body, Some(ty), None)) - } - hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)), - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - Some((body, None, Some(sig))) - } - _ => None, - }, - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)), - hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::AnonConst(constant) => Some((constant.body, None, None)), - _ => None, - } -} - -fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id); - if typeck_root_def_id != def_id { - return tcx.has_typeck_results(typeck_root_def_id); - } - - if let Some(def_id) = def_id.as_local() { - primary_body_of(tcx.hir_node_by_def_id(def_id)).is_some() - } else { - false - } -} - fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet { &tcx.typeck(def_id).used_trait_imports } @@ -186,22 +135,22 @@ fn typeck_with_fallback<'tcx>( let span = tcx.hir().span(id); // Figure out what primary body this item has. - let (body_id, body_ty, fn_sig) = primary_body_of(node).unwrap_or_else(|| { + let body_id = node.body_id().unwrap_or_else(|| { span_bug!(span, "can't type-check body of {:?}", def_id); }); let body = tcx.hir().body(body_id); let param_env = tcx.param_env(def_id); - let inh = Inherited::new(tcx, def_id); + let root_ctxt = TypeckRootCtxt::new(tcx, def_id); if let Some(inspector) = inspector { - inh.infcx.attach_obligation_inspector(inspector); + root_ctxt.infcx.attach_obligation_inspector(inspector); } - let mut fcx = FnCtxt::new(&inh, param_env, def_id); + let mut fcx = FnCtxt::new(&root_ctxt, param_env, def_id); - if let Some(hir::FnSig { header, decl, .. }) = fn_sig { + if let Some(hir::FnSig { header, decl, .. }) = node.fn_sig() { let fn_sig = if decl.output.get_infer_ret_ty().is_some() { - fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) + fcx.lowerer().lower_fn_ty(id, header.unsafety, header.abi, decl, None, None) } else { tcx.fn_sig(def_id).instantiate_identity() }; @@ -214,42 +163,7 @@ fn typeck_with_fallback<'tcx>( check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params); } else { - let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - })) - } else if let Node::AnonConst(_) = node { - match tcx.parent_hir_node(id) { - Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), .. }) - if anon_const.hir_id == id => - { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - })) - } - Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) - | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { - asm.operands.iter().find_map(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == id => { - // Inline assembly constants must be integers. - Some(fcx.next_int_var()) - } - hir::InlineAsmOperand::SymFn { anon_const } if anon_const.hir_id == id => { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - })) - } - _ => None, - }) - } - _ => None, - } - } else { - None - }; + let expected_type = infer_type_if_missing(&fcx, node); let expected_type = expected_type.unwrap_or_else(fallback); let expected_type = fcx.normalize(body.value.span, expected_type); @@ -319,6 +233,50 @@ fn typeck_with_fallback<'tcx>( typeck_results } +fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Option> { + let tcx = fcx.tcx; + let def_id = fcx.body_id; + let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = node.ty() { + if let Some(item) = tcx.opt_associated_item(def_id.into()) + && let ty::AssocKind::Const = item.kind + && let ty::ImplContainer = item.container + && let Some(trait_item) = item.trait_item_def_id + { + let args = + tcx.impl_trait_ref(item.container_id(tcx)).unwrap().instantiate_identity().args; + Some(tcx.type_of(trait_item).instantiate(tcx, args)) + } else { + Some(fcx.next_ty_var(TypeVariableOrigin { span, param_def_id: None })) + } + } else if let Node::AnonConst(_) = node { + let id = tcx.local_def_id_to_hir_id(def_id); + match tcx.parent_hir_node(id) { + Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), span, .. }) + if anon_const.hir_id == id => + { + Some(fcx.next_ty_var(TypeVariableOrigin { span, param_def_id: None })) + } + Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), span, .. }) + | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), span, .. }) => { + asm.operands.iter().find_map(|(op, _op_sp)| match op { + hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == id => { + // Inline assembly constants must be integers. + Some(fcx.next_int_var()) + } + hir::InlineAsmOperand::SymFn { anon_const } if anon_const.hir_id == id => { + Some(fcx.next_ty_var(TypeVariableOrigin { span, param_def_id: None })) + } + _ => None, + }) + } + _ => None, + } + } else { + None + }; + expected_type +} + /// When `check_fn` is invoked on a coroutine (i.e., a body that /// includes yield), it returns back some information about the yield /// points. @@ -366,13 +324,13 @@ pub struct EnclosingBreakables<'tcx> { } impl<'tcx> EnclosingBreakables<'tcx> { - fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> { + fn find_breakable(&mut self, target_id: HirId) -> &mut BreakableCtxt<'tcx> { self.opt_find_breakable(target_id).unwrap_or_else(|| { bug!("could not find enclosing breakable with id {}", target_id); }) } - fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> { + fn opt_find_breakable(&mut self, target_id: HirId) -> Option<&mut BreakableCtxt<'tcx>> { match self.by_id.get(&target_id) { Some(ix) => Some(&mut self.stack[*ix]), None => None, @@ -392,7 +350,7 @@ fn report_unexpected_variant_res( Res::Def(DefKind::Variant, _) => "struct variant", _ => res.descr(), }; - let path_str = rustc_hir_pretty::qpath_to_string(qpath); + let path_str = rustc_hir_pretty::qpath_to_string(&tcx, qpath); let err = tcx .dcx() .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) @@ -456,11 +414,5 @@ fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { pub fn provide(providers: &mut Providers) { method::provide(providers); - *providers = Providers { - typeck, - diagnostic_only_typeck, - has_typeck_results, - used_trait_imports, - ..*providers - }; + *providers = Providers { typeck, diagnostic_only_typeck, used_trait_imports, ..*providers }; } diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 9307cccf092c4..b44c2345933fb 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -58,24 +58,24 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::PatKind; +use rustc_hir::{HirId, PatKind}; use rustc_infer::infer::InferCtxt; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_trait_selection::infer::InferCtxtExt; pub(crate) trait HirNode { - fn hir_id(&self) -> hir::HirId; + fn hir_id(&self) -> HirId; } impl HirNode for hir::Expr<'_> { - fn hir_id(&self) -> hir::HirId { + fn hir_id(&self) -> HirId { self.hir_id } } impl HirNode for hir::Pat<'_> { - fn hir_id(&self) -> hir::HirId { + fn hir_id(&self) -> HirId { self.hir_id } } @@ -86,7 +86,7 @@ pub(crate) struct MemCategorizationContext<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_owner: LocalDefId, - upvars: Option<&'tcx FxIndexMap>, + upvars: Option<&'tcx FxIndexMap>, } pub(crate) type McResult = Result; @@ -127,11 +127,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.infcx.tainted_by_errors().is_some() } - fn resolve_type_vars_or_error( - &self, - id: hir::HirId, - ty: Option>, - ) -> McResult> { + fn resolve_type_vars_or_error(&self, id: HirId, ty: Option>) -> McResult> { match ty { Some(ty) => { let ty = self.resolve_vars_if_possible(ty); @@ -153,7 +149,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - pub(crate) fn node_ty(&self, hir_id: hir::HirId) -> McResult> { + pub(crate) fn node_ty(&self, hir_id: HirId) -> McResult> { self.resolve_type_vars_or_error(hir_id, self.typeck_results.node_type_opt(hir_id)) } @@ -206,7 +202,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { .get(pat.hir_id) .expect("missing binding mode"); - if let ty::BindByReference(_) = bm { + if matches!(bm.0, hir::ByRef::Yes(_)) { // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. @@ -268,11 +264,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { adjustment::Adjust::Deref(overloaded) => { // Equivalent to *expr or something similar. let base = if let Some(deref) = overloaded { - let ref_ty = Ty::new_ref( - self.tcx(), - deref.region, - ty::TypeAndMut { ty: target, mutbl: deref.mutbl }, - ); + let ref_ty = Ty::new_ref(self.tcx(), deref.region, target, deref.mutbl); self.cat_rvalue(expr.hir_id, ref_ty) } else { previous()? @@ -381,7 +373,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { #[instrument(level = "debug", skip(self, span), ret)] pub(crate) fn cat_res( &self, - hir_id: hir::HirId, + hir_id: HirId, span: Span, expr_ty: Ty<'tcx>, res: Res, @@ -420,7 +412,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// environment and upvar reference as appropriate. Only regionck cares /// about these dereferences, so we let it compute them as needed. #[instrument(level = "debug", skip(self), ret)] - fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult> { + fn cat_upvar(&self, hir_id: HirId, var_id: HirId) -> McResult> { let closure_expr_def_id = self.body_owner; let upvar_id = ty::UpvarId { @@ -433,7 +425,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self), ret)] - pub(crate) fn cat_rvalue(&self, hir_id: hir::HirId, expr_ty: Ty<'tcx>) -> PlaceWithHirId<'tcx> { + pub(crate) fn cat_rvalue(&self, hir_id: HirId, expr_ty: Ty<'tcx>) -> PlaceWithHirId<'tcx> { PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()) } @@ -479,7 +471,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let ty::Ref(region, _, mutbl) = *base_ty.kind() else { span_bug!(expr.span, "cat_overloaded_place: base is not a reference"); }; - let ref_ty = Ty::new_ref(self.tcx(), region, ty::TypeAndMut { ty: place_ty, mutbl }); + let ref_ty = Ty::new_ref(self.tcx(), region, place_ty, mutbl); let base = self.cat_rvalue(expr.hir_id, ref_ty); self.cat_deref(expr, base) @@ -527,7 +519,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { fn variant_index_for_adt( &self, qpath: &hir::QPath<'_>, - pat_hir_id: hir::HirId, + pat_hir_id: HirId, span: Span, ) -> McResult { let res = self.typeck_results.qpath_res(qpath, pat_hir_id); @@ -560,7 +552,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Here `pat_hir_id` is the HirId of the pattern itself. fn total_fields_in_adt_variant( &self, - pat_hir_id: hir::HirId, + pat_hir_id: HirId, variant_index: VariantIdx, span: Span, ) -> McResult { @@ -577,7 +569,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Returns the total number of fields in a tuple used within a Tuple pattern. /// Here `pat_hir_id` is the HirId of the pattern itself. - fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult { + fn total_fields_in_tuple(&self, pat_hir_id: HirId, span: Span) -> McResult { let ty = self.typeck_results.node_type(pat_hir_id); match ty.kind() { ty::Tuple(args) => Ok(args.len()), @@ -726,6 +718,16 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let subplace = self.cat_deref(pat, place_with_id)?; self.cat_pattern_(subplace, subpat, op)?; } + PatKind::Deref(subpat) => { + let mutable = self.typeck_results.pat_has_ref_mut_binding(subpat); + let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; + let re_erased = self.tcx().lifetimes.re_erased; + let ty = self.pat_ty_adjusted(subpat)?; + let ty = Ty::new_ref(self.tcx(), re_erased, ty, mutability); + // A deref pattern generates a temporary. + let place = self.cat_rvalue(pat.hir_id, ty); + self.cat_pattern_(place, subpat, op)?; + } PatKind::Slice(before, ref slice, after) => { let Some(element_ty) = place_with_id.place.ty().builtin_index() else { diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index a580c114f2614..d57015282de9e 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -4,10 +4,10 @@ use crate::{callee, FnCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; -use rustc_hir_analysis::astconv::generics::{ - check_generic_arg_count_for_call, create_args_for_parent_generic_args, +use rustc_hir_analysis::hir_ty_lowering::generics::{ + check_generic_arg_count_for_call, lower_generic_args, }; -use rustc_hir_analysis::astconv::{AstConv, CreateInstantiationsForGenericArgsCtxt, IsMethodCall}; +use rustc_hir_analysis::hir_ty_lowering::{GenericArgsLowerer, HirTyLowerer, IsMethodCall}; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -173,7 +173,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let Some((ty, n)) = autoderef.nth(pick.autoderefs) else { return Ty::new_error_with_message( self.tcx, - rustc_span::DUMMY_SP, + DUMMY_SP, format!("failed autoderef {}", pick.autoderefs), ); }; @@ -188,7 +188,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // Type we're wrapping in a reference, used later for unsizing let base_ty = target; - target = Ty::new_ref(self.tcx, region, ty::TypeAndMut { mutbl, ty: target }); + target = Ty::new_ref(self.tcx, region, target, mutbl); // Method call receivers are the primary use case // for two-phase borrows. @@ -208,11 +208,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { base_ty ) }; - target = Ty::new_ref( - self.tcx, - region, - ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty }, - ); + target = Ty::new_ref(self.tcx, region, unsized_ty, mutbl.into()); adjustments.push(Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), target, @@ -221,9 +217,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { target = match target.kind() { - &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + &ty::RawPtr(ty, mutbl) => { assert!(mutbl.is_mut()); - Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) + Ty::new_imm_ptr(self.tcx, ty) } other => panic!("Cannot adjust receiver type {other:?} to const ptr"), }; @@ -366,14 +362,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // combining parameters from the type and those from the method. assert_eq!(generics.parent_count, parent_args.len()); - struct MethodInstantiationsCtxt<'a, 'tcx> { + struct GenericArgsCtxt<'a, 'tcx> { cfcx: &'a ConfirmContext<'a, 'tcx>, pick: &'a probe::Pick<'tcx>, seg: &'a hir::PathSegment<'tcx>, } - impl<'a, 'tcx> CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> - for MethodInstantiationsCtxt<'a, 'tcx> - { + impl<'a, 'tcx> GenericArgsLowerer<'a, 'tcx> for GenericArgsCtxt<'a, 'tcx> { fn args_for_def_id( &mut self, def_id: DefId, @@ -393,13 +387,13 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.cfcx.fcx.astconv().ast_region_to_region(lt, Some(param)).into() + self.cfcx.fcx.lowerer().lower_lifetime(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.cfcx.to_ty(ty).raw.into() + self.cfcx.lower_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.cfcx.const_arg_to_const(&ct.value, param.def_id).into() + self.cfcx.lower_const_arg(&ct.value, param.def_id).into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.cfcx.ty_infer(Some(param), inf.span).into() @@ -432,14 +426,14 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } } - let args = create_args_for_parent_generic_args( + let args = lower_generic_args( self.tcx, pick.item.def_id, parent_args, false, None, &arg_count_correct, - &mut MethodInstantiationsCtxt { cfcx: self, pick, seg }, + &mut GenericArgsCtxt { cfcx: self, pick, seg }, ); // When the method is confirmed, the `args` includes @@ -614,7 +608,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let span = predicates .iter() .find_map(|(p, span)| if p == pred { Some(span) } else { None }) - .unwrap_or(rustc_span::DUMMY_SP); + .unwrap_or(DUMMY_SP); Some((trait_pred, span)) } _ => None, @@ -634,6 +628,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { Some(self.self_expr.span), self.call_expr.span, trait_def_id, + self.body_id.to_def_id(), ) { self.set_tainted_by_errors(e); } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 3b26a791f6577..39d54f1a25e0b 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -81,7 +81,7 @@ pub struct NoMatchData<'tcx> { // A pared down enum describing just the places from which a method // candidate can arise. Used for error reporting only. -#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum CandidateSource { Impl(DefId), Trait(DefId /* trait id */), @@ -200,11 +200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(span) = result.illegal_sized_bound { let mut needs_mut = false; if let ty::Ref(region, t_type, mutability) = self_ty.kind() { - let trait_type = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, - ); + let trait_type = Ty::new_ref(self.tcx, *region, *t_type, mutability.invert()); // We probe again to see if there might be a borrow mutability discrepancy. match self.lookup_probe( segment.ident, diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 0e43cb40485a5..22eef8e53dae0 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -7,8 +7,8 @@ use hir::HirId; use hir::ItemKind; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::{Adt, Array, Ref, Ty}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; +use rustc_middle::ty::{self, Ty}; use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; use rustc_span::symbol::kw::{Empty, Underscore}; use rustc_span::symbol::{sym, Ident}; @@ -44,7 +44,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter // before Rust 2021, which results in the same problem. // It is only a problem for arrays. - sym::into_iter if let Array(..) = self_ty.kind() => { + sym::into_iter if let ty::Array(..) = self_ty.kind() => { // In this case, it wasn't really a prelude addition that was the problem. // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021. rustc_lint::ARRAY_INTO_ITER @@ -64,7 +64,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pick.autoref_or_ptr_adjustment, Some(probe::AutorefOrPtrAdjustment::Autoref { .. }) ) - && matches!(self_ty.kind(), Ref(..)) + && matches!(self_ty.kind(), ty::Ref(..)) { return; } @@ -218,10 +218,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we know it does not, we don't need to warn. if method_name.name == sym::from_iter { if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) { - let any_type = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - }); + let any_type = + self.infcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span }); if !self .infcx .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env) @@ -280,7 +278,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the user has written the self type with generics already which we (naively) do by looking // for a "<" in `self_ty_name`. if !self_ty_name.contains('<') { - if let Adt(def, _) = self_ty.kind() { + if let ty::Adt(def, _) = self_ty.kind() { let generics = self.tcx.generics_of(def.did()); if !generics.params.is_empty() { let counts = generics.own_counts(); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index bdc796aca3a46..8a7ebd6478b13 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,12 +3,12 @@ use super::CandidateSource; use super::MethodError; use super::NoMatchData; -use crate::errors::MethodCallOnUnknownRawPointee; use crate::FnCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir::HirId; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; @@ -21,7 +21,7 @@ use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::AssocItem; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::ToPredicate; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArgs, GenericArgsRef}; use rustc_session::lint; use rustc_span::def_id::DefId; @@ -37,7 +37,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{ CandidateStep, MethodAutoderefStepsResult, }; use rustc_trait_selection::traits::query::CanonicalTyGoal; -use rustc_trait_selection::traits::NormalizeExt; +use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCause}; use std::cell::RefCell; use std::cmp::max; @@ -87,7 +87,7 @@ pub(crate) struct ProbeContext<'a, 'tcx> { Vec<(ty::Predicate<'tcx>, Option>, Option>)>, >, - scope_expr_id: hir::HirId, + scope_expr_id: HirId, } impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> { @@ -99,39 +99,6 @@ impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> { #[derive(Debug, Clone)] pub(crate) struct Candidate<'tcx> { - // Candidates are (I'm not quite sure, but they are mostly) basically - // some metadata on top of a `ty::AssocItem` (without args). - // - // However, method probing wants to be able to evaluate the predicates - // for a function with the args applied - for example, if a function - // has `where Self: Sized`, we don't want to consider it unless `Self` - // is actually `Sized`, and similarly, return-type suggestions want - // to consider the "actual" return type. - // - // The way this is handled is through `xform_self_ty`. It contains - // the receiver type of this candidate, but `xform_self_ty`, - // `xform_ret_ty` and `kind` (which contains the predicates) have the - // generic parameters of this candidate instantiated with the *same set* - // of inference variables, which acts as some weird sort of "query". - // - // When we check out a candidate, we require `xform_self_ty` to be - // a subtype of the passed-in self-type, and this equates the type - // variables in the rest of the fields. - // - // For example, if we have this candidate: - // ``` - // trait Foo { - // fn foo(&self) where Self: Sized; - // } - // ``` - // - // Then `xform_self_ty` will be `&'erased ?X` and `kind` will contain - // the predicate `?X: Sized`, so if we are evaluating `Foo` for a - // the receiver `&T`, we'll do the subtyping which will make `?X` - // get the right value, then when we evaluate the predicate we'll check - // if `T: Sized`. - xform_self_ty: Ty<'tcx>, - xform_ret_ty: Option>, pub(crate) item: ty::AssocItem, pub(crate) kind: CandidateKind<'tcx>, pub(crate) import_ids: SmallVec<[LocalDefId; 1]>, @@ -139,17 +106,10 @@ pub(crate) struct Candidate<'tcx> { #[derive(Debug, Clone)] pub(crate) enum CandidateKind<'tcx> { - InherentImplCandidate( - GenericArgsRef<'tcx>, - // Normalize obligations - Vec>, - ), - ObjectCandidate, - TraitCandidate(ty::TraitRef<'tcx>), - WhereClauseCandidate( - // Trait - ty::PolyTraitRef<'tcx>, - ), + InherentImplCandidate(DefId), + ObjectCandidate(ty::PolyTraitRef<'tcx>), + TraitCandidate(ty::PolyTraitRef<'tcx>), + WhereClauseCandidate(ty::PolyTraitRef<'tcx>), } #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -264,7 +224,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mode: Mode, return_type: Ty<'tcx>, self_ty: Ty<'tcx>, - scope_expr_id: hir::HirId, + scope_expr_id: HirId, candidate_filter: impl Fn(&ty::AssocItem) -> bool, ) -> Vec { let method_names = self @@ -308,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Option>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, - scope_expr_id: hir::HirId, + scope_expr_id: HirId, scope: ProbeScope, ) -> PickResult<'tcx> { self.probe_op( @@ -332,7 +292,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Option>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, - scope_expr_id: hir::HirId, + scope_expr_id: HirId, scope: ProbeScope, ) -> Vec> { self.probe_op( @@ -363,7 +323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Option>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, - scope_expr_id: hir::HirId, + scope_expr_id: HirId, scope: ProbeScope, op: OP, ) -> Result> @@ -386,10 +346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let infcx = &self.infcx; let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) = - infcx.instantiate_canonical_with_fresh_inference_vars( - span, - ¶m_env_and_self_ty, - ); + infcx.instantiate_canonical(span, ¶m_env_and_self_ty); debug!( "probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}", param_env_and_self_ty, self_ty @@ -433,21 +390,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if is_suggestion.0 { // Ambiguity was encountered during a suggestion. Just keep going. debug!("ProbeContext: encountered ambiguity in suggestion"); - } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types { + } else if bad_ty.reached_raw_pointer + && !self.tcx.features().arbitrary_self_types + && !self.tcx.sess.at_least_rust_2018() + { // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) - if self.tcx.sess.at_least_rust_2018() { - self.dcx().emit_err(MethodCallOnUnknownRawPointee { span }); - } else { - self.tcx.node_span_lint( - lint::builtin::TYVAR_BEHIND_RAW_POINTER, - scope_expr_id, - span, - "type annotations needed", - |_| {}, - ); - } + self.tcx.node_span_lint( + lint::builtin::TYVAR_BEHIND_RAW_POINTER, + scope_expr_id, + span, + "type annotations needed", + |_| {}, + ); } else { // Ended up encountering a type variable when doing autoderef, // but it may not be a type variable after processing obligations @@ -458,10 +414,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { - ty::Infer(ty::TyVar(_)) => self - .err_ctxt() - .emit_inference_failure_err(self.body_id, span, ty.into(), E0282, true) - .emit(), + ty::Infer(ty::TyVar(_)) => { + let raw_ptr_call = + bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types; + let mut err = self.err_ctxt().emit_inference_failure_err( + self.body_id, + span, + ty.into(), + E0282, + !raw_ptr_call, + ); + if raw_ptr_call { + err.span_label(span, "cannot call a method on a raw pointer with an unknown pointee type"); + } + err.emit() + } ty::Error(guar) => guar, _ => bug!("unexpected bad final type in method autoderef"), }; @@ -531,7 +498,7 @@ fn method_autoderef_steps<'tcx>( from_unsafe_deref: reached_raw_pointer, unsize: false, }; - if let ty::RawPtr(_) = ty.kind() { + if let ty::RawPtr(_, _) = ty.kind() { // all the subsequent steps will be from_unsafe_deref reached_raw_pointer = true; } @@ -583,7 +550,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return_type: Option>, orig_steps_var_values: &'a OriginalQueryValues<'tcx>, steps: &'tcx [CandidateStep<'tcx>], - scope_expr_id: hir::HirId, + scope_expr_id: HirId, ) -> ProbeContext<'a, 'tcx> { ProbeContext { fcx, @@ -661,13 +628,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // of the iterations in the autoderef loop, so there is no problem with it // being discoverable in another one of these iterations. // - // Using `instantiate_canonical_with_fresh_inference_vars` on our + // Using `instantiate_canonical` on our // `Canonical>>` and then *throwing away* the // `CanonicalVarValues` will exactly give us such a generalization - it // will still match the original object type, but it won't pollute our // type variables in any form, so just do that! let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) = - self.fcx.instantiate_canonical_with_fresh_inference_vars(self.span, self_ty); + self.fcx.instantiate_canonical(self.span, self_ty); self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type(p.def_id()); @@ -699,7 +666,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Never | ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty), @@ -736,42 +703,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.record_static_candidate(CandidateSource::Impl(impl_def_id)); continue; } - - let (impl_ty, impl_args) = self.impl_ty_and_args(impl_def_id); - let impl_ty = impl_ty.instantiate(self.tcx, impl_args); - - debug!("impl_ty: {:?}", impl_ty); - - // Determine the receiver type that the method itself expects. - let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(item, impl_ty, impl_args); - debug!("xform_self_ty: {:?}, xform_ret_ty: {:?}", xform_self_ty, xform_ret_ty); - - // We can't use `FnCtxt::normalize` as it will pollute the - // fcx's fulfillment context after this probe is over. - // - // Note: we only normalize `xform_self_ty` here since the normalization - // of the return type can lead to inference results that prohibit - // valid candidates from being found, see issue #85671 - // - // FIXME Postponing the normalization of the return type likely only hides a deeper bug, - // which might be caused by the `param_env` itself. The clauses of the `param_env` - // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized, - // see issue #89650 - let cause = traits::ObligationCause::misc(self.span, self.body_id); - let InferOk { value: xform_self_ty, obligations } = - self.fcx.at(&cause, self.param_env).normalize(xform_self_ty); - - debug!( - "assemble_inherent_impl_probe after normalization: xform_self_ty = {:?}/{:?}", - xform_self_ty, xform_ret_ty - ); - self.push_candidate( Candidate { - xform_self_ty, - xform_ret_ty, item, - kind: InherentImplCandidate(impl_args, obligations), + kind: InherentImplCandidate(impl_def_id), import_ids: smallvec![], }, true, @@ -803,26 +738,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // a `&self` method will wind up with an argument type like `&dyn Trait`. let trait_ref = principal.with_self_ty(self.tcx, self_ty); self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| { - if new_trait_ref.has_non_region_bound_vars() { - this.dcx().span_delayed_bug( - this.span, - "tried to select method from HRTB with non-lifetime bound vars", - ); - return; - } - - let new_trait_ref = this.instantiate_bound_regions_with_erased(new_trait_ref); - - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.args); this.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - kind: ObjectCandidate, - import_ids: smallvec![], - }, + Candidate { item, kind: ObjectCandidate(new_trait_ref), import_ids: smallvec![] }, true, ); }); @@ -853,19 +770,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }); self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { - let trait_ref = this.instantiate_binder_with_fresh_vars( - this.span, - infer::BoundRegionConversionTime::FnCall, - poly_trait_ref, - ); - - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(item, trait_ref.self_ty(), trait_ref.args); - this.push_candidate( Candidate { - xform_self_ty, - xform_ret_ty, item, kind: WhereClauseCandidate(poly_trait_ref), import_ids: smallvec![], @@ -922,27 +828,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - fn matches_return_type( - &self, - method: ty::AssocItem, - self_ty: Option>, - expected: Ty<'tcx>, - ) -> bool { + fn matches_return_type(&self, method: ty::AssocItem, expected: Ty<'tcx>) -> bool { match method.kind { ty::AssocKind::Fn => self.probe(|_| { let args = self.fresh_args_for_item(self.span, method.def_id); let fty = self.tcx.fn_sig(method.def_id).instantiate(self.tcx, args); let fty = self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, fty); - - if let Some(self_ty) = self_ty { - if self - .at(&ObligationCause::dummy(), self.param_env) - .sup(DefineOpaqueTypes::No, fty.inputs()[0], self_ty) - .is_err() - { - return false; - } - } self.can_sub(self.param_env, fty.output(), expected) }), _ => false, @@ -971,21 +862,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { bound_trait_ref.def_id(), )); } else { - let new_trait_ref = self.instantiate_binder_with_fresh_vars( - self.span, - infer::BoundRegionConversionTime::FnCall, - bound_trait_ref, - ); - - let (xform_self_ty, xform_ret_ty) = - self.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.args); self.push_candidate( Candidate { - xform_self_ty, - xform_ret_ty, item, import_ids: import_ids.clone(), - kind: TraitCandidate(new_trait_ref), + kind: TraitCandidate(bound_trait_ref), }, false, ); @@ -1004,16 +885,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.record_static_candidate(CandidateSource::Trait(trait_def_id)); continue; } - - let (xform_self_ty, xform_ret_ty) = - self.xform_self_ty(item, trait_ref.self_ty(), trait_args); self.push_candidate( Candidate { - xform_self_ty, - xform_ret_ty, item, import_ids: import_ids.clone(), - kind: TraitCandidate(trait_ref), + kind: TraitCandidate(ty::Binder::dummy(trait_ref)), }, false, ); @@ -1033,7 +909,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { if let Some(return_ty) = self.return_type { - self.matches_return_type(candidate.item, None, return_ty) + self.matches_return_type(candidate.item, return_ty) } else { true } @@ -1216,7 +1092,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // In general, during probing we erase regions. let region = tcx.lifetimes.re_erased; - let autoref_ty = Ty::new_ref(tcx, region, ty::TypeAndMut { ty: self_ty, mutbl }); + let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl); self.pick_method(autoref_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1241,12 +1117,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return None; } - let &ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) = self_ty.kind() else { + let &ty::RawPtr(ty, hir::Mutability::Mut) = self_ty.kind() else { return None; }; - let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }; - let const_ptr_ty = Ty::new_ptr(self.tcx, const_self_ty); + let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty); self.pick_method(const_ptr_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1376,7 +1251,7 @@ impl<'tcx> Pick<'tcx> { &self, tcx: TyCtxt<'tcx>, span: Span, - scope_expr_id: hir::HirId, + scope_expr_id: HirId, ) { if self.unstable_candidates.is_empty() { return; @@ -1420,15 +1295,13 @@ impl<'tcx> Pick<'tcx> { } _ => {} } - if tcx.sess.is_nightly_build() { - for (candidate, feature) in &self.unstable_candidates { - lint.help(format!( - "add `#![feature({})]` to the crate attributes to enable `{}`", - feature, - tcx.def_path_str(candidate.item.def_id), - )); - } - } + tcx.disabled_nightly_features( + lint, + Some(scope_expr_id), + self.unstable_candidates.iter().map(|(candidate, feature)| { + (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) + }), + ); }, ); } @@ -1446,16 +1319,20 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource { match candidate.kind { - InherentImplCandidate(..) => { + InherentImplCandidate(_) => { CandidateSource::Impl(candidate.item.container_id(self.tcx)) } - ObjectCandidate | WhereClauseCandidate(_) => { + ObjectCandidate(_) | WhereClauseCandidate(_) => { CandidateSource::Trait(candidate.item.container_id(self.tcx)) } TraitCandidate(trait_ref) => self.probe(|_| { + let trait_ref = + self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, trait_ref); + let (xform_self_ty, _) = + self.xform_self_ty(candidate.item, trait_ref.self_ty(), trait_ref.args); let _ = self.at(&ObligationCause::dummy(), self.param_env).sup( DefineOpaqueTypes::No, - candidate.xform_self_ty, + xform_self_ty, self_ty, ); match self.select_trait_candidate(trait_ref) { @@ -1482,54 +1359,46 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ) -> ProbeResult { debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe); - self.probe(|_| { - // First check that the self type can be related. - let sub_obligations = match self.at(&ObligationCause::dummy(), self.param_env).sup( - DefineOpaqueTypes::No, - probe.xform_self_ty, - self_ty, - ) { - Ok(InferOk { obligations, value: () }) => obligations, - Err(err) => { - debug!("--> cannot relate self-types {:?}", err); - return ProbeResult::NoMatch; - } - }; + self.probe(|snapshot| { + let outer_universe = self.universe(); let mut result = ProbeResult::Match; - let mut xform_ret_ty = probe.xform_ret_ty; - debug!(?xform_ret_ty); - - let cause = traits::ObligationCause::misc(self.span, self.body_id); + let cause = &self.misc(self.span); + let ocx = ObligationCtxt::new(self); - let mut parent_pred = None; + let mut trait_predicate = None; + let (mut xform_self_ty, mut xform_ret_ty); - // If so, impls may carry other conditions (e.g., where - // clauses) that must be considered. Make sure that those - // match as well (or at least may match, sometimes we - // don't have enough information to fully evaluate). match probe.kind { - InherentImplCandidate(args, ref ref_obligations) => { - // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, - // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` - // for why this is necessary - let InferOk { - value: normalized_xform_ret_ty, - obligations: normalization_obligations, - } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty); - xform_ret_ty = normalized_xform_ret_ty; - debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); - + InherentImplCandidate(impl_def_id) => { + let impl_args = self.fresh_args_for_item(self.span, impl_def_id); + let impl_ty = self.tcx.type_of(impl_def_id).instantiate(self.tcx, impl_args); + (xform_self_ty, xform_ret_ty) = + self.xform_self_ty(probe.item, impl_ty, impl_args); + xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); + // FIXME: Make this `ocx.sup` once we define opaques more eagerly. + match self.at(cause, self.param_env).sup( + DefineOpaqueTypes::No, + xform_self_ty, + self_ty, + ) { + Ok(infer_ok) => { + ocx.register_infer_ok_obligations(infer_ok); + } + Err(err) => { + debug!("--> cannot relate self-types {:?}", err); + return ProbeResult::NoMatch; + } + } + // FIXME: Weirdly, we normalize the ret ty in this candidate, but no other candidates. + xform_ret_ty = ocx.normalize(cause, self.param_env, xform_ret_ty); // Check whether the impl imposes obligations we have to worry about. let impl_def_id = probe.item.container_id(self.tcx); - let impl_bounds = self.tcx.predicates_of(impl_def_id); - let impl_bounds = impl_bounds.instantiate(self.tcx, args); - - let InferOk { value: impl_bounds, obligations: norm_obligations } = - self.fcx.at(&cause, self.param_env).normalize(impl_bounds); - + let impl_bounds = + self.tcx.predicates_of(impl_def_id).instantiate(self.tcx, impl_args); + let impl_bounds = ocx.normalize(cause, self.param_env, impl_bounds); // Convert the bounds into obligations. - let impl_obligations = traits::predicates_for_generics( + ocx.register_obligations(traits::predicates_for_generics( |idx, span| { let code = if span.is_dummy() { traits::ExprItemObligation(impl_def_id, self.scope_expr_id, idx) @@ -1545,106 +1414,92 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }, self.param_env, impl_bounds, - ); - - let candidate_obligations = impl_obligations - .chain(norm_obligations) - .chain(ref_obligations.iter().cloned()) - .chain(normalization_obligations); - - // Evaluate those obligations to see if they might possibly hold. - for o in candidate_obligations { - let o = self.resolve_vars_if_possible(o); - if !self.predicate_may_hold(&o) { - result = ProbeResult::NoMatch; - let parent_o = o.clone(); - let implied_obligations = traits::elaborate(self.tcx, vec![o]); - for o in implied_obligations { - let parent = if o == parent_o { - None - } else { - if o.predicate.to_opt_poly_trait_pred().map(|p| p.def_id()) - == self.tcx.lang_items().sized_trait() - { - // We don't care to talk about implicit `Sized` bounds. - continue; - } - Some(parent_o.predicate) - }; - if !self.predicate_may_hold(&o) { - possibly_unsatisfied_predicates.push(( - o.predicate, - parent, - Some(o.cause), - )); - } - } - } - } + )); } - - ObjectCandidate | WhereClauseCandidate(..) => { - // These have no additional conditions to check. - } - - TraitCandidate(trait_ref) => { + TraitCandidate(poly_trait_ref) => { + // Some trait methods are excluded for arrays before 2021. + // (`array.into_iter()` wants a slice iterator for compatibility.) if let Some(method_name) = self.method_name { - // Some trait methods are excluded for arrays before 2021. - // (`array.into_iter()` wants a slice iterator for compatibility.) if self_ty.is_array() && !method_name.span.at_least_rust_2021() { - let trait_def = self.tcx.trait_def(trait_ref.def_id); + let trait_def = self.tcx.trait_def(poly_trait_ref.def_id()); if trait_def.skip_array_during_method_dispatch { return ProbeResult::NoMatch; } } } - let predicate = ty::Binder::dummy(trait_ref).to_predicate(self.tcx); - parent_pred = Some(predicate); - let obligation = - traits::Obligation::new(self.tcx, cause.clone(), self.param_env, predicate); - if !self.predicate_may_hold(&obligation) { + + let trait_ref = self.instantiate_binder_with_fresh_vars( + self.span, + infer::FnCall, + poly_trait_ref, + ); + let trait_ref = ocx.normalize(cause, self.param_env, trait_ref); + (xform_self_ty, xform_ret_ty) = + self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args); + xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); + // FIXME: Make this `ocx.sup` once we define opaques more eagerly. + match self.at(cause, self.param_env).sup( + DefineOpaqueTypes::No, + xform_self_ty, + self_ty, + ) { + Ok(infer_ok) => { + ocx.register_infer_ok_obligations(infer_ok); + } + Err(err) => { + debug!("--> cannot relate self-types {:?}", err); + return ProbeResult::NoMatch; + } + } + let obligation = traits::Obligation::new( + self.tcx, + cause.clone(), + self.param_env, + ty::Binder::dummy(trait_ref), + ); + + // FIXME(-Znext-solver): We only need this hack to deal with fatal + // overflow in the old solver. + if self.infcx.next_trait_solver() || self.infcx.predicate_may_hold(&obligation) + { + ocx.register_obligation(obligation); + } else { result = ProbeResult::NoMatch; - if self.probe(|_| { - match self.select_trait_candidate(trait_ref) { - Err(_) => return true, - Ok(Some(impl_source)) - if !impl_source.borrow_nested_obligations().is_empty() => - { - for obligation in impl_source.borrow_nested_obligations() { - // Determine exactly which obligation wasn't met, so - // that we can give more context in the error. - if !self.predicate_may_hold(obligation) { - let nested_predicate = - self.resolve_vars_if_possible(obligation.predicate); - let predicate = - self.resolve_vars_if_possible(predicate); - let p = if predicate == nested_predicate { - // Avoid "`MyStruct: Foo` which is required by - // `MyStruct: Foo`" in E0599. - None - } else { - Some(predicate) - }; - possibly_unsatisfied_predicates.push(( - nested_predicate, - p, - Some(obligation.cause.clone()), - )); - } - } - } - _ => { - // Some nested subobligation of this predicate - // failed. - let predicate = self.resolve_vars_if_possible(predicate); - possibly_unsatisfied_predicates.push((predicate, None, None)); + if let Ok(Some(candidate)) = self.select_trait_candidate(trait_ref) { + for nested_obligation in candidate.nested_obligations() { + if !self.infcx.predicate_may_hold(&nested_obligation) { + possibly_unsatisfied_predicates.push(( + self.resolve_vars_if_possible(nested_obligation.predicate), + Some(self.resolve_vars_if_possible(obligation.predicate)), + Some(nested_obligation.cause), + )); } } - false - }) { - // This candidate's primary obligation doesn't even - // select - don't bother registering anything in - // `potentially_unsatisfied_predicates`. + } + } + + trait_predicate = Some(ty::Binder::dummy(trait_ref).to_predicate(self.tcx)); + } + ObjectCandidate(poly_trait_ref) | WhereClauseCandidate(poly_trait_ref) => { + let trait_ref = self.instantiate_binder_with_fresh_vars( + self.span, + infer::FnCall, + poly_trait_ref, + ); + (xform_self_ty, xform_ret_ty) = + self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args); + xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); + // FIXME: Make this `ocx.sup` once we define opaques more eagerly. + match self.at(cause, self.param_env).sup( + DefineOpaqueTypes::No, + xform_self_ty, + self_ty, + ) { + Ok(infer_ok) => { + ocx.register_infer_ok_obligations(infer_ok); + } + Err(err) => { + debug!("--> cannot relate self-types {:?}", err); return ProbeResult::NoMatch; } } @@ -1652,11 +1507,22 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } // Evaluate those obligations to see if they might possibly hold. - for o in sub_obligations { - let o = self.resolve_vars_if_possible(o); - if !self.predicate_may_hold(&o) { - result = ProbeResult::NoMatch; - possibly_unsatisfied_predicates.push((o.predicate, parent_pred, Some(o.cause))); + for error in ocx.select_where_possible() { + result = ProbeResult::NoMatch; + let nested_predicate = self.resolve_vars_if_possible(error.obligation.predicate); + if let Some(trait_predicate) = trait_predicate + && nested_predicate == self.resolve_vars_if_possible(trait_predicate) + { + // Don't report possibly unsatisfied predicates if the root + // trait obligation from a `TraitCandidate` is unsatisfied. + // That just means the candidate doesn't hold. + } else { + possibly_unsatisfied_predicates.push(( + nested_predicate, + Some(self.resolve_vars_if_possible(error.root_obligation.predicate)) + .filter(|root_predicate| *root_predicate != nested_predicate), + Some(error.obligation.cause), + )); } } @@ -1668,36 +1534,37 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // We don't normalize the other candidates for perf/backwards-compat reasons... // but `self.return_type` is only set on the diagnostic-path, so we // should be okay doing it here. - if !matches!(probe.kind, InherentImplCandidate(..)) { - let InferOk { - value: normalized_xform_ret_ty, - obligations: normalization_obligations, - } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty); - xform_ret_ty = normalized_xform_ret_ty; - debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); - // Evaluate those obligations to see if they might possibly hold. - for o in normalization_obligations { - let o = self.resolve_vars_if_possible(o); - if !self.predicate_may_hold(&o) { - result = ProbeResult::NoMatch; - possibly_unsatisfied_predicates.push(( - o.predicate, - None, - Some(o.cause), - )); - } - } + if !matches!(probe.kind, InherentImplCandidate(_)) { + xform_ret_ty = ocx.normalize(&cause, self.param_env, xform_ret_ty); } debug!("comparing return_ty {:?} with xform ret ty {:?}", return_ty, xform_ret_ty); - if let ProbeResult::Match = result - && self - .at(&ObligationCause::dummy(), self.param_env) - .sup(DefineOpaqueTypes::No, return_ty, xform_ret_ty) - .is_err() - { - result = ProbeResult::BadReturnType; + match ocx.sup(cause, self.param_env, return_ty, xform_ret_ty) { + Ok(()) => {} + Err(_) => { + result = ProbeResult::BadReturnType; + } } + + // Evaluate those obligations to see if they might possibly hold. + for error in ocx.select_where_possible() { + result = ProbeResult::NoMatch; + possibly_unsatisfied_predicates.push(( + error.obligation.predicate, + Some(error.root_obligation.predicate) + .filter(|predicate| *predicate != error.obligation.predicate), + Some(error.root_obligation.cause), + )); + } + } + + // Previously, method probe used `evaluate_predicate` to determine if a predicate + // was impossible to satisfy. This did a leak check, so we must also do a leak + // check here to prevent backwards-incompatible ambiguity being introduced. See + // `tests/ui/methods/leak-check-disquality.rs` for a simple example of when this + // may happen. + if let Err(_) = self.leak_check(outer_universe, Some(snapshot)) { + result = ProbeResult::NoMatch; } result @@ -1890,40 +1757,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn_sig.instantiate(self.tcx, args) }; - self.instantiate_bound_regions_with_erased(xform_fn_sig) - } - - /// Gets the type of an impl and generate generic parameters with inference vars. - fn impl_ty_and_args( - &self, - impl_def_id: DefId, - ) -> (ty::EarlyBinder>, GenericArgsRef<'tcx>) { - (self.tcx.type_of(impl_def_id), self.fresh_args_for_item(self.span, impl_def_id)) - } - - /// Replaces late-bound-regions bound by `value` with `'static` using - /// `ty::instantiate_bound_regions_with_erased`. - /// - /// This is only a reasonable thing to do during the *probe* phase, not the *confirm* phase, of - /// method matching. It is reasonable during the probe phase because we don't consider region - /// relationships at all. Therefore, we can just replace all the region variables with 'static - /// rather than creating fresh region variables. This is nice for two reasons: - /// - /// 1. Because the numbers of the region variables would otherwise be fairly unique to this - /// particular method call, it winds up creating fewer types overall, which helps for memory - /// usage. (Admittedly, this is a rather small effect, though measurable.) - /// - /// 2. It makes it easier to deal with higher-ranked trait bounds, because we can replace any - /// late-bound regions with 'static. Otherwise, if we were going to replace late-bound - /// regions with actual region variables as is proper, we'd have to ensure that the same - /// region got replaced with the same variable, which requires a bit more coordination - /// and/or tracking the instantiations and - /// so forth. - fn instantiate_bound_regions_with_erased(&self, value: ty::Binder<'tcx, T>) -> T - where - T: TypeFoldable>, - { - self.tcx.instantiate_bound_regions_with_erased(value) + self.tcx.instantiate_bound_regions_with_erased(xform_fn_sig) } /// Determine if the given associated item type is relevant in the current context. @@ -1935,7 +1769,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - /// Determine if the associated item withe the given DefId matches + /// Determine if the associated item with the given DefId matches /// the desired name via a doc alias. fn matches_by_doc_alias(&self, def_id: DefId) -> bool { let Some(name) = self.method_name else { @@ -2047,10 +1881,10 @@ impl<'tcx> Candidate<'tcx> { Pick { item: self.item, kind: match self.kind { - InherentImplCandidate(..) => InherentImplPick, - ObjectCandidate => ObjectPick, + InherentImplCandidate(_) => InherentImplPick, + ObjectCandidate(_) => ObjectPick, TraitCandidate(_) => TraitPick, - WhereClauseCandidate(ref trait_ref) => { + WhereClauseCandidate(trait_ref) => { // Only trait derived from where-clauses should // appear here, so they should not contain any // inference variables or other artifacts. This @@ -2061,7 +1895,7 @@ impl<'tcx> Candidate<'tcx> { && !trait_ref.skip_binder().args.has_placeholders() ); - WhereClausePick(*trait_ref) + WhereClausePick(trait_ref) } }, import_ids: self.import_ids.clone(), diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c5bbcc56f86a5..078009515e428 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -22,12 +22,8 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::PatKind::Binding; use rustc_hir::PathSegment; use rustc_hir::{ExprKind, Node, QPath}; -use rustc_infer::infer::{ - self, - type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, - RegionVariableOrigin, -}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_infer::infer::{self, type_variable::TypeVariableOrigin, RegionVariableOrigin}; +use rustc_middle::infer::unify_key::ConstVariableOrigin; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; @@ -49,7 +45,6 @@ use std::borrow::Cow; use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; use rustc_hir::intravisit::Visitor; -use std::cmp::{self, Ordering}; use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -83,13 +78,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_ref = ty::TraitRef::new( tcx, fn_once, - [ - ty, - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - }), - ], + [ty, self.next_ty_var(TypeVariableOrigin { param_def_id: None, span })], ); let poly_trait_ref = ty::Binder::dummy(trait_ref); let obligation = Obligation::misc( @@ -136,11 +125,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(arg_ty) = args[0].as_type() else { return false; }; - let ty::Param(param) = arg_ty.kind() else { + let ty::Param(param) = *arg_ty.kind() else { return false; }; // Is `generic_param` the same as the arg for this trait predicate? - generic_param.index == generics.type_param(¶m, tcx).index + generic_param.index == generics.type_param(param, tcx).index } else { false } @@ -167,10 +156,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - match ty.peel_refs().kind() { + match *ty.peel_refs().kind() { ty::Param(param) => { let generics = self.tcx.generics_of(self.body_id); - let generic_param = generics.type_param(¶m, self.tcx); + let generic_param = generics.type_param(param, self.tcx); for unsatisfied in unsatisfied_predicates.iter() { // The parameter implements `IntoIterator` // but it has called a method that requires it to implement `Iterator` @@ -304,11 +293,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { if needs_mut { - let trait_type = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, - ); + let trait_type = + Ty::new_ref(self.tcx, *region, *t_type, mutability.invert()); let msg = format!("you need `{trait_type}` instead of `{rcvr_ty}`"); let mut kind = &self_expr.kind; while let hir::ExprKind::AddrOf(_, _, expr) @@ -533,16 +519,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() { @@ -778,7 +792,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let projection_ty = pred.skip_binder().projection_ty; let args_with_infer_self = tcx.mk_args_from_iter( - iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into()) + iter::once(Ty::new_var(tcx, ty::TyVid::ZERO).into()) .chain(projection_ty.args.iter().skip(1)), ); @@ -875,7 +889,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { Some(pred.def_id()) == self.tcx.lang_items().sized_trait() - && pred.polarity == ty::ImplPolarity::Positive + && pred.polarity == ty::PredicatePolarity::Positive } _ => false, } @@ -1189,7 +1203,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect::>(); if !inherent_impls_candidate.is_empty() { - inherent_impls_candidate.sort(); + inherent_impls_candidate.sort_by_key(|id| self.tcx.def_path_str(id)); inherent_impls_candidate.dedup(); // number of types to show at most @@ -1247,7 +1261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|expr| { self.node_ty_opt(expr.hir_id).unwrap_or_else(|| { self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, + param_def_id: None, span: expr.span, }) }) @@ -1570,7 +1584,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sources: &mut Vec, sugg_span: Option, ) { - sources.sort(); + sources.sort_by_key(|source| match source { + CandidateSource::Trait(id) => (0, self.tcx.def_path_str(id)), + CandidateSource::Impl(id) => (1, self.tcx.def_path_str(id)), + }); sources.dedup(); // Dynamic limit to avoid hiding just one candidate, which is silly. let limit = if sources.len() == 5 { 5 } else { 4 }; @@ -1827,23 +1844,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { has_unsuggestable_args = true; match arg.unpack() { GenericArgKind::Lifetime(_) => self - .next_region_var(RegionVariableOrigin::MiscVariable( - rustc_span::DUMMY_SP, - )) + .next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) .into(), GenericArgKind::Type(_) => self .next_ty_var(TypeVariableOrigin { - span: rustc_span::DUMMY_SP, - kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + param_def_id: None, }) .into(), GenericArgKind::Const(arg) => self .next_const_var( arg.ty(), - ConstVariableOrigin { - span: rustc_span::DUMMY_SP, - kind: ConstVariableOriginKind::MiscVariable, - }, + ConstVariableOrigin { span: DUMMY_SP, param_def_id: None }, ) .into(), } @@ -2166,7 +2178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (filename, parent_node) { ( FileName::Real(_), - Node::Local(hir::Local { + Node::LetStmt(hir::LetStmt { source: hir::LocalSource::Normal, ty, .. @@ -2221,7 +2233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'v> Visitor<'v> for LetVisitor { type Result = ControlFlow>>; fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { - if let hir::StmtKind::Let(&hir::Local { pat, init, .. }) = ex.kind + if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind && let Binding(_, _, ident, ..) = pat.kind && ident.name == self.ident_name { @@ -2552,7 +2564,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }) .collect(); - preds.sort_by_key(|pred| (pred.def_id(), pred.self_ty())); + preds.sort_by_key(|pred| pred.trait_ref.to_string()); let def_ids = preds .iter() .filter_map(|pred| match pred.self_ty().kind() { @@ -2666,7 +2678,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { traits.push(trait_pred.def_id()); } } - traits.sort(); + traits.sort_by_key(|id| self.tcx.def_path_str(id)); traits.dedup(); let len = traits.len(); @@ -2741,7 +2753,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let SelfSource::QPath(ty) = self_source else { return; }; - for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { + for (deref_ty, _) in self.autoderef(DUMMY_SP, rcvr_ty).skip(1) { if let Ok(pick) = self.probe_for_name( Mode::Path, item_name, @@ -2889,7 +2901,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> bool { if !valid_out_of_scope_traits.is_empty() { let mut candidates = valid_out_of_scope_traits; - candidates.sort(); + candidates.sort_by_key(|id| self.tcx.def_path_str(id)); candidates.dedup(); // `TryFrom` and `FromIterator` have no methods @@ -3215,13 +3227,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if !candidates.is_empty() { - // Sort from most relevant to least relevant. - candidates.sort_by_key(|&info| cmp::Reverse(info)); + // Sort local crate results before others + candidates + .sort_by_key(|&info| (!info.def_id.is_local(), self.tcx.def_path_str(info.def_id))); candidates.dedup(); - let param_type = match rcvr_ty.kind() { + let param_type = match *rcvr_ty.kind() { ty::Param(param) => Some(param), - ty::Ref(_, ty, _) => match ty.kind() { + ty::Ref(_, ty, _) => match *ty.kind() { ty::Param(param) => Some(param), _ => None, }, @@ -3264,8 +3277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Colon, Nothing, } - let ast_generics = hir.get_generics(id.owner.def_id).unwrap(); - let trait_def_ids: DefIdSet = ast_generics + let hir_generics = hir.get_generics(id.owner.def_id).unwrap(); + let trait_def_ids: DefIdSet = hir_generics .bounds_for_param(def_id) .flat_map(|bp| bp.bounds.iter()) .filter_map(|bound| bound.trait_ref()?.trait_def_id()) @@ -3277,15 +3290,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "restrict type parameter `{}` with", param.name.ident(), )); - let bounds_span = ast_generics.bounds_span_for_suggestions(def_id); - if rcvr_ty.is_ref() && param.is_impl_trait() && bounds_span.is_some() { + let bounds_span = hir_generics.bounds_span_for_suggestions(def_id); + if rcvr_ty.is_ref() + && param.is_impl_trait() + && let Some((bounds_span, _)) = bounds_span + { err.multipart_suggestions( msg, candidates.iter().map(|t| { vec![ (param.span.shrink_to_lo(), "(".to_string()), ( - bounds_span.unwrap(), + bounds_span, format!(" + {})", self.tcx.def_path_str(t.def_id)), ), ] @@ -3295,32 +3311,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - let (sp, introducer) = if let Some(span) = bounds_span { - (span, Introducer::Plus) - } else if let Some(colon_span) = param.colon_span { - (colon_span.shrink_to_hi(), Introducer::Nothing) - } else if param.is_impl_trait() { - (param.span.shrink_to_hi(), Introducer::Plus) - } else { - (param.span.shrink_to_hi(), Introducer::Colon) - }; + let (sp, introducer, open_paren_sp) = + if let Some((span, open_paren_sp)) = bounds_span { + (span, Introducer::Plus, open_paren_sp) + } else if let Some(colon_span) = param.colon_span { + (colon_span.shrink_to_hi(), Introducer::Nothing, None) + } else if param.is_impl_trait() { + (param.span.shrink_to_hi(), Introducer::Plus, None) + } else { + (param.span.shrink_to_hi(), Introducer::Colon, None) + }; - err.span_suggestions( - sp, + let all_suggs = candidates.iter().map(|cand| { + let suggestion = format!( + "{} {}", + match introducer { + Introducer::Plus => " +", + Introducer::Colon => ":", + Introducer::Nothing => "", + }, + self.tcx.def_path_str(cand.def_id) + ); + + let mut suggs = vec![]; + + if let Some(open_paren_sp) = open_paren_sp { + suggs.push((open_paren_sp, "(".to_string())); + suggs.push((sp, format!("){suggestion}"))); + } else { + suggs.push((sp, suggestion)); + } + + suggs + }); + + err.multipart_suggestions( msg, - candidates.iter().map(|t| { - format!( - "{} {}", - match introducer { - Introducer::Plus => " +", - Introducer::Colon => ":", - Introducer::Nothing => "", - }, - self.tcx.def_path_str(t.def_id) - ) - }), + all_suggs, Applicability::MaybeIncorrect, ); + return; } Node::Item(hir::Item { @@ -3367,7 +3397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "inherent impls can't be candidates, only trait impls can be", ) }) - .filter(|header| header.polarity == ty::ImplPolarity::Negative) + .filter(|header| header.polarity != ty::ImplPolarity::Positive) .any(|header| { let imp = header.trait_ref.instantiate_identity(); let imp_simp = @@ -3416,7 +3446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { param_type.map_or_else( || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. - ToString::to_string, + |p| p.to_string(), ) }, }, @@ -3564,33 +3594,11 @@ pub enum SelfSource<'a> { MethodCall(&'a hir::Expr<'a> /* rcvr */), } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq)] pub struct TraitInfo { pub def_id: DefId, } -impl PartialEq for TraitInfo { - fn eq(&self, other: &TraitInfo) -> bool { - self.cmp(other) == Ordering::Equal - } -} -impl Eq for TraitInfo {} -impl PartialOrd for TraitInfo { - fn partial_cmp(&self, other: &TraitInfo) -> Option { - Some(self.cmp(other)) - } -} -impl Ord for TraitInfo { - fn cmp(&self, other: &TraitInfo) -> Ordering { - // Local crates are more important than remote ones (local: - // `cnum == 0`), and otherwise we throw in the defid for totality. - - let lhs = (other.def_id.krate, other.def_id); - let rhs = (self.def_id.krate, self.def_id); - lhs.cmp(&rhs) - } -} - /// Retrieves all traits in this crate and any dependent crates, /// and wraps them into `TraitInfo` for custom sorting. pub fn all_traits(tcx: TyCtxt<'_>) -> Vec { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 79f574aa7fdc4..a4f840d849dd2 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -7,7 +7,7 @@ use rustc_ast as ast; use rustc_data_structures::packed::Pu128; use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag}; use rustc_hir as hir; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, @@ -39,7 +39,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op); - Ty::new_unit(self.tcx) + self.tcx.types.unit } else { return_ty }; @@ -219,10 +219,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. let lhs_ty = self.check_expr(lhs_expr); - let fresh_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: lhs_expr.span, - }); + let fresh_var = self + .next_ty_var(TypeVariableOrigin { param_def_id: None, span: lhs_expr.span }); self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No) } IsAssign::Yes => { @@ -241,10 +239,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // using this variable as the expected type, which sometimes lets // us do better coercions than we would be able to do otherwise, // particularly for things like `String + &String`. - let rhs_ty_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: rhs_expr.span, - }); + let rhs_ty_var = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: rhs_expr.span }); let result = self.lookup_op_method( (lhs_expr, lhs_ty), @@ -382,11 +378,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(); } @@ -508,11 +510,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggest_deref_binop(&mut err, *lhs_deref_ty); } else { let lhs_inv_mutbl = mutbl.invert(); - let lhs_inv_mutbl_ty = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: *lhs_deref_ty, mutbl: lhs_inv_mutbl }, - ); + let lhs_inv_mutbl_ty = + Ty::new_ref(self.tcx, *region, *lhs_deref_ty, lhs_inv_mutbl); suggest_different_borrow( &mut err, @@ -524,11 +523,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() { let rhs_inv_mutbl = mutbl.invert(); - let rhs_inv_mutbl_ty = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: *rhs_deref_ty, mutbl: rhs_inv_mutbl }, - ); + let rhs_inv_mutbl_ty = + Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl); suggest_different_borrow( &mut err, @@ -601,8 +597,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(output_def_id) = output_def_id && let Some(trait_def_id) = trait_def_id && self.tcx.parent(output_def_id) == trait_def_id - && let Some(output_ty) = - output_ty.make_suggestable(self.tcx, false) + && let Some(output_ty) = output_ty + .make_suggestable(self.tcx, false, None) { Some(("Output", output_ty)) } else { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index bb963ad7a3972..382dba5f95fb9 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -5,21 +5,20 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, }; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{HirId, Pat, PatKind}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Pat, PatKind}; use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; +use rustc_lint as lint; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::Span; -use rustc_span::{BytePos, DUMMY_SP}; +use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use ty::VariantDef; @@ -80,9 +79,13 @@ struct TopInfo<'tcx> { #[derive(Copy, Clone)] struct PatInfo<'tcx, 'a> { - binding_mode: BindingMode, + binding_mode: ByRef, + max_ref_mutbl: Mutability, top_info: TopInfo<'tcx>, decl_origin: Option>, + + /// The depth of current pattern + current_depth: u32, } impl<'tcx> FnCtxt<'_, 'tcx> { @@ -122,14 +125,19 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } } -const INITIAL_BM: BindingMode = BindingMode::BindByValue(hir::Mutability::Not); - /// Mode for adjusting the expected type and binding mode. enum AdjustMode { /// Peel off all immediate reference types. Peel, /// Reset binding mode to the initial mode. + /// Used for destructuring assignment, where we don't want any match ergonomics. Reset, + /// Produced by ref patterns. + /// Reset the binding mode to the initial mode, + /// and if the old biding mode was by-reference + /// with mutability matching the pattern, + /// mark the pattern as having consumed this reference. + ResetAndConsumeRef(Mutability), /// Pass on the input binding mode and expected type. Pass, } @@ -152,7 +160,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl_origin: Option>, ) { let info = TopInfo { expected, origin_expr, span }; - let pat_info = PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin }; + let pat_info = PatInfo { + binding_mode: ByRef::No, + max_ref_mutbl: Mutability::Mut, + top_info: info, + decl_origin, + current_depth: 0, + }; self.check_pat(pat, expected, pat_info); } @@ -163,7 +177,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Conversely, inside this module, `check_pat_top` should never be used. #[instrument(level = "debug", skip(self, pat_info))] fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { - let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; + let PatInfo { binding_mode: def_bm, max_ref_mutbl, top_info: ti, current_depth, .. } = + pat_info; + let path_res = match &pat.kind { PatKind::Path(qpath) => Some( self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None), @@ -171,9 +187,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); - let pat_info = - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin: pat_info.decl_origin }; + let (expected, def_bm, max_ref_mutbl, ref_pattern_already_consumed) = + self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode, max_ref_mutbl); + let pat_info = PatInfo { + binding_mode: def_bm, + max_ref_mutbl, + top_info: ti, + decl_origin: pat_info.decl_origin, + current_depth: current_depth + 1, + }; let ty = match pat.kind { PatKind::Wild | PatKind::Err(_) => expected, @@ -203,7 +225,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info) } PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), - PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), + PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info), + PatKind::Ref(inner, mutbl) => self.check_pat_ref( + pat, + inner, + mutbl, + expected, + pat_info, + ref_pattern_already_consumed, + ), PatKind::Slice(before, slice, after) => { self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) } @@ -256,17 +286,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Compute the new expected type and default binding mode from the old ones /// as well as the pattern form we are currently checking. + /// + /// Last entry is only relevant for ref patterns (`&` and `&mut`); + /// if `true`, then the ref pattern consumed a match ergonomics inserted reference + /// and so does no need to match against a reference in the scrutinee type. fn calc_default_binding_mode( &self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - def_bm: BindingMode, + def_br: ByRef, adjust_mode: AdjustMode, - ) -> (Ty<'tcx>, BindingMode) { + max_ref_mutbl: Mutability, + ) -> (Ty<'tcx>, ByRef, Mutability, bool) { + if let ByRef::Yes(mutbl) = def_br { + debug_assert!(mutbl <= max_ref_mutbl); + } match adjust_mode { - AdjustMode::Pass => (expected, def_bm), - AdjustMode::Reset => (expected, INITIAL_BM), - AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm), + AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false), + AdjustMode::Reset => (expected, ByRef::No, Mutability::Mut, false), + AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => { + let mutbls_match = def_br == ByRef::Yes(ref_pat_mutbl); + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + if mutbls_match { + debug!("consuming inherited reference"); + (expected, ByRef::No, cmp::min(max_ref_mutbl, ref_pat_mutbl), true) + } else { + let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut { + self.peel_off_references( + pat, + expected, + def_br, + Mutability::Not, + max_ref_mutbl, + ) + } else { + (expected, def_br.cap_ref_mutability(Mutability::Not), Mutability::Not) + }; + (new_ty, new_bm, max_ref_mutbl, false) + } + } else { + (expected, ByRef::No, max_ref_mutbl, mutbls_match) + } + } + AdjustMode::Peel => { + let peeled = + self.peel_off_references(pat, expected, def_br, Mutability::Mut, max_ref_mutbl); + (peeled.0, peeled.1, peeled.2, false) + } } } @@ -286,6 +352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::TupleStruct(..) | PatKind::Tuple(..) | PatKind::Box(_) + | PatKind::Deref(_) | PatKind::Range(..) | PatKind::Slice(..) => AdjustMode::Peel, // A never pattern behaves somewhat like a literal or unit variant. @@ -320,7 +387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ``` // // See issue #46688. - PatKind::Ref(..) => AdjustMode::Reset, + PatKind::Ref(_, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl), // A `_` pattern works with any expected type, so there's no need to do anything. PatKind::Wild // A malformed pattern doesn't have an expected type, so let's just accept any type. @@ -344,8 +411,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - mut def_bm: BindingMode, - ) -> (Ty<'tcx>, BindingMode) { + mut def_br: ByRef, + max_peelable_mutability: Mutability, + mut max_ref_mutability: Mutability, + ) -> (Ty<'tcx>, ByRef, Mutability) { let mut expected = self.try_structurally_resolve_type(pat.span, expected); // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches @@ -356,7 +425,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // See the examples in `ui/match-defbm*.rs`. let mut pat_adjustments = vec![]; - while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { + while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() + && inner_mutability <= max_peelable_mutability + { debug!("inspecting {:?}", expected); debug!("current discriminant is Ref, inserting implicit deref"); @@ -364,28 +435,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_adjustments.push(expected); expected = self.try_structurally_resolve_type(pat.span, inner_ty); - def_bm = ty::BindByReference(match def_bm { + def_br = ByRef::Yes(match def_br { // If default binding mode is by value, make it `ref` or `ref mut` // (depending on whether we observe `&` or `&mut`). - ty::BindByValue(_) | + ByRef::No | // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). - ty::BindByReference(hir::Mutability::Mut) => inner_mutability, + ByRef::Yes(Mutability::Mut) => inner_mutability, // Once a `ref`, always a `ref`. // This is because a `& &mut` cannot mutate the underlying value. - ty::BindByReference(m @ hir::Mutability::Not) => m, + ByRef::Yes(Mutability::Not) => Mutability::Not, }); } + if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 { + def_br = def_br.cap_ref_mutability(max_ref_mutability); + if def_br == ByRef::Yes(Mutability::Not) { + max_ref_mutability = Mutability::Not; + } + } + if !pat_adjustments.is_empty() { - debug!("default binding mode is now {:?}", def_bm); - self.inh - .typeck_results + debug!("default binding mode is now {:?}", def_br); + self.typeck_results .borrow_mut() .pat_adjustments_mut() .insert(pat.hir_id, pat_adjustments); } - (expected, def_bm) + (expected, def_br, max_ref_mutability) } fn check_pat_lit( @@ -590,27 +667,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_ident( &self, pat: &'tcx Pat<'tcx>, - ba: hir::BindingAnnotation, + ba: BindingMode, var_id: HirId, sub: Option<&'tcx Pat<'tcx>>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; + let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info; // Determine the binding mode... let bm = match ba { - hir::BindingAnnotation::NONE => def_bm, - _ => BindingMode::convert(ba), + BindingMode(ByRef::No, Mutability::Mut) + if !(pat.span.at_least_rust_2024() + && self.tcx.features().mut_preserve_binding_mode_2024) + && matches!(def_br, ByRef::Yes(_)) => + { + // `mut x` resets the binding mode in edition <= 2021. + self.tcx.emit_node_span_lint( + lint::builtin::DEREFERENCING_MUT_BINDING, + pat.hir_id, + pat.span, + errors::DereferencingMutBinding { span: pat.span }, + ); + BindingMode(ByRef::No, Mutability::Mut) + } + BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), + BindingMode(ByRef::Yes(_), _) => ba, }; // ...and store it in a side table: - self.inh.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); + self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); let local_ty = self.local_ty(pat.span, pat.hir_id); - let eq_ty = match bm { - ty::BindByReference(mutbl) => { + let eq_ty = match bm.0 { + ByRef::Yes(mutbl) => { // If the binding is like `ref x | ref mut x`, // then `x` is assigned a value of type `&M T` where M is the // mutability and T is the expected type. @@ -621,10 +712,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.new_ref_ty(pat.span, mutbl, expected) } // Otherwise, the type of x is the expected type `T`. - ty::BindByValue(_) => { - // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). - expected - } + ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). }; self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); @@ -646,7 +734,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// bindings have the same type by comparing them all against the type of that first pat. fn check_binding_alt_eq_ty( &self, - ba: hir::BindingAnnotation, + ba: BindingMode, span: Span, var_id: HirId, ty: Ty<'tcx>, @@ -686,10 +774,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, expected: Ty<'tcx>, actual: Ty<'tcx>, - ba: hir::BindingAnnotation, + ba: BindingMode, ) { match (expected.kind(), actual.kind(), ba) { - (ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE) + (ty::Ref(_, inner_ty, _), _, BindingMode::NONE) if self.can_eq(self.param_env, *inner_ty, actual) => { err.span_suggestion_verbose( @@ -699,7 +787,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - (_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF) + (_, ty::Ref(_, inner_ty, _), BindingMode::REF) if self.can_eq(self.param_env, expected, *inner_ty) => { err.span_suggestion_verbose( @@ -713,7 +801,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // Precondition: pat is a Ref(_) pattern + /// Precondition: pat is a `Ref(_)` pattern fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) { let tcx = self.tcx; if let PatKind::Ref(inner, mutbl) = pat.kind @@ -734,7 +822,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ident_kind = match binding_parent { hir::Node::Param(_) => "parameter", - hir::Node::Local(_) => "variable", + hir::Node::LetStmt(_) => "variable", hir::Node::Arm(_) => "binding", // Provide diagnostics only if the parent pattern is struct-like, @@ -751,6 +839,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Binding(..) | PatKind::Path(..) | PatKind::Box(..) + | PatKind::Deref(_) | PatKind::Ref(..) | PatKind::Lit(..) | PatKind::Range(..) @@ -790,7 +879,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let PatKind::Ref(the_ref, _) = i.kind && let PatKind::Binding(mt, _, ident, _) = the_ref.kind { - let hir::BindingAnnotation(_, mtblty) = mt; + let BindingMode(_, mtblty) = mt; err.span_suggestion_verbose( i.span, format!("consider removing `&{mutability}` from the pattern"), @@ -834,8 +923,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) && let ty::Dynamic(..) = mt.ty.kind() { - // This is "x = SomeTrait" being reduced from - // "let &x = &SomeTrait" or "let box x = Box", an error. + // This is "x = dyn SomeTrait" being reduced from + // "let &x = &dyn SomeTrait" or "let box x = Box", an error. let type_str = self.ty_to_string(expected); let mut err = struct_span_code_err!( self.dcx(), @@ -908,8 +997,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, E0533, expected); return Ty::new_error(tcx, e); } - Res::SelfCtor(..) - | Res::Def( + Res::SelfCtor(def_id) => { + if let ty::Adt(adt_def, _) = *tcx.type_of(def_id).skip_binder().kind() + && adt_def.is_struct() + && let Some((CtorKind::Const, _)) = adt_def.non_enum_variant().ctor + { + // Ok, we allow unit struct ctors in patterns only. + } else { + let e = report_unexpected_variant_res( + tcx, + res, + qpath, + pat.span, + E0533, + "unit struct", + ); + return Ty::new_error(tcx, e); + } + } + Res::Def( DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst @@ -998,7 +1104,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => { let (type_def_id, item_def_id) = match pat_ty.kind() { - Adt(def, _) => match res { + ty::Adt(def, _) => match res { Res::Def(DefKind::Const, def_id) => (Some(def.did()), Some(def_id)), _ => (None, None), }, @@ -1046,15 +1152,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { - let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info; let tcx = self.tcx; let on_error = |e| { for pat in subpats { - self.check_pat( - pat, - Ty::new_error(tcx, e), - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ); + self.check_pat(pat, Ty::new_error(tcx, e), pat_info); } }; let report_unexpected_res = |res: Res| { @@ -1099,7 +1200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); // Type-check the tuple struct pattern against the expected type. - let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); + let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, pat_info.top_info); let had_err = if let Some(err) = diag { err.emit(); true @@ -1117,11 +1218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field = &variant.fields[FieldIdx::from_usize(i)]; let field_ty = self.field_ty(subpat.span, field, args); - self.check_pat( - subpat, - field_ty, - PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, - ); + self.check_pat(subpat, field_ty, pat_info); self.tcx.check_stability( variant.fields[FieldIdx::from_usize(i)].did, @@ -1131,16 +1228,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } else { - // Pattern has wrong number of fields. - let e = - self.e0023(pat.span, res, qpath, subpats, &variant.fields.raw, expected, had_err); + let e = self.emit_err_pat_wrong_number_of_fields( + pat.span, + res, + qpath, + subpats, + &variant.fields.raw, + expected, + had_err, + ); on_error(e); return Ty::new_error(tcx, e); } pat_ty } - fn e0023( + fn emit_err_pat_wrong_number_of_fields( &self, pat_span: Span, res: Res, @@ -1317,13 +1420,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let max_len = cmp::max(expected_len, elements.len()); - let element_tys_iter = (0..max_len).map(|_| { - self.next_ty_var( - // FIXME: `MiscVariable` for now -- obtaining the span and name information - // from all tuple elements isn't trivial. - TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }, - ) - }); + let element_tys_iter = + (0..max_len).map(|_| self.next_ty_var(TypeVariableOrigin { param_def_id: None, span })); let element_tys = tcx.mk_type_list_from_iter(element_tys_iter); let pat_ty = Ty::new_tup(tcx, element_tys); if let Some(err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info) { @@ -1513,7 +1611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand); if has_shorthand_field_name { - let path = rustc_hir_pretty::qpath_to_string(qpath); + let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath); let mut err = struct_span_code_err!( self.dcx(), pat.span, @@ -1695,7 +1793,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - let path = rustc_hir_pretty::qpath_to_string(qpath); + let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath); let mut err = struct_span_code_err!( self.dcx(), pat.span, @@ -1745,7 +1843,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { f } } - Err(_) => rustc_hir_pretty::pat_to_string(field.pat), + Err(_) => rustc_hir_pretty::pat_to_string(&self.tcx, field.pat), } }) .collect::>() @@ -1949,10 +2047,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(()) => { // Here, `demand::subtype` is good enough, but I don't // think any errors can be introduced by using `demand::eqtype`. - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); + let inner_ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: inner.span }); let box_ty = Ty::new_box(tcx, inner_ty); self.demand_eqtype_pat(span, expected, box_ty, pat_info.top_info); (box_ty, inner_ty) @@ -1966,66 +2062,125 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { box_ty } + fn check_pat_deref( + &self, + span: Span, + inner: &'tcx Pat<'tcx>, + expected: Ty<'tcx>, + pat_info: PatInfo<'tcx, '_>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + // Register a `DerefPure` bound, which is required by all `deref!()` pats. + self.register_bound( + expected, + tcx.require_lang_item(hir::LangItem::DerefPure, Some(span)), + self.misc(span), + ); + // ::Target + let ty = Ty::new_projection( + tcx, + tcx.require_lang_item(hir::LangItem::DerefTarget, Some(span)), + [expected], + ); + let ty = self.normalize(span, ty); + let ty = self.try_structurally_resolve_type(span, ty); + self.check_pat(inner, ty, pat_info); + + // Check if the pattern has any `ref mut` bindings, which would require + // `DerefMut` to be emitted in MIR building instead of just `Deref`. + // We do this *after* checking the inner pattern, since we want to make + // sure to apply any match-ergonomics adjustments. + if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) { + self.register_bound( + expected, + tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)), + self.misc(span), + ); + } + + expected + } + // Precondition: Pat is Ref(inner) fn check_pat_ref( &self, pat: &'tcx Pat<'tcx>, inner: &'tcx Pat<'tcx>, - mutbl: hir::Mutability, + mutbl: Mutability, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>, + consumed_inherited_ref: bool, ) -> Ty<'tcx> { - let tcx = self.tcx; - let expected = self.shallow_resolve(expected); - let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { - Ok(()) => { - // `demand::subtype` would be good enough, but using `eqtype` turns - // out to be equally general. See (note_1) for details. - - // Take region, inner-type from expected type if we can, - // to avoid creating needless variables. This also helps with - // the bad interactions of the given hack detailed in (note_1). - debug!("check_pat_ref: expected={:?}", expected); - match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), - _ => { - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); - debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); - let err = self.demand_eqtype_pat_diag( - pat.span, - expected, - ref_ty, - pat_info.top_info, - ); + if consumed_inherited_ref + && pat.span.at_least_rust_2024() + && self.tcx.features().ref_pat_eat_one_layer_2024 + { + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + expected + } else { + let tcx = self.tcx; + let expected = self.shallow_resolve(expected); + let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) { + Ok(()) => { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. + + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match *expected.kind() { + ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), + _ => { + if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere { + // We already matched against a match-ergonmics inserted reference, + // so we don't need to match against a reference from the original type. + // Save this infor for use in lowering later + self.typeck_results + .borrow_mut() + .skipped_ref_pats_mut() + .insert(pat.hir_id); + (expected, expected) + } else { + let inner_ty = self.next_ty_var(TypeVariableOrigin { + param_def_id: None, + span: inner.span, + }); + let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + let err = self.demand_eqtype_pat_diag( + pat.span, + expected, + ref_ty, + pat_info.top_info, + ); - // Look for a case like `fn foo(&foo: u32)` and suggest - // `fn foo(foo: &u32)` - if let Some(mut err) = err { - self.borrow_pat_suggestion(&mut err, pat); - err.emit(); + // Look for a case like `fn foo(&foo: u32)` and suggest + // `fn foo(foo: &u32)` + if let Some(mut err) = err { + self.borrow_pat_suggestion(&mut err, pat); + err.emit(); + } + (ref_ty, inner_ty) + } } - (ref_ty, inner_ty) } } - } - Err(guar) => { - let err = Ty::new_error(tcx, guar); - (err, err) - } - }; - self.check_pat(inner, inner_ty, pat_info); - ref_ty + Err(guar) => { + let err = Ty::new_error(tcx, guar); + (err, err) + } + }; + self.check_pat(inner, inner_ty, pat_info); + ref_ty + } } /// Create a reference type with a fresh region variable. - fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { + fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { let region = self.next_region_var(infer::PatternRegion(span)); - let mt = ty::TypeAndMut { ty, mutbl }; - Ty::new_ref(self.tcx, region, mt) + Ty::new_ref(self.tcx, region, ty, mutbl) } fn try_resolve_slice_ty_to_array_ty( @@ -2040,8 +2195,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let len = before.len(); - let ty_var_origin = - TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; + let ty_var_origin = TypeVariableOrigin { param_def_id: None, span }; let inner_ty = self.next_ty_var(ty_var_origin); Some(Ty::new_array(tcx, inner_ty, len.try_into().unwrap())) @@ -2134,7 +2288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // The expected type must be an array or slice, but was neither, so error. _ => { let guar = expected.error_reported().err().unwrap_or_else(|| { - self.error_expected_array_or_slice(span, expected, pat_info.top_info) + self.error_expected_array_or_slice(span, expected, pat_info) }); let err = Ty::new_error(self.tcx, guar); (err, Some(err), err) @@ -2169,7 +2323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { len: ty::Const<'tcx>, min_len: u64, ) -> (Option>, Ty<'tcx>) { - let len = match len.eval(self.tcx, self.param_env, None) { + let len = match len.eval(self.tcx, self.param_env, span) { Ok(val) => val .try_to_scalar() .and_then(|scalar| scalar.try_to_int().ok()) @@ -2273,8 +2427,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, expected_ty: Ty<'tcx>, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> ErrorGuaranteed { + let PatInfo { top_info: ti, current_depth, .. } = pat_info; + let mut err = struct_span_code_err!( self.dcx(), span, @@ -2292,9 +2448,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let Some(_) = ti.origin_expr && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let ty = self.resolve_vars_if_possible(ti.expected); - let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(ty); - match is_slice_or_array_or_vector.1.kind() { + let resolved_ty = self.resolve_vars_if_possible(ti.expected); + let (is_slice_or_array_or_vector, resolved_ty) = + self.is_slice_or_array_or_vector(resolved_ty); + match resolved_ty.kind() { ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Option, adt_def.did()) || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) => @@ -2309,7 +2466,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => (), } - if is_slice_or_array_or_vector.0 { + + let is_top_level = current_depth <= 1; + if is_slice_or_array_or_vector && is_top_level { err.span_suggestion( span, "consider slicing here", diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 6c5715b323c9e..bce43b3be3434 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -4,7 +4,7 @@ use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir_analysis::autoderef::Autoderef; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::InferOk; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCoercion}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; @@ -147,10 +147,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If some lookup succeeds, write callee into table and extract index/element // type from the method signature. // If some lookup succeeded, install method in table - let input_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AutoDeref, - span: base_expr.span, - }); + let input_ty = + self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: base_expr.span }); let method = self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index); @@ -162,11 +160,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)), - target: Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty }, - ), + target: Ty::new_imm_ref(self.tcx, *region, adjusted_ty), }); } else { span_bug!(expr.span, "input to index is not a ref?"); @@ -400,11 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { allow_two_phase_borrow: AllowTwoPhase::No, }; adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)); - adjustment.target = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: source, mutbl: mutbl.into() }, - ); + adjustment.target = Ty::new_ref(self.tcx, *region, source, mutbl.into()); } source = adjustment.target; } diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs new file mode 100644 index 0000000000000..31ce271a5fc48 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -0,0 +1,166 @@ +use super::callee::DeferredCallResolution; + +use rustc_data_structures::unord::{UnordMap, UnordSet}; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{HirId, HirIdMap}; +use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::def_id::LocalDefIdMap; +use rustc_span::Span; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::{self, PredicateObligation, TraitEngine, TraitEngineExt as _}; + +use std::cell::RefCell; +use std::ops::Deref; + +// Data shared between a "typeck root" and its nested bodies, +/// e.g. closures defined within the function. For example: +/// ```ignore (illustrative) +/// fn foo() { +/// bar(move|| { ... }) +/// } +/// ``` +/// Here, the function `foo()` and the closure passed to +/// `bar()` will each have their own `FnCtxt`, but they will +/// share the inference context, will process obligations together, +/// can access each other's local types (scoping permitted), etc. +pub(crate) struct TypeckRootCtxt<'tcx> { + pub(super) infcx: InferCtxt<'tcx>, + + pub(super) typeck_results: RefCell>, + + pub(super) locals: RefCell>>, + + pub(super) fulfillment_cx: RefCell>>, + + /// Some additional `Sized` obligations badly affect type inference. + /// These obligations are added in a later stage of typeck. + /// Removing these may also cause additional complications, see #101066. + pub(super) deferred_sized_obligations: + RefCell, Span, traits::ObligationCauseCode<'tcx>)>>, + + /// When we process a call like `c()` where `c` is a closure type, + /// we may not have decided yet whether `c` is a `Fn`, `FnMut`, or + /// `FnOnce` closure. In that case, we defer full resolution of the + /// call until upvar inference can kick in and make the + /// decision. We keep these deferred resolutions grouped by the + /// def-id of the closure, so that once we decide, we can easily go + /// back and process them. + pub(super) deferred_call_resolutions: RefCell>>>, + + pub(super) deferred_cast_checks: RefCell>>, + + pub(super) deferred_transmute_checks: RefCell, Ty<'tcx>, HirId)>>, + + pub(super) deferred_asm_checks: RefCell, HirId)>>, + + pub(super) deferred_coroutine_interiors: RefCell)>>, + + /// Whenever we introduce an adjustment from `!` into a type variable, + /// we record that type variable here. This is later used to inform + /// fallback. See the `fallback` module for details. + pub(super) diverging_type_vars: RefCell>>, + + pub(super) infer_var_info: RefCell>, +} + +impl<'tcx> Deref for TypeckRootCtxt<'tcx> { + type Target = InferCtxt<'tcx>; + fn deref(&self) -> &Self::Target { + &self.infcx + } +} + +impl<'tcx> TypeckRootCtxt<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { + let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; + + let infcx = tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(def_id).build(); + let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); + + TypeckRootCtxt { + typeck_results, + fulfillment_cx: RefCell::new(>::new(&infcx)), + infcx, + locals: RefCell::new(Default::default()), + deferred_sized_obligations: RefCell::new(Vec::new()), + deferred_call_resolutions: RefCell::new(Default::default()), + deferred_cast_checks: RefCell::new(Vec::new()), + deferred_transmute_checks: RefCell::new(Vec::new()), + deferred_asm_checks: RefCell::new(Vec::new()), + deferred_coroutine_interiors: RefCell::new(Vec::new()), + diverging_type_vars: RefCell::new(Default::default()), + infer_var_info: RefCell::new(Default::default()), + } + } + + #[instrument(level = "debug", skip(self))] + pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { + if obligation.has_escaping_bound_vars() { + span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); + } + + self.update_infer_var_info(&obligation); + + self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); + } + + pub(super) fn register_predicates(&self, obligations: I) + where + I: IntoIterator>, + { + for obligation in obligations { + self.register_predicate(obligation); + } + } + + pub(super) fn register_infer_ok_obligations(&self, infer_ok: InferOk<'tcx, T>) -> T { + self.register_predicates(infer_ok.obligations); + infer_ok.value + } + + pub fn update_infer_var_info(&self, obligation: &PredicateObligation<'tcx>) { + let infer_var_info = &mut self.infer_var_info.borrow_mut(); + + // (*) binder skipped + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(tpred)) = + obligation.predicate.kind().skip_binder() + && let Some(ty) = + self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t)) + && self.tcx.lang_items().sized_trait().is_some_and(|st| st != tpred.trait_ref.def_id) + { + let new_self_ty = self.tcx.types.unit; + + // Then construct a new obligation with Self = () added + // to the ParamEnv, and see if it holds. + let o = obligation.with( + self.tcx, + obligation.predicate.kind().rebind( + // (*) binder moved here + ty::PredicateKind::Clause(ty::ClauseKind::Trait( + tpred.with_self_ty(self.tcx, new_self_ty), + )), + ), + ); + // Don't report overflow errors. Otherwise equivalent to may_hold. + if let Ok(result) = self.probe(|_| self.evaluate_obligation(&o)) + && result.may_apply() + { + infer_var_info.entry(ty).or_default().self_in_trait = true; + } + } + + if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) = + obligation.predicate.kind().skip_binder() + { + // If the projection predicate (Foo::Bar == X) has X as a non-TyVid, + // we need to make it into one. + if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) { + debug!("infer_var_info: {:?}.output = true", vid); + infer_var_info.entry(vid).or_default().output = true; + } + } + } +} diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index be14f5bf0d8b5..2bf4f51a8038a 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -38,12 +38,14 @@ use rustc_errors::{Applicability, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::HirId; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; 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; @@ -87,7 +89,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] enum UpvarMigrationInfo { /// We previously captured all of `x`, but now we capture some sub-path. - CapturingPrecise { source_expr: Option, var_name: String }, + CapturingPrecise { source_expr: Option, var_name: String }, CapturingNothing { // where the variable appears in the closure (but is not captured) use_span: Span, @@ -130,7 +132,7 @@ struct MigrationLintNote { /// Intermediate format to store the hir id of the root variable and a HashSet containing /// information on why the root variable should be fully captured struct NeededMigration { - var_hir_id: hir::HirId, + var_hir_id: HirId, diagnostics_info: Vec, } @@ -162,11 +164,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self, body), level = "debug")] fn analyze_closure( &self, - closure_hir_id: hir::HirId, + closure_hir_id: HirId, span: Span, body_id: hir::BodyId, body: &'tcx hir::Body<'tcx>, - capture_clause: hir::CaptureBy, + mut capture_clause: hir::CaptureBy, ) { // Extract the type of the closure. let ty = self.node_ty(closure_hir_id); @@ -191,6 +193,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); @@ -217,7 +220,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bug!(); }; for stmt in block.stmts { - let hir::StmtKind::Let(hir::Local { + let hir::StmtKind::Let(hir::LetStmt { init: Some(init), source: hir::LocalSource::AsyncFn, pat, @@ -226,8 +229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else { bug!(); }; - let hir::PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), _, _, _) = - pat.kind + let hir::PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), _, _, _) = pat.kind else { // Complex pattern, skip the non-upvar local. continue; @@ -259,6 +261,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); + // If a coroutine is comes from a coroutine-closure that is `move`, but + // the coroutine-closure was inferred to be `FnOnce` during signature + // inference, then it's still possible that we try to borrow upvars from + // the coroutine-closure because they are not used by the coroutine body + // in a way that forces a move. + // + // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` + // coroutine bodies can't borrow from their parent closure. To fix this, + // we force the inner coroutine to also be `move`. This only matters for + // coroutine-closures that are `move` since otherwise they themselves will + // be borrowing from the outer environment, so there's no self-borrows occuring. + if let UpvarArgs::Coroutine(..) = args + && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = + self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") + && let parent_hir_id = + self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id)) + && let parent_ty = self.node_ty(parent_hir_id) + && let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty) + { + capture_clause = self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause; + } + debug!( "For closure={:?}, capture_information={:#?}", closure_def_id, delegate.capture_information @@ -339,46 +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::from_usize(0), - kind: ty::BoundRegionKind::BrEnv, - }, + 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, @@ -402,16 +436,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Additionally, we can now constrain the coroutine's kind type. - let ty::Coroutine(_, coroutine_args) = - *self.typeck_results.borrow().expr_ty(body.value).kind() - else { - bug!(); - }; - self.demand_eqtype( - span, - coroutine_args.as_coroutine().kind_ty(), - Ty::from_closure_kind(self.tcx, closure_kind), - ); + // + // We only do this if `infer_kind`, because if we have constrained + // the kind from closure signature inference, the kind inferred + // for the inner coroutine may actually be more restrictive. + if infer_kind { + let ty::Coroutine(_, coroutine_args) = + *self.typeck_results.borrow().expr_ty(body.value).kind() + else { + bug!(); + }; + self.demand_eqtype( + span, + coroutine_args.as_coroutine().kind_ty(), + Ty::from_coroutine_closure_kind(self.tcx, closure_kind), + ); + } } self.log_closure_min_capture_info(closure_def_id, span); @@ -1058,7 +1098,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn compute_2229_migrations_for_trait( &self, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, - var_hir_id: hir::HirId, + var_hir_id: HirId, closure_clause: hir::CaptureBy, ) -> Option>> { let auto_traits_def_id = [ @@ -1170,7 +1210,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_span: Span, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, closure_clause: hir::CaptureBy, - var_hir_id: hir::HirId, + var_hir_id: HirId, ) -> Option> { let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id)); @@ -1610,7 +1650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn place_for_root_variable( &self, closure_def_id: LocalDefId, - var_hir_id: hir::HirId, + var_hir_id: HirId, ) -> Place<'tcx> { let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); @@ -1711,15 +1751,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); - let mut is_mutbl = match bm { - ty::BindByValue(mutability) => mutability, - ty::BindByReference(_) => hir::Mutability::Not, - }; + let mut is_mutbl = bm.1; for pointer_ty in place.deref_tys() { match pointer_ty.kind() { // We don't capture derefs of raw ptrs - ty::RawPtr(_) => unreachable!(), + ty::RawPtr(_, _) => unreachable!(), // Dereferencing a mut-ref allows us to mut the Place if we don't deref // an immut-ref after on top of this. @@ -1739,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>( @@ -1780,16 +1874,14 @@ fn apply_capture_kind_on_capture_ty<'tcx>( ) -> Ty<'tcx> { match capture_kind { ty::UpvarCapture::ByValue => ty, - ty::UpvarCapture::ByRef(kind) => Ty::new_ref( - tcx, - region.unwrap(), - ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() }, - ), + ty::UpvarCapture::ByRef(kind) => { + Ty::new_ref(tcx, region.unwrap(), ty, kind.to_mutbl_lossy()) + } } } /// Returns the Span of where the value with the provided HirId would be dropped -fn drop_location_span(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> Span { +fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span { let owner_id = tcx.hir().get_enclosing_scope(hir_id).unwrap(); let owner_node = tcx.hir_node(owner_id); @@ -1841,7 +1933,7 @@ struct InferBorrowKind<'tcx> { /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } /// ``` capture_information: InferredCaptureInformation<'tcx>, - fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, + fake_reads: Vec<(Place<'tcx>, FakeReadCause, HirId)>, } impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { @@ -1849,7 +1941,7 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { &mut self, place: &PlaceWithHirId<'tcx>, cause: FakeReadCause, - diag_expr_id: hir::HirId, + diag_expr_id: HirId, ) { let PlaceBase::Upvar(_) = place.place.base else { return }; @@ -1864,7 +1956,7 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { } #[instrument(skip(self), level = "debug")] - fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { + fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); @@ -1882,7 +1974,7 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { fn borrow( &mut self, place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, + diag_expr_id: HirId, bk: ty::BorrowKind, ) { let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; @@ -1913,7 +2005,7 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { } #[instrument(skip(self), level = "debug")] - fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { + fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { self.borrow(assignee_place, diag_expr_id, ty::BorrowKind::MutBorrow); } } @@ -2100,14 +2192,14 @@ fn construct_capture_info_string<'tcx>( format!("{place_str} -> {capture_kind_str}") } -fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { +fn var_name(tcx: TyCtxt<'_>, var_hir_id: HirId) -> Symbol { tcx.hir().name(var_hir_id) } #[instrument(level = "debug", skip(tcx))] fn should_do_rust_2021_incompatible_closure_captures_analysis( tcx: TyCtxt<'_>, - closure_id: hir::HirId, + closure_id: HirId, ) -> bool { if tcx.sess.at_least_rust_2021() { return false; diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 0740a3fd3e970..0a40ffb0d5aab 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -7,6 +7,7 @@ use rustc_data_structures::unord::ExtendUnord; use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::HirId; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -134,7 +135,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.fcx.tcx } - fn write_ty_to_typeck_results(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) { + fn write_ty_to_typeck_results(&mut self, hir_id: HirId, ty: Ty<'tcx>) { debug!("write_ty_to_typeck_results({:?}, {:?})", hir_id, ty); assert!( !ty.has_infer() && !ty.has_placeholders() && !ty.has_free_regions(), @@ -345,13 +346,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { _ => {} }; + self.visit_skipped_ref_pats(p.hir_id); self.visit_pat_adjustments(p.span, p.hir_id); self.visit_node_id(p.span, p.hir_id); intravisit::walk_pat(self, p); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { intravisit::walk_local(self, l); let var_ty = self.fcx.local_ty(l.span, l.hir_id); let var_ty = self.resolve(var_ty, &l.span); @@ -460,7 +462,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fcx_typeck_results.closure_kind_origins().items_in_stable_order(); for (local_id, origin) in fcx_closure_kind_origins { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; + let hir_id = HirId { owner: common_hir_owner, local_id }; let place_span = origin.0; let place = self.resolve(origin.1.clone(), &place_span); self.typeck_results.closure_kind_origins_mut().insert(hir_id, (place_span, place)); @@ -489,7 +491,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let mut errors_buffer = Vec::new(); for (local_id, c_ty) in sorted_user_provided_types { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; + let hir_id = HirId { owner: common_hir_owner, local_id }; if let ty::UserType::TypeOf(_, user_args) = c_ty.value { // This is a unit-testing mechanism. @@ -512,7 +514,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.user_provided_types_mut().extend( fcx_typeck_results.user_provided_types().items().map(|(local_id, c_ty)| { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; + let hir_id = HirId { owner: common_hir_owner, local_id }; if cfg!(debug_assertions) && c_ty.has_infer() { span_bug!( @@ -549,15 +551,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_coroutine_interior(&mut self) { let fcx_typeck_results = self.fcx.typeck_results.borrow(); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - self.tcx().with_stable_hashing_context(move |ref hcx| { - for (&expr_def_id, predicates) in - fcx_typeck_results.coroutine_interior_predicates.to_sorted(hcx, false).into_iter() - { - let predicates = - self.resolve(predicates.clone(), &self.fcx.tcx.def_span(expr_def_id)); - self.typeck_results.coroutine_interior_predicates.insert(expr_def_id, predicates); - } - }) + for (predicate, cause) in &fcx_typeck_results.coroutine_stalled_predicates { + let (predicate, cause) = self.resolve((*predicate, cause.clone()), &cause.span); + self.typeck_results.coroutine_stalled_predicates.insert((predicate, cause)); + } } #[instrument(skip(self), level = "debug")] @@ -603,7 +600,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } - fn visit_field_id(&mut self, hir_id: hir::HirId) { + fn visit_field_id(&mut self, hir_id: HirId) { if let Some(index) = self.fcx.typeck_results.borrow_mut().field_indices_mut().remove(hir_id) { self.typeck_results.field_indices_mut().insert(hir_id, index); @@ -616,7 +613,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } #[instrument(skip(self, span), level = "debug")] - fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) { + fn visit_node_id(&mut self, span: Span, hir_id: HirId) { // Export associated path extensions and method resolutions. if let Some(def) = self.fcx.typeck_results.borrow_mut().type_dependent_defs_mut().remove(hir_id) @@ -643,7 +640,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } #[instrument(skip(self, span), level = "debug")] - fn visit_adjustments(&mut self, span: Span, hir_id: hir::HirId) { + fn visit_adjustments(&mut self, span: Span, hir_id: HirId) { let adjustment = self.fcx.typeck_results.borrow_mut().adjustments_mut().remove(hir_id); match adjustment { None => { @@ -659,7 +656,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } #[instrument(skip(self, span), level = "debug")] - fn visit_pat_adjustments(&mut self, span: Span, hir_id: hir::HirId) { + fn visit_pat_adjustments(&mut self, span: Span, hir_id: HirId) { let adjustment = self.fcx.typeck_results.borrow_mut().pat_adjustments_mut().remove(hir_id); match adjustment { None => { @@ -674,6 +671,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + #[instrument(skip(self), level = "debug")] + fn visit_skipped_ref_pats(&mut self, hir_id: hir::HirId) { + if self.fcx.typeck_results.borrow_mut().skipped_ref_pats_mut().remove(hir_id) { + debug!("node is a skipped ref pat"); + self.typeck_results.skipped_ref_pats_mut().insert(hir_id); + } + } + fn visit_liberated_fn_sigs(&mut self) { let fcx_typeck_results = self.fcx.typeck_results.borrow(); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); @@ -682,7 +687,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let fcx_liberated_fn_sigs = fcx_typeck_results.liberated_fn_sigs().items_in_stable_order(); for (local_id, &fn_sig) in fcx_liberated_fn_sigs { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; + let hir_id = HirId { owner: common_hir_owner, local_id }; let fn_sig = self.resolve(fn_sig, &hir_id); self.typeck_results.liberated_fn_sigs_mut().insert(hir_id, fn_sig); } @@ -696,7 +701,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let fcx_fru_field_types = fcx_typeck_results.fru_field_types().items_in_stable_order(); for (local_id, ftys) in fcx_fru_field_types { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; + let hir_id = HirId { owner: common_hir_owner, local_id }; let ftys = self.resolve(ftys.clone(), &hir_id); self.typeck_results.fru_field_types_mut().insert(hir_id, ftys); } @@ -710,7 +715,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { for (local_id, &(container, ref indices)) in fcx_typeck_results.offset_of_data().items_in_stable_order() { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; + let hir_id = HirId { owner: common_hir_owner, local_id }; let container = self.resolve(container, &hir_id); self.typeck_results.offset_of_data_mut().insert(hir_id, (container, indices.clone())); } @@ -745,7 +750,7 @@ impl Locatable for Span { } } -impl Locatable for hir::HirId { +impl Locatable for HirId { fn to_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.hir().span(*self) } diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index aa6f184a2d7d1..8c66f239f8ee9 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -46,6 +46,7 @@ use rustc_middle::dep_graph::{ }; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; +use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 0729986f32fdf..79402c88de47a 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -6,8 +6,6 @@ #![feature(rustdoc_internals)] #![allow(internal_features)] -#[macro_use] -extern crate rustc_middle; #[macro_use] extern crate tracing; diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 24512dea93999..e901ca36daded 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -65,7 +65,7 @@ const BASE_HIR: &[&str] = &[ const BASE_IMPL: &[&str] = &[label_strs::associated_item_def_ids, label_strs::generics_of, label_strs::impl_trait_header]; -/// DepNodes for mir_built/Optimized, which is relevant in "executable" +/// DepNodes for exported mir bodies, which is relevant in "executable" /// code, i.e., functions+methods const BASE_MIR: &[&str] = &[label_strs::optimized_mir, label_strs::promoted_mir]; @@ -148,7 +148,7 @@ pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { dirty_clean_visitor.check_item(id.owner_id.def_id); } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 1462037c8c8e7..3d7c0cfc30aa3 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -110,6 +110,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; use rustc_errors::ErrorGuaranteed; use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy}; +use rustc_middle::bug; use rustc_session::config::CrateType; use rustc_session::output::{collect_crate_types, find_crate_name}; use rustc_session::{Session, StableCrateId}; @@ -162,8 +163,11 @@ pub fn query_cache_path(sess: &Session) -> PathBuf { fn lock_file_path(session_dir: &Path) -> PathBuf { let crate_dir = session_dir.parent().unwrap(); - let directory_name = session_dir.file_name().unwrap().to_string_lossy(); - assert_no_characters_lost(&directory_name); + let directory_name = session_dir + .file_name() + .unwrap() + .to_str() + .expect("malformed session dir name: contains non-Unicode characters"); let dash_indices: Vec<_> = directory_name.match_indices('-').map(|(idx, _)| idx).collect(); if dash_indices.len() != 3 { @@ -329,8 +333,11 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { debug!("finalize_session_directory() - session directory: {}", incr_comp_session_dir.display()); - let old_sub_dir_name = incr_comp_session_dir.file_name().unwrap().to_string_lossy(); - assert_no_characters_lost(&old_sub_dir_name); + let old_sub_dir_name = incr_comp_session_dir + .file_name() + .unwrap() + .to_str() + .expect("malformed session dir name: contains non-Unicode characters"); // Keep the 's-{timestamp}-{random-number}' prefix, but replace the // '-working' part with the SVH of the crate @@ -527,8 +534,10 @@ where for session_dir in iter { debug!("find_source_directory_in_iter - inspecting `{}`", session_dir.display()); - let directory_name = session_dir.file_name().unwrap().to_string_lossy(); - assert_no_characters_lost(&directory_name); + let Some(directory_name) = session_dir.file_name().unwrap().to_str() else { + debug!("find_source_directory_in_iter - ignoring"); + continue; + }; if source_directories_already_tried.contains(&session_dir) || !is_session_directory(&directory_name) @@ -619,12 +628,6 @@ fn crate_path(sess: &Session) -> PathBuf { incr_dir.join(crate_name) } -fn assert_no_characters_lost(s: &str) { - if s.contains('\u{FFFD}') { - bug!("Could not losslessly convert '{}'.", s) - } -} - fn is_old_enough_to_be_collected(timestamp: SystemTime) -> bool { timestamp < SystemTime::now() - Duration::from_secs(10) } @@ -657,14 +660,14 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< }; let entry_name = dir_entry.file_name(); - let entry_name = entry_name.to_string_lossy(); + let Some(entry_name) = entry_name.to_str() else { + continue; + }; if is_session_directory_lock_file(&entry_name) { - assert_no_characters_lost(&entry_name); - lock_files.insert(entry_name.into_owned()); + lock_files.insert(entry_name.to_string()); } else if is_session_directory(&entry_name) { - assert_no_characters_lost(&entry_name); - session_directories.insert(entry_name.into_owned()); + session_directories.insert(entry_name.to_string()); } else { // This is something we don't know, leave it alone } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 357f2ae92d4c3..26aaa24771fe8 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -11,6 +11,7 @@ use rustc_session::config::IncrementalStateAssertion; use rustc_session::Session; use rustc_span::ErrorGuaranteed; use std::path::{Path, PathBuf}; +use std::sync::Arc; use super::data::*; use super::file_format; @@ -88,7 +89,7 @@ fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) { work_product::delete_workproduct_files(sess, &swp.work_product); } -fn load_dep_graph(sess: &Session) -> LoadResult<(SerializedDepGraph, WorkProductMap)> { +fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkProductMap)> { let prof = sess.prof.clone(); if sess.opts.incremental.is_none() { diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 32759f5284af7..9777f76928095 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -10,6 +10,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_serialize::Encodable as RustcEncodable; use rustc_session::Session; use std::fs; +use std::sync::Arc; use super::data::*; use super::dirty_clean; @@ -147,7 +148,7 @@ fn encode_query_cache(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult /// and moves it to the permanent dep-graph path pub(crate) fn build_dep_graph( sess: &Session, - prev_graph: SerializedDepGraph, + prev_graph: Arc, prev_work_products: WorkProductMap, ) -> Option { if sess.opts.incremental.is_none() { diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 12f8e42c78f94..e37af6610dd9b 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -400,7 +400,7 @@ enum Chunk { } // This type is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] crate::static_assert_size!(Chunk, 16); impl ChunkedBitSet { diff --git a/compiler/rustc_index_macros/src/lib.rs b/compiler/rustc_index_macros/src/lib.rs index 532cac5791e06..015518ae4d688 100644 --- a/compiler/rustc_index_macros/src/lib.rs +++ b/compiler/rustc_index_macros/src/lib.rs @@ -34,13 +34,7 @@ mod newtype; #[proc_macro] #[cfg_attr( feature = "nightly", - allow_internal_unstable( - step_trait, - rustc_attrs, - trusted_step, - spec_option_partial_eq, - min_specialization - ) + allow_internal_unstable(step_trait, rustc_attrs, trusted_step, min_specialization) )] pub fn newtype_index(input: TokenStream) -> TokenStream { newtype::newtype(input) diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs index 0b25628b9e195..fe9a048734fc7 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -156,32 +156,6 @@ impl Parse for Newtype { } }; - let spec_partial_eq_impl = if let Lit::Int(max) = &max { - if let Ok(max_val) = max.base10_parse::() { - quote! { - #gate_rustc_only - impl core::option::SpecOptionPartialEq for #name { - #[inline] - fn eq(l: &Option, r: &Option) -> bool { - if #max_val < u32::MAX { - l.map(|i| i.as_u32()).unwrap_or(#max_val+1) == r.map(|i| i.as_u32()).unwrap_or(#max_val+1) - } else { - match (l, r) { - (Some(l), Some(r)) => r == l, - (None, None) => true, - _ => false - } - } - } - } - } - } else { - quote! {} - } - } else { - quote! {} - }; - Ok(Self(quote! { #(#attrs)* #[derive(Clone, Copy, PartialEq, Eq, Hash, #(#derive_paths),*)] @@ -200,6 +174,9 @@ impl Parse for Newtype { /// Maximum value the index can take. #vis const MAX: Self = Self::from_u32(#max); + /// Zero value of the index. + #vis const ZERO: Self = Self::from_u32(0); + /// Creates a new index from a given `usize`. /// /// # Panics @@ -283,8 +260,6 @@ impl Parse for Newtype { #step - #spec_partial_eq_impl - impl From<#name> for u32 { #[inline] fn from(v: #name) -> u32 { diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index e44a6ae3b3f2e..64f52ea7ac196 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 -> @@ -169,7 +172,7 @@ infer_lifetime_param_suggestion_elided = each elided lifetime in input position infer_meant_byte_literal = if you meant to write a byte literal, prefix with `b` infer_meant_char_literal = if you meant to write a `char` literal, use single quotes -infer_meant_str_literal = if you meant to write a `str` literal, use double quotes +infer_meant_str_literal = if you meant to write a string literal, use double quotes infer_mismatched_static_lifetime = incompatible lifetime on type infer_more_targeted = {$has_param_name -> [true] `{$param_name}` @@ -270,9 +273,6 @@ infer_ril_introduced_by = requirement introduced by this return type infer_ril_introduced_here = `'static` requirement introduced here infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type -infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions - -infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object infer_source_kind_closure_return = try giving this closure an explicit return type diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index a3cf0d8e5208a..2acaeac24398d 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>, #[subdiagnostic] pub multi_suggestions: Vec>, + #[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>, #[subdiagnostic] pub multi_suggestions: Vec>, + #[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>, #[subdiagnostic] pub multi_suggestions: Vec>, + #[note(infer_full_type_written)] + pub was_written: Option<()>, + pub path: PathBuf, } // Used when a better one isn't available @@ -228,7 +239,7 @@ impl Subdiagnostic for RegionOriginNote<'_> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { let mut label_or_note = |span, msg: DiagMessage| { let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); @@ -293,7 +304,7 @@ impl Subdiagnostic for LifetimeMismatchLabels { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { match self { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { @@ -341,7 +352,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { let mut mk_suggestion = || { let ( @@ -443,7 +454,7 @@ impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { fn add_to_diag_with>( mut self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { self.unmet_requirements .push_span_label(self.binding_span, fluent::infer_msl_introduces_static); @@ -762,7 +773,7 @@ impl Subdiagnostic for ConsiderBorrowingParamHelp { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - f: F, + f: &F, ) { let mut type_param_span: MultiSpan = self.spans.clone().into(); for &span in &self.spans { @@ -807,7 +818,7 @@ impl Subdiagnostic for DynTraitConstraintSuggestion { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - f: F, + f: &F, ) { let mut multi_span: MultiSpan = vec![self.span].into(); multi_span.push_span_label(self.span, fluent::infer_dtcs_has_lifetime_req_label); @@ -854,7 +865,7 @@ impl Subdiagnostic for ReqIntroducedLocations { fn add_to_diag_with>( mut self, diag: &mut Diag<'_, G>, - f: F, + f: &F, ) { for sp in self.spans { self.span.push_span_label(sp, fluent::infer_ril_introduced_here); @@ -877,7 +888,7 @@ impl Subdiagnostic for MoreTargeted { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { diag.code(E0772); diag.primary_message(fluent::infer_more_targeted); @@ -1262,24 +1273,6 @@ pub enum SuggestAccessingField<'a> { }, } -#[derive(Subdiagnostic)] -pub enum SuggestBoxingForReturnImplTrait { - #[multipart_suggestion(infer_sbfrit_change_return_type, applicability = "maybe-incorrect")] - ChangeReturnType { - #[suggestion_part(code = "Box, - #[suggestion_part(code = ")")] - ends: Vec, - }, -} - #[derive(Subdiagnostic)] #[multipart_suggestion(infer_stp_wrap_one, applicability = "maybe-incorrect")] pub struct SuggestTuplePatternOne { @@ -1300,7 +1293,7 @@ impl Subdiagnostic for SuggestTuplePatternMany { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - f: F, + f: &F, ) { diag.arg("path", self.path); let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into()); @@ -1339,15 +1332,12 @@ pub enum TypeErrorAdditionalDiags { span: Span, code: String, }, - #[suggestion( - infer_meant_str_literal, - code = "\"{code}\"", - applicability = "machine-applicable" - )] + #[multipart_suggestion(infer_meant_str_literal, applicability = "machine-applicable")] MeantStrLiteral { - #[primary_span] - span: Span, - code: String, + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, }, #[suggestion( infer_consider_specifying_length, diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 7b962b0140808..70d94873530e7 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -163,7 +163,7 @@ impl Subdiagnostic for RegionExplanation<'_> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - f: F, + f: &F, ) { diag.arg("pref_kind", self.prefix); diag.arg("suff_kind", self.suffix); diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 94088216c029a..0f21d3966c40b 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -75,7 +75,7 @@ impl<'tcx> InferCtxt<'tcx> { pub fn fork_with_intercrate(&self, intercrate: bool) -> Self { Self { tcx: self.tcx, - defining_use_anchor: self.defining_use_anchor, + defining_opaque_types: self.defining_opaque_types, considering_regions: self.considering_regions, skip_leak_check: self.skip_leak_check, inner: self.inner.clone(), @@ -282,7 +282,7 @@ impl<'a, 'tcx> Trace<'a, 'tcx> { { let Trace { at, trace } = self; debug_assert!(at.infcx.next_trait_solver()); - let mut fields = at.infcx.combine_fields(trace, at.param_env, DefineOpaqueTypes::No); + let mut fields = at.infcx.combine_fields(trace, at.param_env, DefineOpaqueTypes::Yes); fields .equate(StructurallyRelateAliases::Yes) .relate(a, b) @@ -424,25 +424,7 @@ impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> { ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: PolyTraitRefs(ExpectedFound::new( - a_is_expected, - ty::Binder::dummy(a), - ty::Binder::dummy(b), - )), - } - } -} - -impl<'tcx> ToTrace<'tcx> for ty::PolyTraitRef<'tcx> { - fn to_trace( - cause: &ObligationCause<'tcx>, - a_is_expected: bool, - a: Self, - b: Self, - ) -> TypeTrace<'tcx> { - TypeTrace { - cause: cause.clone(), - values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b)), + values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)), } } } diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 9f70fee993dfe..4d712e9ffd372 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -42,7 +42,7 @@ impl<'tcx> InferCtxt<'tcx> { V: TypeFoldable>, { let (param_env, value) = value.into_parts(); - let param_env = self.tcx.canonical_param_env_cache.get_or_insert( + let mut param_env = self.tcx.canonical_param_env_cache.get_or_insert( self.tcx, param_env, query_state, @@ -59,6 +59,8 @@ impl<'tcx> InferCtxt<'tcx> { }, ); + param_env.defining_opaque_types = self.defining_opaque_types; + Canonicalizer::canonicalize_with_base( param_env, value, @@ -440,6 +442,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { | ty::Tuple(..) | ty::Alias(..) | ty::Foreign(..) + | ty::Pat(..) | ty::Param(..) => { if t.flags().intersects(self.needs_canonical_flags) { t.super_fold_with(self) @@ -540,6 +543,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: (), + defining_opaque_types: infcx.map(|i| i.defining_opaque_types).unwrap_or_default(), }; Canonicalizer::canonicalize_with_base( base, @@ -609,7 +613,15 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { .max() .unwrap_or(ty::UniverseIndex::ROOT); - Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) } + assert!( + !infcx.is_some_and(|infcx| infcx.defining_opaque_types != base.defining_opaque_types) + ); + Canonical { + max_universe, + variables: canonical_variables, + value: (base.value, out_value), + defining_opaque_types: base.defining_opaque_types, + } } /// Creates a canonical variable replacing `kind` from the input, @@ -790,7 +802,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { const_var: ty::Const<'tcx>, ) -> ty::Const<'tcx> { debug_assert!( - !self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve(const_var)) + !self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var)) ); let var = self.canonical_var(info, const_var.into()); ty::Const::new_bound(self.tcx, self.binder_index, var, self.fold_ty(const_var.ty())) diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs index c8adbf7f57ab4..f95cc13623cd1 100644 --- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs +++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs @@ -7,6 +7,7 @@ //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_macros::extension; use rustc_middle::ty::fold::{FnMutDelegate, TypeFoldable}; use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::{self, TyCtxt}; diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 1d203a29b1492..734fa919eb5c0 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -21,8 +21,8 @@ //! //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html -use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind}; -use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::ConstVariableOrigin; +use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin}; use rustc_index::IndexVec; use rustc_middle::infer::unify_key::EffectVarValue; use rustc_middle::ty::fold::TypeFoldable; @@ -38,8 +38,8 @@ mod instantiate; pub mod query_response; impl<'tcx> InferCtxt<'tcx> { - /// Creates an instantiation S for the canonical value with fresh - /// inference variables and applies it to the canonical value. + /// Creates an instantiation S for the canonical value with fresh inference + /// variables and placeholders then applies it to the canonical value. /// Returns both the instantiated result *and* the instantiation S. /// /// This can be invoked as part of constructing an @@ -50,7 +50,7 @@ impl<'tcx> InferCtxt<'tcx> { /// At the end of processing, the instantiation S (once /// canonicalized) then represents the values that you computed /// for each of the canonical inputs to your query. - pub fn instantiate_canonical_with_fresh_inference_vars( + pub fn instantiate_canonical( &self, span: Span, canonical: &Canonical<'tcx, T>, @@ -115,7 +115,7 @@ impl<'tcx> InferCtxt<'tcx> { CanonicalVarKind::Ty(ty_kind) => { let ty = match ty_kind { CanonicalTyVarKind::General(ui) => self.next_ty_var_in_universe( - TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }, + TypeVariableOrigin { param_def_id: None, span }, universe_map(ui), ), @@ -148,7 +148,7 @@ impl<'tcx> InferCtxt<'tcx> { CanonicalVarKind::Const(ui, ty) => self .next_const_var_in_universe( ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span }, + ConstVariableOrigin { param_def_id: None, span }, universe_map(ui), ) .into(), diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 9d2e065afa37e..b948067e750eb 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -505,12 +505,9 @@ impl<'tcx> InferCtxt<'tcx> { let b = instantiate_value(self.tcx, &result_args, b); debug!(?a, ?b, "constrain opaque type"); // We use equate here instead of, for example, just registering the - // opaque type's hidden value directly, because we may be instantiating - // a query response that was canonicalized in an InferCtxt that had - // a different defining anchor. In that case, we may have inferred - // `NonLocalOpaque := LocalOpaque` but can only instantiate it in - // the other direction as `LocalOpaque := NonLocalOpaque`. Using eq - // here allows us to try both directions (in `InferCtxt::handle_opaque_type`). + // opaque type's hidden value directly, because the hidden type may have been an inference + // variable that got constrained to the opaque type itself. In that case we want to equate + // the generic args of the opaque with the generic params of its hidden type version. obligations.extend( self.at(cause, param_env) .eq( diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 5d2a95593cd25..635bbca37ecc2 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -61,13 +61,14 @@ use crate::traits::{ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, - ErrorGuaranteed, IntoDiagArg, + ErrorGuaranteed, IntoDiagArg, StringPart, }; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; +use rustc_macros::extension; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError}; use rustc_middle::ty::relate::{self, RelateResult, TypeRelation}; @@ -403,8 +404,10 @@ impl<'tcx> InferCtxt<'tcx> { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; - self.tcx.explicit_item_bounds(def_id).iter_instantiated_copied(self.tcx, args).find_map( - |(predicate, _)| { + self.tcx + .explicit_item_super_predicates(def_id) + .iter_instantiated_copied(self.tcx, args) + .find_map(|(predicate, _)| { predicate .kind() .map_bound(|kind| match kind { @@ -417,8 +420,7 @@ impl<'tcx> InferCtxt<'tcx> { }) .no_bound_vars() .flatten() - }, - ) + }) } } @@ -783,7 +785,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { prior_arm_ty, source, ref prior_non_diverging_arms, - opt_suggest_box_span, scrut_span, .. }) => match source { @@ -852,17 +853,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) { err.subdiagnostic(self.dcx(), subdiag); } - if let Some(ret_sp) = opt_suggest_box_span { - // Get return type span and point to it. - self.suggest_boxing_for_return_impl_trait( - err, - ret_sp, - prior_non_diverging_arms - .iter() - .chain(std::iter::once(&arm_span)) - .copied(), - ); - } } }, ObligationCauseCode::IfExpression(box IfExpressionCause { @@ -871,7 +861,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { then_ty, else_ty, outer_span, - opt_suggest_box_span, + .. }) => { let then_span = self.find_block_span_from_hir_id(then_id); let else_span = self.find_block_span_from_hir_id(else_id); @@ -889,30 +879,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) { err.subdiagnostic(self.dcx(), subdiag); } - // don't suggest wrapping either blocks in `if .. {} else {}` - let is_empty_arm = |id| { - let hir::Node::Block(blk) = self.tcx.hir_node(id) else { - return false; - }; - if blk.expr.is_some() || !blk.stmts.is_empty() { - return false; - } - let Some((_, hir::Node::Expr(expr))) = self.tcx.hir().parent_iter(id).nth(1) - else { - return false; - }; - matches!(expr.kind, hir::ExprKind::If(..)) - }; - if let Some(ret_sp) = opt_suggest_box_span - && !is_empty_arm(then_id) - && !is_empty_arm(else_id) - { - self.suggest_boxing_for_return_impl_trait( - err, - ret_sp, - [then_span, else_span].into_iter(), - ); - } } ObligationCauseCode::LetElse => { err.help("try adding a diverging expression, such as `return` or `panic!(..)`"); @@ -1073,7 +1039,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS) .name_all_regions(sig) .unwrap(); - let lts: Vec = reg.into_values().map(|kind| kind.to_string()).collect(); + let lts: Vec = + reg.into_items().map(|(_, kind)| kind.to_string()).into_sorted_stable_ord(); (if lts.is_empty() { String::new() } else { format!("for<{}> ", lts.join(", ")) }, sig) }; @@ -1687,7 +1654,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .report(diag); (false, Mismatch::Fixed("signature")) } - ValuePairs::PolyTraitRefs(_) => (false, Mismatch::Fixed("trait")), + ValuePairs::TraitRefs(_) => (false, Mismatch::Fixed("trait")), ValuePairs::Aliases(infer::ExpectedFound { expected, .. }) => { (false, Mismatch::Fixed(self.tcx.def_descr(expected.def_id))) } @@ -1951,6 +1918,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); if !is_simple_error || terr.must_include_note() { diag.note_expected_found(&expected_label, expected, &found_label, found); + + if let Some(ty::Closure(_, args)) = + exp_found.map(|expected_type_found| expected_type_found.found.kind()) + { + diag.highlighted_note(vec![ + StringPart::normal("closure has signature: `"), + StringPart::highlighted( + self.tcx + .signature_unclosure( + args.as_closure().sig(), + rustc_hir::Unsafety::Normal, + ) + .to_string(), + ), + StringPart::normal("`"), + ]); + } } } } @@ -2003,8 +1987,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.note_and_explain_type_err(diag, exp_found, cause, span, cause.body_id.to_def_id()); } - if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values - && let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() + if let Some(ValuePairs::TraitRefs(exp_found)) = values + && let ty::Closure(def_id, _) = exp_found.expected.self_ty().kind() && let Some(def_id) = def_id.as_local() && terr.involves_regions() { @@ -2078,16 +2062,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If a string was expected and the found expression is a character literal, // perhaps the user meant to write `"s"` to specify a string literal. (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { - if let Some(code) = - code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - { - suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { - span, - code: escape_literal(code), - }) - } - } + suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { + start: span.with_hi(span.lo() + BytePos(1)), + end: span.with_lo(span.hi() - BytePos(1)), + }) } // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, // we try to suggest to add the missing `let` for `if let Some(..) = expr` @@ -2139,7 +2117,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // the same span as the error and the type is specified. if let hir::Stmt { kind: - hir::StmtKind::Let(hir::Local { + hir::StmtKind::Let(hir::LetStmt { init: Some(hir::Expr { span: init_span, .. }), ty: Some(array_ty), .. @@ -2228,7 +2206,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::Aliases(exp_found) => self.expected_found_str(exp_found), infer::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), infer::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), - infer::PolyTraitRefs(exp_found) => { + infer::TraitRefs(exp_found) => { let pretty_exp_found = ty::error::ExpectedFound { expected: exp_found.expected.print_trait_sugared(), found: exp_found.found.print_trait_sugared(), @@ -2392,9 +2370,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { generic_param_scope = self.tcx.local_parent(generic_param_scope); } - // type_param_sugg_span is (span, has_bounds) + // type_param_sugg_span is (span, has_bounds, needs_parentheses) let (type_scope, type_param_sugg_span) = match bound_kind { - GenericKind::Param(ref param) => { + GenericKind::Param(param) => { let generics = self.tcx.generics_of(generic_param_scope); let def_id = generics.type_param(param, self.tcx).def_id.expect_local(); let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; @@ -2403,10 +2381,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // instead we suggest `T: 'a + 'b` in that case. let hir_generics = self.tcx.hir().get_generics(scope).unwrap(); let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) { - Some(span) => Some((span, true)), + Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)), // If `param` corresponds to `Self`, no usable suggestion span. None if generics.has_self && param.index == 0 => None, - None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false)), + None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false, None)), }; (scope, sugg_span) } @@ -2429,12 +2407,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut suggs = vec![]; let lt_name = self.suggest_name_region(sub, &mut suggs); - if let Some((sp, has_lifetimes)) = type_param_sugg_span + if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span && suggestion_scope == type_scope { let suggestion = if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") }; - suggs.push((sp, suggestion)) + + if let Some(open_paren_sp) = open_paren_sp { + suggs.push((open_paren_sp, "(".to_string())); + suggs.push((sp, format!("){suggestion}"))); + } else { + suggs.push((sp, suggestion)) + } } else if let GenericKind::Alias(ref p) = bound_kind && let ty::Projection = p.kind(self.tcx) && let DefKind::AssocTy = self.tcx.def_kind(p.def_id) @@ -2553,7 +2537,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i), hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i), hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"), - hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), + hir::OwnerNode::Synthetic => unreachable!(), } let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap(); 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 13e2152e45ea1..a2a38d1c507a1 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 @@ -3,7 +3,7 @@ use crate::errors::{ SourceKindMultiSuggestion, SourceKindSubdiag, }; use crate::infer::error_reporting::TypeErrCtxt; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::type_variable::TypeVariableOrigin; use crate::infer::InferCtxt; use rustc_errors::{codes::*, Diag, IntoDiagArg}; use rustc_hir as hir; @@ -11,20 +11,20 @@ use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; +use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; use rustc_middle::hir::nested_filter; -use rustc_middle::infer::unify_key::{ - ConstVariableOrigin, ConstVariableOriginKind, ConstVariableValue, -}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue}; 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_span::symbol::{kw, sym, Ident}; -use rustc_span::{BytePos, Span}; +use rustc_middle::ty::{ + self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, +}; +use rustc_span::symbol::{sym, Ident}; +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 +153,29 @@ impl UnderspecifiedArgKind { } } +struct ClosureEraser<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TypeFolder> 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| { @@ -163,8 +186,11 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte let mut infcx_inner = infcx.inner.borrow_mut(); let ty_vars = infcx_inner.type_variables(); let var_origin = ty_vars.var_origin(ty_vid); - if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = var_origin.kind - && name != kw::SelfUpper + if let Some(def_id) = var_origin.param_def_id + // The `Self` param of a trait has the def-id of the trait, + // since it's a synthetic parameter. + && infcx.tcx.def_kind(def_id) == DefKind::TyParam + && let name = infcx.tcx.item_name(def_id) && !var_origin.span.from_expansion() { let generics = infcx.tcx.generics_of(infcx.tcx.parent(def_id)); @@ -191,8 +217,8 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte None } ConstVariableValue::Unknown { origin, universe: _ } => { - if let ConstVariableOriginKind::ConstParameterDefinition(name, _) = origin.kind { - return Some(name); + if let Some(def_id) = origin.param_def_id { + Some(infcx.tcx.item_name(def_id)) } else { None } @@ -209,6 +235,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 +253,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() @@ -278,21 +303,18 @@ impl<'tcx> InferCtxt<'tcx> { let mut inner = self.inner.borrow_mut(); let ty_vars = &inner.type_variables(); let var_origin = ty_vars.var_origin(ty_vid); - if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = - var_origin.kind + if let Some(def_id) = var_origin.param_def_id + // The `Self` param of a trait has the def-id of the trait, + // since it's a synthetic parameter. + && self.tcx.def_kind(def_id) == DefKind::TyParam + && !var_origin.span.from_expansion() { - if name != kw::SelfUpper && !var_origin.span.from_expansion() { - return InferenceDiagnosticsData { - name: name.to_string(), - span: Some(var_origin.span), - kind: UnderspecifiedArgKind::Type { - prefix: "type parameter".into(), - }, - parent: InferenceDiagnosticsParentData::for_def_id( - self.tcx, def_id, - ), - }; - } + return InferenceDiagnosticsData { + name: self.tcx.item_name(def_id).to_string(), + span: Some(var_origin.span), + kind: UnderspecifiedArgKind::Type { prefix: "type parameter".into() }, + parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id), + }; } } @@ -317,11 +339,9 @@ impl<'tcx> InferCtxt<'tcx> { } ConstVariableValue::Unknown { origin, universe: _ } => origin, }; - if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = - origin.kind - { + if let Some(def_id) = origin.param_def_id { return InferenceDiagnosticsData { - name: name.to_string(), + name: self.tcx.item_name(def_id).to_string(), span: Some(origin.span), kind: UnderspecifiedArgKind::Const { is_parameter: true }, parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id), @@ -387,6 +407,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 +418,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 +429,8 @@ impl<'tcx> InferCtxt<'tcx> { infer_subdiags, multi_suggestions, bad_label, + was_written: None, + path: Default::default(), }), } } @@ -442,7 +468,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,17 +544,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), GenericArgKind::Type(_) => self .next_ty_var(TypeVariableOrigin { - span: rustc_span::DUMMY_SP, - kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + param_def_id: None, }) .into(), GenericArgKind::Const(arg) => self .next_const_var( arg.ty(), - ConstVariableOrigin { - span: rustc_span::DUMMY_SP, - kind: ConstVariableOriginKind::MiscVariable, - }, + ConstVariableOrigin { span: DUMMY_SP, param_def_id: None }, ) .into(), } @@ -546,40 +569,53 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { - let mut printer = fmt_printer(self, Namespace::ValueNS); - printer.print_def_path(def_id, args).unwrap(); - let def_path = printer.into_buffer(); - - // We only care about whether we have to add `&` or `&mut ` for now. - // This is the case if the last adjustment is a borrow and the - // first adjustment was not a builtin deref. - let adjustment = match typeck_results.expr_adjustments(receiver) { - [ - Adjustment { kind: Adjust::Deref(None), target: _ }, - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, - ] => "", - [ - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), target: _ }, - ] => hir::Mutability::from(*mut_).ref_prefix_str(), - _ => "", - }; + let placeholder = Some( + self.next_ty_var(TypeVariableOrigin { span: DUMMY_SP, param_def_id: None }), + ); + if let Some(args) = args.make_suggestable(self.infcx.tcx, true, placeholder) { + let mut printer = fmt_printer(self, Namespace::ValueNS); + printer.print_def_path(def_id, args).unwrap(); + let def_path = printer.into_buffer(); + + // We only care about whether we have to add `&` or `&mut ` for now. + // This is the case if the last adjustment is a borrow and the + // first adjustment was not a builtin deref. + let adjustment = match typeck_results.expr_adjustments(receiver) { + [ + Adjustment { kind: Adjust::Deref(None), target: _ }, + .., + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, + ] => "", + [ + .., + Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), + target: _, + }, + ] => hir::Mutability::from(*mut_).ref_prefix_str(), + _ => "", + }; - multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( - receiver.span, - def_path, - adjustment, - successor, - )); + multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( + receiver.span, + def_path, + adjustment, + successor, + )); + } } InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { - let ty_info = ty_to_string(self, ty, None); - multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( - ty_info, - data, - should_wrap_expr, - )); + let placeholder = Some( + self.next_ty_var(TypeVariableOrigin { span: DUMMY_SP, param_def_id: None }), + ); + if let Some(ty) = ty.make_suggestable(self.infcx.tcx, true, placeholder) { + let ty_info = ty_to_string(self, ty, None); + multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( + ty_info, + data, + should_wrap_expr, + )); + } } } match error_code { @@ -591,6 +627,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, @@ -600,6 +638,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, @@ -609,6 +649,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer_subdiags, multi_suggestions, bad_label: None, + was_written: path.as_ref().map(|_| ()), + path: path.unwrap_or_default(), }), } } @@ -673,22 +715,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) { + 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), } } } @@ -990,7 +1033,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { let generics_def_id = tcx.res_generics_def_id(path.res)?; let generics = tcx.generics_of(generics_def_id); if generics.has_impl_trait() { - None?; + do yeet (); } let insert_span = path.segments.last().unwrap().ident.span.shrink_to_hi().with_hi(path.span.hi()); @@ -1044,7 +1087,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { let generics = tcx.generics_of(def_id); let segment: Option<_> = try { if !segment.infer_args || generics.has_impl_trait() { - None?; + do yeet (); } let span = tcx.hir().span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); @@ -1107,7 +1150,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { self.tecx.tcx.hir() } - fn visit_local(&mut self, local: &'tcx Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { intravisit::walk_local(self, local); if let Some(ty) = self.opt_node_type(local.hir_id) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index 98719e240bda6..01e75d59f4dfe 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -195,13 +195,13 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { value_pairs: &ValuePairs<'tcx>, ) -> Option> { let (expected_args, found_args, trait_def_id) = match value_pairs { - ValuePairs::PolyTraitRefs(ExpectedFound { expected, found }) - if expected.def_id() == found.def_id() => + ValuePairs::TraitRefs(ExpectedFound { expected, found }) + if expected.def_id == found.def_id => { // It's possible that the placeholders come from a binder // outside of this value pair. Use `no_bound_vars` as a // simple heuristic for that. - (expected.no_bound_vars()?.args, found.no_bound_vars()?.args, expected.def_id()) + (expected.args, found.args, expected.def_id) } _ => return None, }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 503645191aacf..0bbabefaf9540 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -13,8 +13,8 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnosti use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node, - TyKind, + self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, + LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind, }; use rustc_middle::ty::{ self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, @@ -283,6 +283,7 @@ pub fn suggest_new_region_bound( continue; } match fn_return.kind { + // FIXME(precise_captures): Suggest adding to `use<...>` list instead. TyKind::OpaqueDef(item_id, _, _) => { let item = tcx.hir().item(item_id); let ItemKind::OpaqueTy(opaque) = &item.kind else { @@ -355,20 +356,8 @@ pub fn suggest_new_region_bound( // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any if let Some(id) = scope_def_id && let Some(generics) = tcx.hir().get_generics(id) - && let mut spans_suggs = generics - .params - .iter() - .filter(|p| p.is_elided_lifetime()) - .map(|p| { - if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { - // Ampersand (elided without '_) - (p.span.shrink_to_hi(), format!("{name} ")) - } else { - // Underscore (elided with '_) - (p.span, name.to_string()) - } - }) - .collect::>() + && let mut spans_suggs = + make_elided_region_spans_suggs(name, generics.params.iter()) && spans_suggs.len() > 1 { let use_lt = if existing_lt_name == None { @@ -430,6 +419,57 @@ pub fn suggest_new_region_bound( } } +fn make_elided_region_spans_suggs<'a>( + name: &str, + generic_params: impl Iterator>, +) -> Vec<(Span, String)> { + let mut spans_suggs = Vec::new(); + let mut bracket_span = None; + let mut consecutive_brackets = 0; + + let mut process_consecutive_brackets = + |span: Option, spans_suggs: &mut Vec<(Span, String)>| { + if span + .is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span)) + { + consecutive_brackets += 1; + } else if let Some(bracket_span) = bracket_span.take() { + let sugg = std::iter::once("<") + .chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", ")) + .chain([">"]) + .collect(); + spans_suggs.push((bracket_span.shrink_to_hi(), sugg)); + consecutive_brackets = 0; + } + bracket_span = span; + }; + + for p in generic_params { + if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind { + match kind { + MissingLifetimeKind::Underscore => { + process_consecutive_brackets(None, &mut spans_suggs); + spans_suggs.push((p.span, name.to_string())) + } + MissingLifetimeKind::Ampersand => { + process_consecutive_brackets(None, &mut spans_suggs); + spans_suggs.push((p.span.shrink_to_hi(), format!("{name} "))); + } + MissingLifetimeKind::Comma => { + process_consecutive_brackets(None, &mut spans_suggs); + spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, "))); + } + MissingLifetimeKind::Brackets => { + process_consecutive_brackets(Some(p.span), &mut spans_suggs); + } + } + } + } + process_consecutive_brackets(None, &mut spans_suggs); + + spans_suggs +} + impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub fn get_impl_ident_and_self_ty_from_trait( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 24eaff08220d6..6a42f9b42c384 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -28,7 +28,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { match err { ArgumentSorts(values, _) | Sorts(values) => { - match (values.expected.kind(), values.found.kind()) { + match (*values.expected.kind(), *values.found.kind()) { (ty::Closure(..), ty::Closure(..)) => { diag.note("no two closures, even if identical, have the same type"); diag.help("consider boxing your closure and/or using it as a trait object"); @@ -299,17 +299,19 @@ impl Trait for X { } (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() - && tcx.explicit_item_bounds(alias.def_id).skip_binder().iter().any( - |(pred, _span)| match pred.kind().skip_binder() { + && tcx + .explicit_item_super_predicates(alias.def_id) + .skip_binder() + .iter() + .any(|(pred, _span)| match pred.kind().skip_binder() { ty::ClauseKind::Trait(trait_predicate) if trait_predicate.polarity - == ty::ImplPolarity::Positive => + == ty::PredicatePolarity::Positive => { trait_predicate.def_id() == def_id } _ => false, - }, - ) => + }) => { diag.help(format!( "you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \ @@ -412,13 +414,13 @@ impl Trait for X { ty::Alias(..) => values.expected, _ => values.found, }; - let preds = tcx.explicit_item_bounds(opaque_ty.def_id); + let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id); for (pred, _span) in preds.skip_binder() { let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() else { continue; }; - if trait_predicate.polarity != ty::ImplPolarity::Positive { + if trait_predicate.polarity != ty::PredicatePolarity::Positive { continue; } let def_id = trait_predicate.def_id(); @@ -450,7 +452,7 @@ impl Trait for X { } (ty::FnPtr(sig), ty::FnDef(def_id, _)) | (ty::FnDef(def_id, _), ty::FnPtr(sig)) => { - if tcx.fn_sig(*def_id).skip_binder().unsafety() < sig.unsafety() { + if tcx.fn_sig(def_id).skip_binder().unsafety() < sig.unsafety() { diag.note( "unsafe functions cannot be coerced into safe function pointers", ); @@ -525,7 +527,7 @@ impl Trait for X { diag: &mut Diag<'_>, msg: impl Fn() -> String, body_owner_def_id: DefId, - proj_ty: &ty::AliasTy<'tcx>, + proj_ty: ty::AliasTy<'tcx>, ty: Ty<'tcx>, ) -> bool { let tcx = self.tcx; @@ -539,7 +541,7 @@ impl Trait for X { }; // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. // This will also work for `impl Trait`. - let ty::Param(param_ty) = proj_ty.self_ty().kind() else { + let ty::Param(param_ty) = *proj_ty.self_ty().kind() else { return false; }; let generics = tcx.generics_of(body_owner_def_id); @@ -596,7 +598,7 @@ impl Trait for X { fn expected_projection( &self, diag: &mut Diag<'_>, - proj_ty: &ty::AliasTy<'tcx>, + proj_ty: ty::AliasTy<'tcx>, values: ExpectedFound>, body_owner_def_id: DefId, cause_code: &ObligationCauseCode<'_>, @@ -707,7 +709,7 @@ fn foo(&self) -> Self::T { String::new() } &self, diag: &mut Diag<'_>, msg: impl Fn() -> String, - proj_ty: &ty::AliasTy<'tcx>, + proj_ty: ty::AliasTy<'tcx>, ty: Ty<'tcx>, ) -> bool { let tcx = self.tcx; diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 9081fbaa2dc2b..bf470bb1e3f89 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -2,7 +2,7 @@ use crate::infer::error_reporting::hir::Path; use core::ops::ControlFlow; use hir::def::CtorKind; use hir::intravisit::{walk_expr, walk_stmt, Visitor}; -use hir::{Local, QPath}; +use hir::{LetStmt, QPath}; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; @@ -15,13 +15,12 @@ use rustc_middle::traits::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, Span}; use crate::errors::{ ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, - FunctionPointerSuggestion, SuggestAccessingField, SuggestBoxingForReturnImplTrait, - SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, - TypeErrorAdditionalDiags, + FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, + SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, }; use super::TypeErrCtxt; @@ -80,28 +79,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - pub(super) fn suggest_boxing_for_return_impl_trait( - &self, - err: &mut Diag<'_>, - return_sp: Span, - arm_spans: impl Iterator, - ) { - let sugg = SuggestBoxingForReturnImplTrait::ChangeReturnType { - start_sp: return_sp.with_hi(return_sp.lo() + BytePos(4)), - end_sp: return_sp.shrink_to_hi(), - }; - err.subdiagnostic(self.dcx(), sugg); - - let mut starts = Vec::new(); - let mut ends = Vec::new(); - for span in arm_spans { - starts.push(span.shrink_to_lo()); - ends.push(span.shrink_to_hi()); - } - let sugg = SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends }; - err.subdiagnostic(self.dcx(), sugg); - } - pub(super) fn suggest_tuple_pattern( &self, cause: &ObligationCause<'tcx>, @@ -321,7 +298,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let Some(expr) = block.expr && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind && let Res::Local(local) = res - && let Node::Local(Local { init: Some(init), .. }) = self.tcx.parent_hir_node(*local) + && let Node::LetStmt(LetStmt { init: Some(init), .. }) = + self.tcx.parent_hir_node(*local) { fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) { match expr.kind { @@ -585,7 +563,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { - if let hir::StmtKind::Let(hir::Local { + if let hir::StmtKind::Let(LetStmt { span, pat: hir::Pat { .. }, ty: None, @@ -621,7 +599,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, span: Span, hir: hir::Node<'_>, - exp_found: &ty::error::ExpectedFound>, + exp_found: &ty::error::ExpectedFound>, diag: &mut Diag<'_>, ) { // 0. Extract fn_decl from hir @@ -636,10 +614,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // 1. Get the args of the closure. // 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1]. - let Some(expected) = exp_found.expected.skip_binder().args.get(1) else { + let Some(expected) = exp_found.expected.args.get(1) else { return; }; - let Some(found) = exp_found.found.skip_binder().args.get(1) else { + let Some(found) = exp_found.found.args.get(1) else { return; }; let expected = expected.unpack(); @@ -785,8 +763,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span); self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? } else { - last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1)) + self.tcx + .sess + .source_map() + .span_extend_while_whitespace(last_expr.span) + .shrink_to_hi() + .with_hi(last_stmt.span.hi()) }; + Some((span, needs_box)) } @@ -889,10 +873,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { format!(" {ident} ") }; let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); - ( - sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span), - sugg, - ) + (sm.span_extend_while_whitespace(left_span), sugg) }; Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident }) } diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index c39d0425f7e25..5ae7f8bf5048b 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -13,6 +13,7 @@ use rustc_data_structures::graph::implementation::{ Direction, Graph, NodeIndex, INCOMING, OUTGOING, }; use rustc_data_structures::intern::Interned; +use rustc_data_structures::unord::UnordSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -139,8 +140,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let mut var_data = self.construct_var_data(); // Deduplicating constraints is shown to have a positive perf impact. - self.data.constraints.sort_by_key(|(constraint, _)| *constraint); - self.data.constraints.dedup_by_key(|(constraint, _)| *constraint); + let mut seen = UnordSet::default(); + self.data.constraints.retain(|(constraint, _)| seen.insert(*constraint)); if cfg!(debug_assertions) { self.dump_constraints(); @@ -888,12 +889,14 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => { - let constraint_idx = - this.constraints.binary_search_by(|(c, _)| c.cmp(&edge.data)).unwrap(); - state.result.push(RegionAndOrigin { - region, - origin: this.constraints[constraint_idx].1.clone(), - }); + let origin = this + .constraints + .iter() + .find(|(c, _)| *c == edge.data) + .unwrap() + .1 + .clone(); + state.result.push(RegionAndOrigin { region, origin }); } Constraint::RegSubReg(..) => panic!( diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 89e4e88b3df24..bb53aec0b4d13 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -4,6 +4,7 @@ pub use lexical_region_resolve::RegionResolutionError; pub use relate::combine::CombineFields; pub use relate::combine::ObligationEmittingRelation; pub use relate::StructurallyRelateAliases; +pub use rustc_macros::{TypeFoldable, TypeVisitable}; pub use rustc_middle::ty::IntVarValue; pub use BoundRegionConversionTime::*; pub use RegionVariableOrigin::*; @@ -20,21 +21,21 @@ use opaque_types::OpaqueTypeStorage; use region_constraints::{GenericKind, VarInfos, VerifyBound}; use region_constraints::{RegionConstraintCollector, RegionConstraintStorage}; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; use rustc_data_structures::unify as ut; use rustc_errors::{Diag, DiagCtxt, ErrorGuaranteed}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_macros::extension; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::infer::unify_key::EffectVarValue; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ToType}; use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::traits::{select, DefiningAnchor}; +use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -48,7 +49,7 @@ use rustc_span::Span; use snapshot::undo_log::InferCtxtUndoLogs; use std::cell::{Cell, RefCell}; use std::fmt; -use type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use type_variable::TypeVariableOrigin; pub mod at; pub mod canonical; @@ -229,23 +230,22 @@ impl<'tcx> InferCtxtInner<'tcx> { .expect("region constraints already solved") .with_log(&mut self.undo_log) } + + // Iterates through the opaque type definitions without taking them; this holds the + // `InferCtxtInner` lock, so make sure to not do anything with `InferCtxt` side-effects + // while looping through this. + pub fn iter_opaque_types( + &self, + ) -> impl Iterator, ty::OpaqueHiddenType<'tcx>)> + '_ { + self.opaque_type_storage.opaque_types.iter().map(|(&k, v)| (k, v.hidden_type)) + } } pub struct InferCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, - /// The `DefId` of the item in whose context we are performing inference or typeck. - /// It is used to check whether an opaque type use is a defining use. - /// - /// If it is `DefiningAnchor::Bubble`, we can't resolve opaque types here and need to bubble up - /// the obligation. This frequently happens for - /// short lived InferCtxt within queries. The opaque type obligations are forwarded - /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. - /// - /// Its default value is `DefiningAnchor::Bind(&[])`, which means no opaque types may be defined. - /// This way it is easier to catch errors that - /// might come up during inference or typeck. - pub defining_use_anchor: DefiningAnchor<'tcx>, + /// The `DefIds` of the opaque types that may have their hidden types constrained. + defining_opaque_types: &'tcx ty::List, /// Whether this inference context should care about region obligations in /// the root universe. Most notably, this is used during hir typeck as region @@ -392,6 +392,10 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> { fn probe_ct_var(&self, vid: ConstVid) -> Option> { self.probe_const_var(vid).ok() } + + fn defining_opaque_types(&self) -> &'tcx ty::List { + self.defining_opaque_types + } } /// See the `error_reporting` module for more details. @@ -400,7 +404,7 @@ pub enum ValuePairs<'tcx> { Regions(ExpectedFound>), Terms(ExpectedFound>), Aliases(ExpectedFound>), - PolyTraitRefs(ExpectedFound>), + TraitRefs(ExpectedFound>), PolySigs(ExpectedFound>), ExistentialTraitRef(ExpectedFound>), ExistentialProjection(ExpectedFound>), @@ -474,8 +478,8 @@ pub enum SubregionOrigin<'tcx> { } // `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(SubregionOrigin<'_>, 32); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(SubregionOrigin<'_>, 32); impl<'tcx> SubregionOrigin<'tcx> { pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> { @@ -606,7 +610,7 @@ impl fmt::Display for FixupError { /// Used to configure inference contexts before their creation. pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, - defining_use_anchor: DefiningAnchor<'tcx>, + defining_opaque_types: &'tcx ty::List, considering_regions: bool, skip_leak_check: bool, /// Whether we are in coherence mode. @@ -621,7 +625,7 @@ impl<'tcx> TyCtxt<'tcx> { fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { InferCtxtBuilder { tcx: self, - defining_use_anchor: DefiningAnchor::Bind(ty::List::empty()), + defining_opaque_types: ty::List::empty(), considering_regions: true, skip_leak_check: false, intercrate: false, @@ -637,8 +641,16 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// It is only meant to be called in two places, for typeck /// (via `Inherited::build`) and for the inference context used /// in mir borrowck. - pub fn with_opaque_type_inference(mut self, defining_use_anchor: DefiningAnchor<'tcx>) -> Self { - self.defining_use_anchor = defining_use_anchor; + pub fn with_opaque_type_inference(mut self, defining_anchor: LocalDefId) -> Self { + self.defining_opaque_types = self.tcx.opaque_types_defined_by(defining_anchor); + self + } + + pub fn with_defining_opaque_types( + mut self, + defining_opaque_types: &'tcx ty::List, + ) -> Self { + self.defining_opaque_types = defining_opaque_types; self } @@ -670,22 +682,22 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// the bound values in `C` to their instantiated values in `V` /// (in other words, `S(C) = V`). pub fn build_with_canonical( - &mut self, + self, span: Span, canonical: &Canonical<'tcx, T>, ) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>) where T: TypeFoldable>, { - let infcx = self.build(); - let (value, args) = infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); + let infcx = self.with_defining_opaque_types(canonical.defining_opaque_types).build(); + let (value, args) = infcx.instantiate_canonical(span, canonical); (infcx, value, args) } pub fn build(&mut self) -> InferCtxt<'tcx> { let InferCtxtBuilder { tcx, - defining_use_anchor, + defining_opaque_types, considering_regions, skip_leak_check, intercrate, @@ -693,7 +705,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { } = *self; InferCtxt { tcx, - defining_use_anchor, + defining_opaque_types, considering_regions, skip_leak_check, inner: RefCell::new(InferCtxtInner::new()), @@ -834,7 +846,9 @@ impl<'tcx> InferCtxt<'tcx> { { let origin = &ObligationCause::dummy(); self.probe(|_| { - self.at(origin, param_env).sub(DefineOpaqueTypes::No, expected, actual).is_ok() + // We're only answering whether there could be a subtyping relation, and with + // opaque types, "there could be one", via registering a hidden type. + self.at(origin, param_env).sub(DefineOpaqueTypes::Yes, expected, actual).is_ok() }) } @@ -843,7 +857,9 @@ impl<'tcx> InferCtxt<'tcx> { T: at::ToTrace<'tcx>, { let origin = &ObligationCause::dummy(); - self.probe(|_| self.at(origin, param_env).eq(DefineOpaqueTypes::No, a, b).is_ok()) + // We're only answering whether the types could be the same, and with + // opaque types, "they can be the same", via registering a hidden type. + self.probe(|_| self.at(origin, param_env).eq(DefineOpaqueTypes::Yes, a, b).is_ok()) } #[instrument(skip(self), level = "debug")] @@ -930,14 +946,27 @@ impl<'tcx> InferCtxt<'tcx> { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { return Err((a_vid, b_vid)); } + // We don't silently want to constrain hidden types here, so we assert that either one side is + // an infer var, so it'll get constrained to whatever the other side is, or there are no opaque + // types involved. + // We don't expect this to actually get hit, but if it does, we now at least know how to write + // a test for it. + (_, ty::Infer(ty::TyVar(_))) => {} + (ty::Infer(ty::TyVar(_)), _) => {} + _ if r_a != r_b && (r_a, r_b).has_opaque_types() => { + span_bug!( + cause.span(), + "opaque types got hidden types registered from within subtype predicate: {r_a:?} vs {r_b:?}" + ) + } _ => {} } self.enter_forall(predicate, |ty::SubtypePredicate { a_is_expected, a, b }| { if a_is_expected { - Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::No, a, b)) + Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b)) } else { - Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::No, b, a)) + Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a)) } }) } @@ -1096,13 +1125,7 @@ impl<'tcx> InferCtxt<'tcx> { // as the generic parameters for the default, `(T, U)`. let ty_var_id = self.inner.borrow_mut().type_variables().new_var( self.universe(), - TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeParameterDefinition( - param.name, - param.def_id, - ), - span, - }, + TypeVariableOrigin { param_def_id: Some(param.def_id), span }, ); Ty::new_var(self.tcx, ty_var_id).into() @@ -1111,13 +1134,7 @@ impl<'tcx> InferCtxt<'tcx> { if is_host_effect { return self.var_for_effect(param); } - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::ConstParameterDefinition( - param.name, - param.def_id, - ), - span, - }; + let origin = ConstVariableOrigin { param_def_id: Some(param.def_id), span }; let const_var_id = self .inner .borrow_mut() @@ -1217,6 +1234,12 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow().opaque_type_storage.opaque_types.clone() } + #[inline(always)] + pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { + let Some(id) = id.into().as_local() else { return false }; + self.defining_opaque_types.contains(&id) + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_vars_if_possible(t).to_string() } @@ -1232,19 +1255,76 @@ impl<'tcx> InferCtxt<'tcx> { } } - /// Resolve any type variables found in `value` -- but only one - /// level. So, if the variable `?X` is bound to some type - /// `Foo`, then this would return `Foo` (but `?Y` may - /// itself be bound to a type). - /// - /// Useful when you only need to inspect the outermost level of - /// the type and don't care about nested types (or perhaps you - /// will be resolving them as well, e.g. in a loop). - pub fn shallow_resolve(&self, value: T) -> T - where - T: TypeFoldable>, - { - value.fold_with(&mut ShallowResolver { infcx: self }) + pub fn shallow_resolve(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Infer(v) = ty.kind() { self.fold_infer_ty(*v).unwrap_or(ty) } else { ty } + } + + // This is separate from `shallow_resolve` to keep that method small and inlinable. + #[inline(never)] + fn fold_infer_ty(&self, v: InferTy) -> Option> { + match v { + ty::TyVar(v) => { + // Not entirely obvious: if `typ` is a type variable, + // it can be resolved to an int/float variable, which + // can then be recursively resolved, hence the + // recursion. Note though that we prevent type + // variables from unifying to other type variables + // directly (though they may be embedded + // structurally), and we prevent cycles in any case, + // so this recursion should always be of very limited + // depth. + // + // Note: if these two lines are combined into one we get + // dynamic borrow errors on `self.inner`. + let known = self.inner.borrow_mut().type_variables().probe(v).known(); + known.map(|t| self.shallow_resolve(t)) + } + + ty::IntVar(v) => self + .inner + .borrow_mut() + .int_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.tcx)), + + ty::FloatVar(v) => self + .inner + .borrow_mut() + .float_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.tcx)), + + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => None, + } + } + + pub fn shallow_resolve_const(&self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.kind() { + ty::ConstKind::Infer(infer_ct) => match infer_ct { + InferConst::Var(vid) => self + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .known() + .unwrap_or(ct), + InferConst::EffectVar(vid) => self + .inner + .borrow_mut() + .effect_unification_table() + .probe_value(vid) + .known() + .unwrap_or(ct), + InferConst::Fresh(_) => ct, + }, + ty::ConstKind::Param(_) + | ty::ConstKind::Bound(_, _) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Value(_) + | ty::ConstKind::Error(_) + | ty::ConstKind::Expr(_) => ct, + } } pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid { @@ -1390,10 +1470,7 @@ impl<'tcx> InferCtxt<'tcx> { .entry(bt.var) .or_insert_with(|| { self.infcx - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.span, - }) + .next_ty_var(TypeVariableOrigin { param_def_id: None, span: self.span }) .into() }) .expect_ty() @@ -1405,10 +1482,7 @@ impl<'tcx> InferCtxt<'tcx> { self.infcx .next_const_var( ty, - ConstVariableOrigin { - kind: ConstVariableOriginKind::MiscVariable, - span: self.span, - }, + ConstVariableOrigin { param_def_id: None, span: self.span }, ) .into() }) @@ -1448,15 +1522,6 @@ impl<'tcx> InferCtxt<'tcx> { closure_kind_ty.to_opt_closure_kind() } - /// Clears the selection, evaluation, and projection caches. This is useful when - /// repeatedly attempting to select an `Obligation` while changing only - /// its `ParamEnv`, since `FulfillmentContext` doesn't use probing. - pub fn clear_caches(&self) { - self.selection_cache.clear(); - self.evaluation_cache.clear(); - self.inner.borrow_mut().projection_cache().clear(); - } - pub fn universe(&self) -> ty::UniverseIndex { self.universe.get() } @@ -1475,7 +1540,7 @@ impl<'tcx> InferCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, unevaluated: ty::UnevaluatedConst<'tcx>, ty: Ty<'tcx>, - span: Option, + span: Span, ) -> Result, ErrorHandled> { match self.const_eval_resolve(param_env, unevaluated, span) { Ok(Some(val)) => Ok(ty::Const::new_value(self.tcx, val, ty)), @@ -1509,7 +1574,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, mut param_env: ty::ParamEnv<'tcx>, unevaluated: ty::UnevaluatedConst<'tcx>, - span: Option, + span: Span, ) -> EvalToValTreeResult<'tcx> { let mut args = self.resolve_vars_if_possible(unevaluated.args); debug!(?args); @@ -1521,12 +1586,9 @@ impl<'tcx> InferCtxt<'tcx> { if let Some(ct) = tcx.thir_abstract_const(unevaluated.def)? { let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, args)); if let Err(e) = ct.error_reported() { - return Err(ErrorHandled::Reported( - e.into(), - span.unwrap_or(rustc_span::DUMMY_SP), - )); + return Err(ErrorHandled::Reported(e.into(), span)); } else if ct.has_non_region_infer() || ct.has_non_region_param() { - return Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))); + return Err(ErrorHandled::TooGeneric(span)); } else { args = replace_param_and_infer_args_with_placeholder(tcx, args); } @@ -1764,89 +1826,6 @@ impl<'tcx> TypeFolder> for InferenceLiteralEraser<'tcx> { } } -struct ShallowResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, -} - -impl<'a, 'tcx> TypeFolder> for ShallowResolver<'a, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - /// If `ty` is a type variable of some kind, resolve it one level - /// (but do not resolve types found in the result). If `typ` is - /// not a type variable, just return it unmodified. - #[inline] - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Infer(v) = ty.kind() { self.fold_infer_ty(*v).unwrap_or(ty) } else { ty } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match ct.kind() { - ty::ConstKind::Infer(InferConst::Var(vid)) => self - .infcx - .inner - .borrow_mut() - .const_unification_table() - .probe_value(vid) - .known() - .unwrap_or(ct), - ty::ConstKind::Infer(InferConst::EffectVar(vid)) => self - .infcx - .inner - .borrow_mut() - .effect_unification_table() - .probe_value(vid) - .known() - .unwrap_or(ct), - _ => ct, - } - } -} - -impl<'a, 'tcx> ShallowResolver<'a, 'tcx> { - // This is separate from `fold_ty` to keep that method small and inlinable. - #[inline(never)] - fn fold_infer_ty(&mut self, v: InferTy) -> Option> { - match v { - ty::TyVar(v) => { - // Not entirely obvious: if `typ` is a type variable, - // it can be resolved to an int/float variable, which - // can then be recursively resolved, hence the - // recursion. Note though that we prevent type - // variables from unifying to other type variables - // directly (though they may be embedded - // structurally), and we prevent cycles in any case, - // so this recursion should always be of very limited - // depth. - // - // Note: if these two lines are combined into one we get - // dynamic borrow errors on `self.inner`. - let known = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); - known.map(|t| self.fold_ty(t)) - } - - ty::IntVar(v) => self - .infcx - .inner - .borrow_mut() - .int_unification_table() - .probe_value(v) - .map(|v| v.to_type(self.infcx.tcx)), - - ty::FloatVar(v) => self - .infcx - .inner - .borrow_mut() - .float_unification_table() - .probe_value(v) - .map(|v| v.to_type(self.infcx.tcx)), - - ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => None, - } - } -} - impl<'tcx> TypeTrace<'tcx> { pub fn span(&self) -> Span { self.cause.span @@ -1864,15 +1843,15 @@ impl<'tcx> TypeTrace<'tcx> { } } - pub fn poly_trait_refs( + pub fn trait_refs( cause: &ObligationCause<'tcx>, a_is_expected: bool, - a: ty::PolyTraitRef<'tcx>, - b: ty::PolyTraitRef<'tcx>, + a: ty::TraitRef<'tcx>, + b: ty::TraitRef<'tcx>, ) -> TypeTrace<'tcx> { TypeTrace { cause: cause.clone(), - values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b)), + values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)), } } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index a6f8115c27e2d..94a546f87eee9 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -1,14 +1,13 @@ -use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use super::type_variable::TypeVariableOrigin; use super::{DefineOpaqueTypes, InferResult}; use crate::errors::OpaqueHiddenTypeDiag; use crate::infer::{InferCtxt, InferOk}; use crate::traits::{self, PredicateObligation}; use hir::def_id::{DefId, LocalDefId}; -use hir::OpaqueTyOrigin; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; -use rustc_middle::traits::{DefiningAnchor, ObligationCause}; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::GenericArgKind; @@ -54,28 +53,19 @@ impl<'tcx> InferCtxt<'tcx> { } let mut obligations = vec![]; - let replace_opaque_type = |def_id: DefId| { - def_id.as_local().is_some_and(|def_id| self.opaque_type_origin(def_id).is_some()) - }; let value = value.fold_with(&mut BottomUpFolder { tcx: self.tcx, lt_op: |lt| lt, ct_op: |ct| ct, ty_op: |ty| match *ty.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) - if replace_opaque_type(def_id) && !ty.has_escaping_bound_vars() => + if self.can_define_opaque_ty(def_id) && !ty.has_escaping_bound_vars() => { let def_span = self.tcx.def_span(def_id); let span = if span.contains(def_span) { def_span } else { span }; let code = traits::ObligationCauseCode::OpaqueReturnType(None); let cause = ObligationCause::new(span, body_id, code); - // FIXME(compiler-errors): We probably should add a new TypeVariableOriginKind - // for opaque types, and then use that kind to fix the spans for type errors - // that we see later on. - let ty_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::OpaqueTypeInference(def_id), - span, - }); + let ty_var = self.next_ty_var(TypeVariableOrigin { param_def_id: None, span }); obligations.extend( self.handle_opaque_type(ty, ty_var, &cause, param_env).unwrap().obligations, ); @@ -94,9 +84,6 @@ impl<'tcx> InferCtxt<'tcx> { cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> InferResult<'tcx, ()> { - if a.references_error() || b.references_error() { - return Ok(InferOk { value: (), obligations: vec![] }); - } let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) if def_id.is_local() => { let def_id = def_id.expect_local(); @@ -109,56 +96,52 @@ impl<'tcx> InferCtxt<'tcx> { b, )); } - match self.defining_use_anchor { - DefiningAnchor::Bind(_) => { - // Check that this is `impl Trait` type is - // declared by `parent_def_id` -- i.e., one whose - // value we are inferring. At present, this is - // always true during the first phase of - // type-check, but not always true later on during - // NLL. Once we support named opaque types more fully, - // this same scenario will be able to arise during all phases. - // - // Here is an example using type alias `impl Trait` - // that indicates the distinction we are checking for: - // - // ```rust - // mod a { - // pub type Foo = impl Iterator; - // pub fn make_foo() -> Foo { .. } - // } - // - // mod b { - // fn foo() -> a::Foo { a::make_foo() } - // } - // ``` - // - // Here, the return type of `foo` references an - // `Opaque` indeed, but not one whose value is - // presently being inferred. You can get into a - // similar situation with closure return types - // today: - // - // ```rust - // fn foo() -> impl Iterator { .. } - // fn bar() { - // let x = || foo(); // returns the Opaque assoc with `foo` - // } - // ``` - if self.opaque_type_origin(def_id).is_none() { - return None; - } - } - DefiningAnchor::Bubble => {} + // Check that this is `impl Trait` type is + // declared by `parent_def_id` -- i.e., one whose + // value we are inferring. At present, this is + // always true during the first phase of + // type-check, but not always true later on during + // NLL. Once we support named opaque types more fully, + // this same scenario will be able to arise during all phases. + // + // Here is an example using type alias `impl Trait` + // that indicates the distinction we are checking for: + // + // ```rust + // mod a { + // pub type Foo = impl Iterator; + // pub fn make_foo() -> Foo { .. } + // } + // + // mod b { + // fn foo() -> a::Foo { a::make_foo() } + // } + // ``` + // + // Here, the return type of `foo` references an + // `Opaque` indeed, but not one whose value is + // presently being inferred. You can get into a + // similar situation with closure return types + // today: + // + // ```rust + // fn foo() -> impl Iterator { .. } + // fn bar() { + // let x = || foo(); // returns the Opaque assoc with `foo` + // } + // ``` + if !self.can_define_opaque_ty(def_id) { + return None; } + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() { // We could accept this, but there are various ways to handle this situation, and we don't // want to make a decision on it right now. Likely this case is so super rare anyway, that // no one encounters it in practice. // It does occur however in `fn fut() -> impl Future { async { 42 } }`, // where it is of no concern, so we only check for TAITs. - if let Some(OpaqueTyOrigin::TyAlias { .. }) = - b_def_id.as_local().and_then(|b_def_id| self.opaque_type_origin(b_def_id)) + if self.can_define_opaque_ty(b_def_id) + && self.tcx.is_type_alias_impl_trait(b_def_id) { self.tcx.dcx().emit_err(OpaqueHiddenTypeDiag { span: cause.span, @@ -370,20 +353,6 @@ impl<'tcx> InferCtxt<'tcx> { op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions), }); } - - /// Returns the origin of the opaque type `def_id` if we're currently - /// in its defining scope. - #[instrument(skip(self), level = "trace", ret)] - pub fn opaque_type_origin(&self, def_id: LocalDefId) -> Option { - let defined_opaque_types = match self.defining_use_anchor { - DefiningAnchor::Bubble => return None, - DefiningAnchor::Bind(bind) => bind, - }; - - let origin = self.tcx.opaque_type_origin(def_id); - - defined_opaque_types.contains(&def_id).then_some(origin) - } } /// Visitor that requires that (almost) all regions in the type visited outlive diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs index 7dd1ec3254220..6bab3ad6ba3c8 100644 --- a/compiler/rustc_infer/src/infer/outlives/components.rs +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -93,6 +93,7 @@ fn compute_components<'tcx>( } } + ty::Pat(element, _) | ty::Array(element, _) => { // Don't look into the len const as it doesn't affect regions compute_components(tcx, element, out, visited); diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 3ef37bf3466f1..bd981c2056770 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -300,7 +300,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { alias_ty: ty::AliasTy<'tcx>, ) -> impl Iterator> { let tcx = self.tcx; - let bounds = tcx.item_bounds(alias_ty.def_id); + let bounds = tcx.item_super_predicates(alias_ty.def_id); trace!("{:#?}", bounds.skip_binder()); bounds .iter_instantiated(tcx, alias_ty.args) diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index 38e74e538687e..e60efe37fd9bc 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::{self, Ty}; use crate::traits::{Obligation, PredicateObligation}; -use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use super::type_variable::TypeVariableOrigin; use super::InferCtxt; impl<'tcx> InferCtxt<'tcx> { @@ -24,7 +24,7 @@ impl<'tcx> InferCtxt<'tcx> { debug_assert!(!self.next_trait_solver()); let def_id = projection_ty.def_id; let ty_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, + param_def_id: None, span: self.tcx.def_span(def_id), }); let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Projection( diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index 06f8dd4a4c6cd..6e8efa3e7c182 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -382,7 +382,7 @@ impl<'tcx> MiniGraph<'tcx> { edges.push((source_node, target_node)); }, ); - let graph = VecGraph::new(nodes.len(), edges); + let graph = VecGraph::<_, false>::new(nodes.len(), edges); let sccs = Sccs::new(&graph); Self { nodes, sccs } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 0f3f2bc5fa634..223e6e3d34416 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -11,6 +11,7 @@ use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; use rustc_index::IndexVec; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::infer::unify_key::{RegionVariableValue, RegionVidKey}; use rustc_middle::ty::ReStatic; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -104,7 +105,7 @@ pub struct RegionConstraintData<'tcx> { } /// Represents a constraint that influences the inference process. -#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum Constraint<'tcx> { /// A region variable is a subregion of another. VarSubVar(RegionVid, RegionVid), diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs index 28b7db275a307..8a3125f9dedf8 100644 --- a/compiler/rustc_infer/src/infer/relate/combine.rs +++ b/compiler/rustc_infer/src/infer/relate/combine.rs @@ -155,8 +155,8 @@ impl<'tcx> InferCtxt<'tcx> { return Ok(a); } - let a = self.shallow_resolve(a); - let b = self.shallow_resolve(b); + let a = self.shallow_resolve_const(a); + let b = self.shallow_resolve_const(b); // We should never have to relate the `ty` field on `Const` as it is checked elsewhere that consts have the // correct type for the generic param they are an argument for. However there have been a number of cases diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 5fb9d9341e033..74929daffe247 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -1,7 +1,7 @@ use std::mem; use super::StructurallyRelateAliases; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue}; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableValue}; use crate::infer::{InferCtxt, ObligationEmittingRelation, RegionVariableOrigin}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -352,7 +352,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { ) -> Result, TypeError<'tcx>> { if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() { return Ok(self.infcx.next_ty_var_in_universe( - TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span: self.span }, + TypeVariableOrigin { param_def_id: None, span: self.span }, self.for_universe, )); } @@ -375,10 +375,7 @@ impl<'tcx> Generalizer<'_, 'tcx> { debug!("generalization failure in alias"); Ok(self.infcx.next_ty_var_in_universe( - TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.span, - }, + TypeVariableOrigin { param_def_id: None, span: self.span }, self.for_universe, )) } diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index 747158585db75..f9470c9b8f676 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -18,7 +18,7 @@ //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) use super::combine::ObligationEmittingRelation; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::type_variable::TypeVariableOrigin; use crate::infer::{DefineOpaqueTypes, InferCtxt}; use crate::traits::ObligationCause; @@ -88,18 +88,14 @@ where // iterate on the subtype obligations that are returned, but I // think this suffices. -nmatsakis (&ty::Infer(TyVar(..)), _) => { - let v = infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::LatticeVariable, - span: this.cause().span, - }); + let v = infcx + .next_ty_var(TypeVariableOrigin { param_def_id: None, span: this.cause().span }); this.relate_bound(v, b, a)?; Ok(v) } (_, &ty::Infer(TyVar(..))) => { - let v = infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::LatticeVariable, - span: this.cause().span, - }); + let v = infcx + .next_ty_var(TypeVariableOrigin { param_def_id: None, span: this.cause().span }); this.relate_bound(v, a, b)?; Ok(v) } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index d5999331dfab6..758aac004dcfd 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -12,21 +12,19 @@ use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; /// useful for printing messages etc but also required at various /// points for correctness. pub struct OpportunisticVarResolver<'a, 'tcx> { - // The shallow resolver is used to resolve inference variables at every - // level of the type. - shallow_resolver: crate::infer::ShallowResolver<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, } impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { #[inline] pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - OpportunisticVarResolver { shallow_resolver: crate::infer::ShallowResolver { infcx } } + OpportunisticVarResolver { infcx } } } impl<'a, 'tcx> TypeFolder> for OpportunisticVarResolver<'a, 'tcx> { fn interner(&self) -> TyCtxt<'tcx> { - TypeFolder::interner(&self.shallow_resolver) + self.infcx.tcx } #[inline] @@ -34,7 +32,7 @@ impl<'a, 'tcx> TypeFolder> for OpportunisticVarResolver<'a, 'tcx> { if !t.has_non_region_infer() { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { - let t = self.shallow_resolver.fold_ty(t); + let t = self.infcx.shallow_resolve(t); t.super_fold_with(self) } } @@ -43,7 +41,7 @@ impl<'a, 'tcx> TypeFolder> for OpportunisticVarResolver<'a, 'tcx> { if !ct.has_non_region_infer() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { - let ct = self.shallow_resolver.fold_const(ct); + let ct = self.infcx.shallow_resolve_const(ct); ct.super_fold_with(self) } } @@ -160,7 +158,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for FullTypeResolver<'a, 'tcx> { if !c.has_infer() { Ok(c) // micro-optimize -- if there is nothing in this const that this fold affects... } else { - let c = self.infcx.shallow_resolve(c); + let c = self.infcx.shallow_resolve_const(c); match c.kind() { ty::ConstKind::Infer(InferConst::Var(vid)) => { return Err(FixupError::UnresolvedConst(vid)); diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 14de461cd17eb..83667f7276d48 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -1,4 +1,4 @@ -use rustc_middle::infer::unify_key::{ConstVariableOriginKind, ConstVariableValue, ConstVidKey}; +use rustc_middle::infer::unify_key::{ConstVariableValue, ConstVidKey}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; @@ -13,7 +13,7 @@ use ut::UnifyKey; use std::ops::Range; fn vars_since_snapshot<'tcx, T>( - table: &mut UnificationTable<'_, 'tcx, T>, + table: &UnificationTable<'_, 'tcx, T>, snapshot_var_len: usize, ) -> Range where @@ -33,10 +33,9 @@ fn const_vars_since_snapshot<'tcx>( range.start.vid..range.end.vid, (range.start.index()..range.end.index()) .map(|index| match table.probe_value(ConstVid::from_u32(index)) { - ConstVariableValue::Known { value: _ } => ConstVariableOrigin { - kind: ConstVariableOriginKind::MiscVariable, - span: rustc_span::DUMMY_SP, - }, + ConstVariableValue::Known { value: _ } => { + ConstVariableOrigin { param_def_id: None, span: rustc_span::DUMMY_SP } + } ConstVariableValue::Unknown { origin, universe: _ } => origin, }) .collect(), @@ -124,11 +123,11 @@ impl<'tcx> InferCtxt<'tcx> { let type_vars = inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); let int_vars = vars_since_snapshot( - &mut inner.int_unification_table(), + &inner.int_unification_table(), variable_lengths.int_var_len, ); let float_vars = vars_since_snapshot( - &mut inner.float_unification_table(), + &inner.float_unification_table(), variable_lengths.float_var_len, ); let region_vars = inner diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 3630b0f439ff4..96afa257ebbad 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -2,7 +2,6 @@ use rustc_data_structures::undo_log::Rollback; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_middle::ty::{self, Ty, TyVid}; -use rustc_span::symbol::Symbol; use rustc_span::Span; use crate::infer::InferCtxtUndoLogs; @@ -37,31 +36,11 @@ pub struct TypeVariableTable<'a, 'tcx> { #[derive(Copy, Clone, Debug)] pub struct TypeVariableOrigin { - pub kind: TypeVariableOriginKind, pub span: Span, -} - -/// Reasons to create a type inference variable -#[derive(Copy, Clone, Debug)] -pub enum TypeVariableOriginKind { - MiscVariable, - NormalizeProjectionType, - TypeInference, - OpaqueTypeInference(DefId), - TypeParameterDefinition(Symbol, DefId), - - /// One of the upvars or closure kind parameters in a `ClosureArgs` - /// (before it has been determined). - // FIXME(eddyb) distinguish upvar inference variables from the rest. - ClosureSynthetic, - AutoDeref, - AdjustmentType, - - /// In type check, when we are type checking a function that - /// returns `-> dyn Foo`, we instantiate a type variable with the - /// return type for diagnostic purposes. - DynReturnFn, - LatticeVariable, + /// `DefId` of the type parameter this was instantiated for, if any. + /// + /// This should only be used for diagnostics. + pub param_def_id: Option, } #[derive(Clone)] diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 029bddda1e1c5..0299af61d45ca 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -18,22 +18,17 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extend_one)] #![feature(let_chains)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(iterator_try_collect)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![feature(try_blocks)] +#![feature(yeet_expr)] #![recursion_limit = "512"] // For rustdoc -#[macro_use] -extern crate rustc_macros; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -#[macro_use] -extern crate rustc_data_structures; #[macro_use] extern crate tracing; #[macro_use] diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index c495810858f7f..cb067c7a6600d 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -1,6 +1,7 @@ use crate::infer::InferCtxt; use crate::traits::Obligation; use rustc_hir::def_id::DefId; +use rustc_macros::extension; use rustc_middle::ty::{self, ToPredicate, Ty}; use super::FulfillmentError; diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 4808a1defdd8f..e3611ab28e532 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -112,8 +112,8 @@ impl<'tcx> PolyTraitObligation<'tcx> { } // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(PredicateObligation<'_>, 48); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(PredicateObligation<'_>, 48); pub type PredicateObligations<'tcx> = Vec>; @@ -209,7 +209,7 @@ impl<'tcx> FulfillmentError<'tcx> { } impl<'tcx> PolyTraitObligation<'tcx> { - pub fn polarity(&self) -> ty::ImplPolarity { + pub fn polarity(&self) -> ty::PredicatePolarity { self.predicate.skip_binder().polarity } diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index 31ceb2343324d..c6ffba59638e3 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -78,11 +78,12 @@ pub struct ProjectionCacheStorage<'tcx> { #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct ProjectionCacheKey<'tcx> { ty: ty::AliasTy<'tcx>, + param_env: ty::ParamEnv<'tcx>, } impl<'tcx> ProjectionCacheKey<'tcx> { - pub fn new(ty: ty::AliasTy<'tcx>) -> Self { - Self { ty } + pub fn new(ty: ty::AliasTy<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { + Self { ty, param_env } } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index e9df0505cbbdc..6d43011d33cad 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -270,7 +270,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { match bound_clause.skip_binder() { ty::ClauseKind::Trait(data) => { // Negative trait bounds do not imply any supertrait bounds - if data.polarity == ty::ImplPolarity::Negative { + if data.polarity != ty::PredicatePolarity::Positive { return; } // Get predicates implied by the trait, or only super predicates if we only care about self predicates. diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 1a82e6c6910ae..c5b81dbd67919 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -5,12 +5,15 @@ use rustc_ast::{LitKind, MetaItemKind}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::defer; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::Lrc; use rustc_errors::registry::Registry; use rustc_errors::{DiagCtxt, ErrorGuaranteed}; use rustc_lint::LintStore; + use rustc_middle::ty; +use rustc_middle::ty::CurrentGcx; use rustc_middle::util::Providers; use rustc_parse::maybe_new_parser_from_source_str; use rustc_query_impl::QueryCtxt; @@ -19,7 +22,7 @@ use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileN use rustc_session::filesearch::{self, sysroot_candidates}; use rustc_session::parse::ParseSess; use rustc_session::{lint, CompilerIO, EarlyDiagCtxt, Session}; -use rustc_span::source_map::FileLoader; +use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs}; use rustc_span::symbol::sym; use rustc_span::FileName; use std::path::PathBuf; @@ -39,6 +42,7 @@ pub struct Compiler { pub sess: Session, pub codegen_backend: Box, pub(crate) override_queries: Option, + pub(crate) current_gcx: CurrentGcx, } /// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`. @@ -48,6 +52,7 @@ pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec) -> Cfg { let psess = ParseSess::with_silent_emitter( vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this error occurred on the command line: `--cfg={s}`"), + true, ); let filename = FileName::cfg_spec_source_code(&s); @@ -111,6 +116,7 @@ pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec) -> CheckCfg { let psess = ParseSess::with_silent_emitter( vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this error occurred on the command line: `--check-cfg={s}`"), + true, ); let filename = FileName::cfg_spec_source_code(&s); @@ -318,6 +324,18 @@ pub struct Config { pub expanded_args: Vec, } +/// Initialize jobserver before getting `jobserver::client` and `build_session`. +pub(crate) fn initialize_checked_jobserver(early_dcx: &EarlyDiagCtxt) { + jobserver::initialize_checked(|err| { + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + early_dcx + .early_struct_warn(err) + .with_note("the build environment is likely misconfigured") + .emit() + }); +} + // JUSTIFICATION: before session exists, only config #[allow(rustc::bad_opt_access)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable @@ -329,63 +347,39 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se // Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); - early_dcx.initialize_checked_jobserver(); + initialize_checked_jobserver(&early_dcx); + + crate::callbacks::setup_callbacks(); + + let sysroot = filesearch::materialize_sysroot(config.opts.maybe_sysroot.clone()); + let target = config::build_target_config(&early_dcx, &config.opts, &sysroot); + let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); + let path_mapping = config.opts.file_path_mapping(); + let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target); util::run_in_thread_pool_with_globals( config.opts.edition, config.opts.unstable_opts.threads, - || { - crate::callbacks::setup_callbacks(); - + SourceMapInputs { file_loader, path_mapping, hash_kind }, + |current_gcx| { + // The previous `early_dcx` can't be reused here because it doesn't + // impl `Send`. Creating a new one is fine. let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); - let sysroot = filesearch::materialize_sysroot(config.opts.maybe_sysroot.clone()); - - let (codegen_backend, target_override) = match config.make_codegen_backend { - None => { - // Build a target without override, so that it can override the backend if needed - let target = - config::build_target_config(&early_dcx, &config.opts, None, &sysroot); - - let backend = util::get_codegen_backend( - &early_dcx, - &sysroot, - config.opts.unstable_opts.codegen_backend.as_deref(), - &target, - ); - - // target_override is documented to be called before init(), so this is okay - let target_override = backend.target_override(&config.opts); - - // Assert that we don't use target's override of the backend and - // backend's override of the target at the same time - if config.opts.unstable_opts.codegen_backend.is_none() - && target.default_codegen_backend.is_some() - && target_override.is_some() - { - rustc_middle::bug!( - "Codegen backend requested target override even though the target requested the backend" - ); - } - - (backend, target_override) - } + let codegen_backend = match config.make_codegen_backend { + None => util::get_codegen_backend( + &early_dcx, + &sysroot, + config.opts.unstable_opts.codegen_backend.as_deref(), + &target, + ), Some(make_codegen_backend) => { - // N.B. `make_codegen_backend` takes precedence over `target.default_codegen_backend`, - // which is ignored in this case. - let backend = make_codegen_backend(&config.opts); - - // target_override is documented to be called before init(), so this is okay - let target_override = backend.target_override(&config.opts); - - (backend, target_override) + // N.B. `make_codegen_backend` takes precedence over + // `target.default_codegen_backend`, which is ignored in this case. + make_codegen_backend(&config.opts) } }; - // Re-build target with the (potential) override - let target_cfg = - config::build_target_config(&early_dcx, &config.opts, target_override, &sysroot); - let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); let bundle = match rustc_errors::fluent_bundle( @@ -396,9 +390,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.opts.unstable_opts.translate_directionality_markers, ) { Ok(bundle) => bundle, - Err(e) => { - early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")); - } + Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")), }; let mut locale_resources = Vec::from(config.locale_resources); @@ -417,8 +409,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.registry.clone(), locale_resources, config.lint_caps, - config.file_loader, - target_cfg, + target, sysroot, util::rustc_version_str().unwrap_or("unknown"), config.ice_file, @@ -457,48 +448,50 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se } sess.lint_store = Some(Lrc::new(lint_store)); - let compiler = - Compiler { sess, codegen_backend, override_queries: config.override_queries }; - - rustc_span::set_source_map(compiler.sess.psess.clone_source_map(), move || { - // There are two paths out of `f`. - // - Normal exit. - // - Panic, e.g. triggered by `abort_if_errors`. - // - // We must run `finish_diagnostics` in both cases. - let res = { - // If `f` panics, `finish_diagnostics` will run during - // unwinding because of the `defer`. - let mut guar = None; - let sess_abort_guard = defer(|| { - guar = compiler.sess.finish_diagnostics(&config.registry); - }); + let compiler = Compiler { + sess, + codegen_backend, + override_queries: config.override_queries, + current_gcx, + }; - let res = f(&compiler); - - // If `f` doesn't panic, `finish_diagnostics` will run - // normally when `sess_abort_guard` is dropped. - drop(sess_abort_guard); - - // If `finish_diagnostics` emits errors (e.g. stashed - // errors) we can't return an error directly, because the - // return type of this function is `R`, not `Result`. - // But we need to communicate the errors' existence to the - // caller, otherwise the caller might mistakenly think that - // no errors occurred and return a zero exit code. So we - // abort (panic) instead, similar to if `f` had panicked. - if guar.is_some() { - compiler.sess.dcx().abort_if_errors(); - } + // There are two paths out of `f`. + // - Normal exit. + // - Panic, e.g. triggered by `abort_if_errors`. + // + // We must run `finish_diagnostics` in both cases. + let res = { + // If `f` panics, `finish_diagnostics` will run during + // unwinding because of the `defer`. + let mut guar = None; + let sess_abort_guard = defer(|| { + guar = compiler.sess.finish_diagnostics(&config.registry); + }); + + let res = f(&compiler); + + // If `f` doesn't panic, `finish_diagnostics` will run + // normally when `sess_abort_guard` is dropped. + drop(sess_abort_guard); + + // If `finish_diagnostics` emits errors (e.g. stashed + // errors) we can't return an error directly, because the + // return type of this function is `R`, not `Result`. + // But we need to communicate the errors' existence to the + // caller, otherwise the caller might mistakenly think that + // no errors occurred and return a zero exit code. So we + // abort (panic) instead, similar to if `f` had panicked. + if guar.is_some() { + compiler.sess.dcx().abort_if_errors(); + } - res - }; + res + }; - let prof = compiler.sess.prof.clone(); - prof.generic_activity("drop_compiler").run(move || drop(compiler)); + let prof = compiler.sess.prof.clone(); + prof.generic_activity("drop_compiler").run(move || drop(compiler)); - res - }) + res }, ) } diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index d0ce23dacb5ef..75df006a56f11 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,5 +1,4 @@ #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(lazy_cell)] #![feature(let_chains)] #![feature(thread_spawn_unchecked)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 4b4c1d6cf672b..b593c41a8eafa 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); @@ -680,23 +682,21 @@ pub fn create_global_ctxt<'tcx>( incremental, ), providers.hooks, + compiler.current_gcx.clone(), ) }) }) } -/// Runs the type-checking, region checking and other miscellaneous analysis -/// passes on the crate. -fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { +/// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses. +/// This function never fails. +fn run_required_analyses(tcx: TyCtxt<'_>) { if tcx.sess.opts.unstable_opts.hir_stats { rustc_passes::hir_stats::print_hir_stats(tcx); } - #[cfg(debug_assertions)] rustc_passes::hir_id_validator::check_crate(tcx); - let sess = tcx.sess; - sess.time("misc_checking_1", || { parallel!( { @@ -732,10 +732,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { } ); }); - - // passes are timed inside typeck - rustc_hir_analysis::check_crate(tcx)?; - + rustc_hir_analysis::check_crate(tcx); sess.time("MIR_borrow_checking", || { tcx.hir().par_body_owners(|def_id| { // Run unsafety check because it's responsible for stealing and @@ -744,12 +741,8 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { tcx.ensure().mir_borrowck(def_id) }); }); - sess.time("MIR_effect_checking", || { for def_id in tcx.hir().body_owners() { - if !tcx.sess.opts.unstable_opts.thir_unsafeck { - rustc_mir_transform::check_unsafety::check_unsafety(tcx, def_id); - } tcx.ensure().has_ffi_unwind_calls(def_id); // If we need to codegen, ensure that we emit all errors from @@ -763,16 +756,24 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { } } }); - tcx.hir().par_body_owners(|def_id| { if tcx.is_coroutine(def_id.to_def_id()) { tcx.ensure().mir_coroutine_witnesses(def_id); - tcx.ensure().check_coroutine_obligations(def_id); + tcx.ensure().check_coroutine_obligations( + tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(), + ); } }); - sess.time("layout_testing", || layout_test::test_layout(tcx)); sess.time("abi_testing", || abi_test::test_abi(tcx)); +} + +/// Runs the type-checking, region checking and other miscellaneous analysis +/// passes on the crate. +fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { + run_required_analyses(tcx); + + let sess = tcx.sess; // Avoid overwhelming user with errors if borrow checking failed. // I'm not sure how helpful this is, to be honest, but it avoids a diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index ee677a092e2f1..1b9165342d45d 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -8,7 +8,7 @@ use rustc_codegen_ssa::CodegenResults; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, OnceLock, WorkerLocal}; -use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; +use rustc_hir::def_id::{StableCrateId, StableCrateIdMap, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_incremental::setup_dep_graph; use rustc_metadata::creader::CStore; @@ -140,11 +140,16 @@ impl<'tcx> Queries<'tcx> { let cstore = FreezeLock::new(Box::new(CStore::new( self.compiler.codegen_backend.metadata_loader(), - stable_crate_id, )) as _); let definitions = FreezeLock::new(Definitions::new(stable_crate_id)); - let untracked = - Untracked { cstore, source_span: AppendOnlyIndexVec::new(), definitions }; + + let stable_crate_ids = FreezeLock::new(StableCrateIdMap::default()); + let untracked = Untracked { + cstore, + source_span: AppendOnlyIndexVec::new(), + definitions, + stable_crate_ids, + }; let qcx = passes::create_global_ctxt( self.compiler, @@ -158,7 +163,8 @@ impl<'tcx> Queries<'tcx> { ); qcx.enter(|tcx| { - let feed = tcx.feed_local_crate(); + let feed = tcx.create_crate_num(stable_crate_id).unwrap(); + assert_eq!(feed.key(), LOCAL_CRATE); feed.crate_name(crate_name); let feed = tcx.feed_unit_query(); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 4d8e749d1da7b..db150cc1f875d 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,67 +1,79 @@ #![allow(rustc::bad_opt_access)] -use crate::interface::parse_cfg; +use crate::interface::{initialize_checked_jobserver, parse_cfg}; use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; use rustc_session::config::{ build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg, - CollapseMacroDebuginfo, CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, - ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold, Input, - InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, - NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, - Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, - WasiExecModel, + CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat, + ErrorOutputType, ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold, + Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, + LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, + PacRet, Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, + SymbolManglingVersion, WasiExecModel, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use rustc_session::{build_session, filesearch, getopts, CompilerIO, EarlyDiagCtxt, Session}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; +use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; use rustc_span::symbol::sym; use rustc_span::{FileName, SourceFileHashAlgorithm}; -use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel}; +use rustc_target::spec::{ + CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel, WasmCAbi, +}; use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel}; use std::collections::{BTreeMap, BTreeSet}; use std::num::NonZero; use std::path::{Path, PathBuf}; use std::sync::Arc; -fn mk_session(matches: getopts::Matches) -> (Session, Cfg) { +fn sess_and_cfg(args: &[&'static str], f: F) +where + F: FnOnce(Session, Cfg), +{ let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); - early_dcx.initialize_checked_jobserver(); + initialize_checked_jobserver(&early_dcx); - let registry = registry::Registry::new(&[]); + let matches = optgroups().parse(args).unwrap(); let sessopts = build_session_options(&mut early_dcx, &matches); - let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); - let io = CompilerIO { - input: Input::Str { name: FileName::Custom(String::new()), input: String::new() }, - output_dir: None, - output_file: None, - temps_dir, - }; - let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); + let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot); + let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target); + let sm_inputs = Some(SourceMapInputs { + file_loader: Box::new(RealFileLoader) as _, + path_mapping: sessopts.file_path_mapping(), + hash_kind, + }); - let target_cfg = - rustc_session::config::build_target_config(&early_dcx, &sessopts, None, &sysroot); - - let sess = build_session( - early_dcx, - sessopts, - io, - None, - registry, - vec![], - Default::default(), - None, - target_cfg, - sysroot, - "", - None, - Arc::default(), - Default::default(), - ); - let cfg = parse_cfg(&sess.dcx(), matches.opt_strs("cfg")); - (sess, cfg) + rustc_span::create_session_globals_then(DEFAULT_EDITION, sm_inputs, || { + let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); + let io = CompilerIO { + input: Input::Str { name: FileName::Custom(String::new()), input: String::new() }, + output_dir: None, + output_file: None, + temps_dir, + }; + + let sess = build_session( + early_dcx, + sessopts, + io, + None, + registry::Registry::new(&[]), + vec![], + Default::default(), + target, + sysroot, + "", + None, + Arc::default(), + Default::default(), + ); + let cfg = parse_cfg(&sess.dcx(), matches.opt_strs("cfg")); + let cfg = build_configuration(&sess, cfg); + f(sess, cfg) + }); } fn new_public_extern_entry(locations: I) -> ExternEntry @@ -126,21 +138,15 @@ fn assert_non_crate_hash_different(x: &Options, y: &Options) { // When the user supplies --test we should implicitly supply --cfg test #[test] fn test_switch_implies_cfg_test() { - rustc_span::create_default_session_globals_then(|| { - let matches = optgroups().parse(&["--test".to_string()]).unwrap(); - let (sess, cfg) = mk_session(matches); - let cfg = build_configuration(&sess, cfg); + sess_and_cfg(&["--test"], |_sess, cfg| { assert!(cfg.contains(&(sym::test, None))); - }); + }) } // When the user supplies --test and --cfg test, don't implicitly add another --cfg test #[test] fn test_switch_implies_cfg_test_unless_cfg_test() { - rustc_span::create_default_session_globals_then(|| { - let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap(); - let (sess, cfg) = mk_session(matches); - let cfg = build_configuration(&sess, cfg); + sess_and_cfg(&["--test", "--cfg=test"], |_sess, cfg| { let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test); assert!(test_items.next().is_some()); assert!(test_items.next().is_none()); @@ -149,22 +155,15 @@ fn test_switch_implies_cfg_test_unless_cfg_test() { #[test] fn test_can_print_warnings() { - rustc_span::create_default_session_globals_then(|| { - let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); + sess_and_cfg(&["-Awarnings"], |sess, _cfg| { assert!(!sess.dcx().can_emit_warnings()); }); - rustc_span::create_default_session_globals_then(|| { - let matches = - optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); + sess_and_cfg(&["-Awarnings", "-Dwarnings"], |sess, _cfg| { assert!(sess.dcx().can_emit_warnings()); }); - rustc_span::create_default_session_globals_then(|| { - let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap(); - let (sess, _) = mk_session(matches); + sess_and_cfg(&["-Adead_code"], |sess, _cfg| { assert!(sess.dcx().can_emit_warnings()); }); } @@ -316,30 +315,40 @@ fn test_search_paths_tracking_hash_different_order() { json_rendered: HumanReadableErrorType::Default(ColorConfig::Never), }; + let push = |opts: &mut Options, search_path| { + opts.search_paths.push(SearchPath::from_cli_opt( + "not-a-sysroot".as_ref(), + &opts.target_triple, + &early_dcx, + search_path, + false, + )); + }; + // Reference - v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc")); - v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def")); - v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi")); - v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl")); - v1.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno")); - - v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc")); - v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi")); - v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def")); - v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl")); - v2.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno")); - - v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def")); - v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl")); - v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc")); - v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi")); - v3.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno")); - - v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "all=mno")); - v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "native=abc")); - v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "crate=def")); - v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "dependency=ghi")); - v4.search_paths.push(SearchPath::from_cli_opt(&early_dcx, "framework=jkl")); + push(&mut v1, "native=abc"); + push(&mut v1, "crate=def"); + push(&mut v1, "dependency=ghi"); + push(&mut v1, "framework=jkl"); + push(&mut v1, "all=mno"); + + push(&mut v2, "native=abc"); + push(&mut v2, "dependency=ghi"); + push(&mut v2, "crate=def"); + push(&mut v2, "framework=jkl"); + push(&mut v2, "all=mno"); + + push(&mut v3, "crate=def"); + push(&mut v3, "framework=jkl"); + push(&mut v3, "native=abc"); + push(&mut v3, "dependency=ghi"); + push(&mut v3, "all=mno"); + + push(&mut v4, "all=mno"); + push(&mut v4, "native=abc"); + push(&mut v4, "crate=def"); + push(&mut v4, "dependency=ghi"); + push(&mut v4, "framework=jkl"); assert_same_hash(&v1, &v2); assert_same_hash(&v1, &v3); @@ -591,6 +600,7 @@ fn test_codegen_options_tracking_hash() { // Make sure that changing a [TRACKED] option changes the hash. // tidy-alphabetical-start tracked!(code_model, Some(CodeModel::Large)); + tracked!(collapse_macro_debuginfo, CollapseMacroDebuginfo::Yes); tracked!(control_flow_guard, CFGuard::Checks); tracked!(debug_assertions, Some(true)); tracked!(debuginfo, DebugInfo::Limited); @@ -616,6 +626,7 @@ fn test_codegen_options_tracking_hash() { tracked!(profile_generate, SwitchWithOptPath::Enabled(None)); tracked!(profile_use, Some(PathBuf::from("abc"))); tracked!(relocation_model, Some(RelocModel::Pic)); + tracked!(relro_level, Some(RelroLevel::Full)); tracked!(soft_float, true); tracked!(split_debuginfo, Some(SplitDebuginfo::Packed)); tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0)); @@ -750,12 +761,10 @@ fn test_unstable_options_tracking_hash() { }) ); tracked!(codegen_backend, Some("abc".to_string())); - tracked!(collapse_macro_debuginfo, CollapseMacroDebuginfo::Yes); - tracked!(coverage_options, CoverageOptions { branch: true }); + tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc }); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); - tracked!(debug_macros, true); tracked!(default_hidden_visibility, Some(true)); tracked!(dep_info_omit_d_target, true); tracked!(direct_access_external_data, Some(true)); @@ -814,7 +823,6 @@ fn test_unstable_options_tracking_hash() { tracked!(profile_sample_use, Some(PathBuf::from("abc"))); tracked!(profiler_runtime, "abc".to_string()); tracked!(relax_elf_relocations, Some(true)); - tracked!(relro_level, Some(RelroLevel::Full)); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); tracked!(sanitizer, SanitizerSet::ADDRESS); tracked!(sanitizer_cfi_canonical_jump_tables, None); @@ -832,19 +840,20 @@ fn test_unstable_options_tracking_hash() { tracked!(stack_protector, StackProtector::All); tracked!(teach, true); tracked!(thinlto, Some(true)); - tracked!(thir_unsafeck, false); tracked!(tiny_const_eval_limit, true); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); tracked!(translate_remapped_path_to_local_path, false); tracked!(trap_unreachable, Some(false)); tracked!(treat_err_as_bug, NonZero::new(1)); tracked!(tune_cpu, Some(String::from("abc"))); + tracked!(ub_checks, Some(false)); tracked!(uninit_const_chunk_threshold, 123); tracked!(unleash_the_miri_inside_of_you, true); tracked!(use_ctors_section, Some(true)); tracked!(verify_llvm_ir, true); tracked!(virtual_function_elimination, true); tracked!(wasi_exec_model, Some(WasiExecModel::Reactor)); + tracked!(wasm_c_abi, WasmCAbi::Spec); // tidy-alphabetical-end macro_rules! tracked_no_crate_hash { diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 7d48f90db3625..02dcfe9c8dffb 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,6 +5,7 @@ use rustc_codegen_ssa::traits::CodegenBackend; #[cfg(parallel_compiler)] use rustc_data_structures::sync; use rustc_metadata::{load_symbol_from_dylib, DylibError}; +use rustc_middle::ty::CurrentGcx; use rustc_parse::validate_attr; use rustc_session as session; use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes}; @@ -13,6 +14,7 @@ use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; use rustc_session::{filesearch, Session}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; +use rustc_span::source_map::SourceMapInputs; use rustc_span::symbol::sym; use rustc_target::spec::Target; use session::output::{categorize_crate_type, CRATE_TYPES}; @@ -48,16 +50,25 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy } } -const STACK_SIZE: usize = 8 * 1024 * 1024; - -fn get_stack_size() -> Option { - // FIXME: Hacks on hacks. If the env is trying to override the stack size - // then *don't* set it explicitly. - env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE) +pub static STACK_SIZE: OnceLock = OnceLock::new(); +pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; + +fn init_stack_size() -> usize { + // Obey the environment setting or default + *STACK_SIZE.get_or_init(|| { + env::var_os("RUST_MIN_STACK") + .map(|os_str| os_str.to_string_lossy().into_owned()) + // ignore if it is set to nothing + .filter(|s| s.trim() != "") + .map(|s| s.trim().parse::().unwrap()) + // otherwise pick a consistent default + .unwrap_or(DEFAULT_STACK_SIZE) + }) } -pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( +fn run_in_thread_with_globals R + Send, R: Send>( edition: Edition, + sm_inputs: SourceMapInputs, f: F, ) -> R { // The "thread pool" is a single spawned thread in the non-parallel @@ -66,10 +77,7 @@ pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( // the parallel compiler, in particular to ensure there is no accidental // sharing of data between the main thread and the compilation thread // (which might cause problems for the parallel compiler). - let mut builder = thread::Builder::new().name("rustc".to_string()); - if let Some(size) = get_stack_size() { - builder = builder.stack_size(size); - } + let builder = thread::Builder::new().name("rustc".to_string()).stack_size(init_stack_size()); // We build the session globals and run `f` on the spawned thread, because // `SessionGlobals` does not impl `Send` in the non-parallel compiler. @@ -77,7 +85,11 @@ pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( // `unwrap` is ok here because `spawn_scoped` only panics if the thread // name contains null bytes. let r = builder - .spawn_scoped(s, move || rustc_span::create_session_globals_then(edition, f)) + .spawn_scoped(s, move || { + rustc_span::create_session_globals_then(edition, Some(sm_inputs), || { + f(CurrentGcx::new()) + }) + }) .unwrap() .join(); @@ -89,18 +101,20 @@ pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( } #[cfg(not(parallel_compiler))] -pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( +pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, _threads: usize, + sm_inputs: SourceMapInputs, f: F, ) -> R { - run_in_thread_with_globals(edition, f) + run_in_thread_with_globals(edition, sm_inputs, f) } #[cfg(parallel_compiler)] -pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( +pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, threads: usize, + sm_inputs: SourceMapInputs, f: F, ) -> R { use rustc_data_structures::{defer, jobserver, sync::FromDyn}; @@ -112,24 +126,34 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); if !sync::is_dyn_thread_safe() { - return run_in_thread_with_globals(edition, || { + return run_in_thread_with_globals(edition, sm_inputs, |current_gcx| { // Register the thread for use with the `WorkerLocal` type. registry.register(); - f() + f(current_gcx) }); } - let mut builder = rayon::ThreadPoolBuilder::new() + let current_gcx = FromDyn::from(CurrentGcx::new()); + let current_gcx2 = current_gcx.clone(); + + let builder = rayon::ThreadPoolBuilder::new() .thread_name(|_| "rustc".to_string()) .acquire_thread_handler(jobserver::acquire_thread) .release_thread_handler(jobserver::release_thread) .num_threads(threads) - .deadlock_handler(|| { + .deadlock_handler(move || { // On deadlock, creates a new thread and forwards information in thread // locals to it. The new thread runs the deadlock handler. - let query_map = - FromDyn::from(tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs())); + + // Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a + // `TyCtxt` TLS reference here. + let query_map = current_gcx2.access(|gcx| { + tls::enter_context(&tls::ImplicitCtxt::new(gcx), || { + tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs()) + }) + }); + let query_map = FromDyn::from(query_map); let registry = rayon_core::Registry::current(); thread::Builder::new() .name("rustc query cycle handler".to_string()) @@ -144,16 +168,14 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( on_panic.disable(); }) .unwrap(); - }); - if let Some(size) = get_stack_size() { - builder = builder.stack_size(size); - } + }) + .stack_size(init_stack_size()); // We create the session globals on the main thread, then create the thread // pool. Upon creation, each worker thread created gets a copy of the // session globals in TLS. This is possible because `SessionGlobals` impls // `Send` in the parallel compiler. - rustc_span::create_session_globals_then(edition, || { + rustc_span::create_session_globals_then(edition, Some(sm_inputs), || { rustc_span::with_session_globals(|session_globals| { let session_globals = FromDyn::from(session_globals); builder @@ -168,7 +190,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( }) }, // Run `f` on the first thread in the thread pool. - move |pool: &rayon::ThreadPool| pool.install(f), + move |pool: &rayon::ThreadPool| pool.install(|| f(current_gcx.into_inner())), ) .unwrap() }) diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index aba7f95487e9d..d173c3ac0327b 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -46,7 +46,7 @@ impl<'a> Cursor<'a> { /// If requested position doesn't exist, `EOF_CHAR` is returned. /// However, getting `EOF_CHAR` doesn't always mean actual end of file, /// it should be checked with `is_eof` method. - pub(crate) fn first(&self) -> char { + pub fn first(&self) -> char { // `.next()` optimizes better than `.nth(0)` self.chars.clone().next().unwrap_or(EOF_CHAR) } @@ -59,6 +59,15 @@ impl<'a> Cursor<'a> { iter.next().unwrap_or(EOF_CHAR) } + /// Peeks the third symbol from the input stream without consuming it. + pub fn third(&self) -> char { + // `.next()` optimizes better than `.nth(1)` + let mut iter = self.chars.clone(); + iter.next(); + iter.next(); + iter.next().unwrap_or(EOF_CHAR) + } + /// Checks if there is nothing more to consume. pub(crate) fn is_eof(&self) -> bool { self.chars.as_str().is_empty() diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index ca84e930c2439..6f8a9792b6ce8 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -88,6 +88,10 @@ pub enum TokenKind { /// tokens. UnknownPrefix, + /// Similar to the above, but *always* an error on every edition. This is used + /// for emoji identifier recovery, as those are not meant to be ever accepted. + InvalidPrefix, + /// Examples: `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid /// suffix, but may be present here on string and float literals. Users of /// this type will need to check for and reject that case. @@ -528,7 +532,7 @@ impl Cursor<'_> { // Known prefixes must have been handled earlier. So if // we see a prefix here, it is definitely an unknown prefix. match self.first() { - '#' | '"' | '\'' => UnknownPrefix, + '#' | '"' | '\'' => InvalidPrefix, _ => InvalidIdent, } } @@ -626,7 +630,7 @@ impl Cursor<'_> { // with a number self.bump(); let mut empty_exponent = false; - if self.first().is_digit(10) { + if self.first().is_ascii_digit() { self.eat_decimal_digits(); match self.first() { 'e' | 'E' => { @@ -657,7 +661,7 @@ impl Cursor<'_> { // If the first symbol is valid for identifier, it can be a lifetime. // Also check if it's a number for a better error reporting (so '0 will // be reported as invalid lifetime and not as unterminated char literal). - is_id_start(self.first()) || self.first().is_digit(10) + is_id_start(self.first()) || self.first().is_ascii_digit() }; if !can_be_a_lifetime { @@ -673,7 +677,7 @@ impl Cursor<'_> { // Either a lifetime or a character literal with // length greater than 1. - let starts_with_number = self.first().is_digit(10); + let starts_with_number = self.first().is_ascii_digit(); // Skip the literal contents. // First symbol can be a number (which isn't a valid identifier start), diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index 03d178eb266a4..d6ea4249247f3 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -259,7 +259,7 @@ fn scan_escape + From>( } else { // This may be a high byte, but that will only happen if `T` is // `MixedUnit`, because of the `allow_high_bytes` check above. - Ok(T::from(value as u8)) + Ok(T::from(value)) }; } 'u' => return scan_unicode(chars, mode.allow_unicode_escapes()).map(T::from), @@ -300,7 +300,7 @@ fn scan_unicode(chars: &mut Chars<'_>, allow_unicode_escapes: bool) -> Result 0x10FFFF { EscapeError::OutOfRangeUnicodeEscape } else { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index d938a0ccb3986..676a7c21841a1 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -299,6 +299,9 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` lint_improper_ctypes_opaque = opaque types have no C equivalent +lint_improper_ctypes_pat_help = consider using the base type instead + +lint_improper_ctypes_pat_reason = pattern types have no C equivalent lint_improper_ctypes_slice_help = consider using a raw pointer instead lint_improper_ctypes_slice_reason = slices have no C equivalent @@ -443,7 +446,7 @@ lint_non_local_definitions_impl = non-local `impl` definition, they should be av [one] `{$body_name}` *[other] `{$body_name}` and up {$depth} bodies } - .non_local = an `impl` definition is non-local if it is nested inside an item and neither the type nor the trait are at the same nesting level as the `impl` block + .non_local = an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` .exception = one exception to the rule are anon-const (`const _: () = {"{"} ... {"}"}`) at top-level module and anon-const at the same nesting as the trait or type .const_anon = use a const-anon item to suppress this lint @@ -453,6 +456,8 @@ lint_non_local_definitions_macro_rules = non-local `macro_rules!` definition, th [one] `{$body_name}` *[other] `{$body_name}` and up {$depth} bodies } + .help_doctest = + remove the `#[macro_export]` or make this doc-test a standalone test with its own `fn main() {"{"} ... {"}"}` .non_local = a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute .exception = one exception to the rule are anon-const (`const _: () = {"{"} ... {"}"}`) at top-level module diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index 3a5c585366a31..8f4bae339573c 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -3,9 +3,11 @@ use crate::{ LateContext, LateLintPass, LintContext, }; use rustc_hir as hir; +use rustc_middle::bug; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::lint::FutureIncompatibilityReason; +use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::edition::Edition; use rustc_span::symbol::sym; use rustc_span::Span; diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs index 5f7a3137d52d4..40778542c75c9 100644 --- a/compiler/rustc_lint/src/async_fn_in_trait.rs +++ b/compiler/rustc_lint/src/async_fn_in_trait.rs @@ -2,6 +2,7 @@ use crate::lints::AsyncFnInTraitDiag; use crate::LateContext; use crate::LateLintPass; use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_trait_selection::traits::error_reporting::suggestions::suggest_desugaring_async_fn_to_impl_future_in_trait; declare_lint! { diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d1343e3b4bae8..ba316e5eeb041 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -51,6 +51,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit::FnKind as HirFnKind; use rustc_hir::{Body, FnDecl, GenericParamKind, Node, PatKind, PredicateOrigin}; +use rustc_middle::bug; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -59,6 +60,7 @@ use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; use rustc_session::lint::{BuiltinLintDiag, FutureIncompatibilityReason}; +use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -726,7 +728,7 @@ fn type_implements_negative_copy_modulo_regions<'tcx>( param_env: ty::ParamEnv<'tcx>, ) -> bool { let trait_ref = ty::TraitRef::new(tcx, tcx.require_lang_item(hir::LangItem::Copy, None), [ty]); - let pred = ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Negative }; + let pred = ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Negative }; let obligation = traits::Obligation { cause: traits::ObligationCause::dummy(), param_env, @@ -1337,8 +1339,9 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { } declare_lint! { - /// The `unreachable_pub` lint triggers for `pub` items not reachable from - /// the crate root. + /// The `unreachable_pub` lint triggers for `pub` items not reachable from other crates - that + /// means neither directly accessible, nor reexported, nor leaked through things like return + /// types. /// /// ### Example /// @@ -1768,13 +1771,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } declare_lint! { - /// The `keyword_idents` lint detects edition keywords being used as an + /// The `keyword_idents_2018` lint detects edition keywords being used as an /// identifier. /// /// ### Example /// /// ```rust,edition2015,compile_fail - /// #![deny(keyword_idents)] + /// #![deny(keyword_idents_2018)] /// // edition 2015 /// fn dyn() {} /// ``` @@ -1803,7 +1806,7 @@ declare_lint! { /// [editions]: https://doc.rust-lang.org/edition-guide/ /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html - pub KEYWORD_IDENTS, + pub KEYWORD_IDENTS_2018, Allow, "detects edition keywords being used as an identifier", @future_incompatible = FutureIncompatibleInfo { @@ -1812,9 +1815,54 @@ declare_lint! { }; } +declare_lint! { + /// The `keyword_idents_2024` lint detects edition keywords being used as an + /// identifier. + /// + /// ### Example + /// + /// ```rust,edition2015,compile_fail + /// #![deny(keyword_idents_2024)] + /// // edition 2015 + /// fn gen() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Rust [editions] allow the language to evolve without breaking + /// backwards compatibility. This lint catches code that uses new keywords + /// that are added to the language that are used as identifiers (such as a + /// variable name, function name, etc.). If you switch the compiler to a + /// new edition without updating the code, then it will fail to compile if + /// you are using a new keyword as an identifier. + /// + /// You can manually change the identifiers to a non-keyword, or use a + /// [raw identifier], for example `r#gen`, to transition to a new edition. + /// + /// This lint solves the problem automatically. It is "allow" by default + /// because the code is perfectly valid in older editions. The [`cargo + /// fix`] tool with the `--edition` flag will switch this lint to "warn" + /// and automatically apply the suggested fix from the compiler (which is + /// to use a raw identifier). This provides a completely automated way to + /// update old code for a new edition. + /// + /// [editions]: https://doc.rust-lang.org/edition-guide/ + /// [raw identifier]: https://doc.rust-lang.org/reference/identifiers.html + /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html + pub KEYWORD_IDENTS_2024, + Allow, + "detects edition keywords being used as an identifier", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "issue #49716 ", + }; +} + declare_lint_pass!( /// Check for uses of edition keywords used as an identifier. - KeywordIdents => [KEYWORD_IDENTS] + KeywordIdents => [KEYWORD_IDENTS_2018, KEYWORD_IDENTS_2024] ); struct UnderMacro(bool); @@ -1840,42 +1888,39 @@ impl KeywordIdents { UnderMacro(under_macro): UnderMacro, ident: Ident, ) { - let next_edition = match cx.sess().edition() { - Edition::Edition2015 => { - match ident.name { - kw::Async | kw::Await | kw::Try => Edition::Edition2018, - - // rust-lang/rust#56327: Conservatively do not - // attempt to report occurrences of `dyn` within - // macro definitions or invocations, because `dyn` - // can legitimately occur as a contextual keyword - // in 2015 code denoting its 2018 meaning, and we - // do not want rustfix to inject bugs into working - // code by rewriting such occurrences. - // - // But if we see `dyn` outside of a macro, we know - // its precise role in the parsed AST and thus are - // assured this is truly an attempt to use it as - // an identifier. - kw::Dyn if !under_macro => Edition::Edition2018, - - _ => return, - } - } + let (lint, edition) = match ident.name { + kw::Async | kw::Await | kw::Try => (KEYWORD_IDENTS_2018, Edition::Edition2018), + + // rust-lang/rust#56327: Conservatively do not + // attempt to report occurrences of `dyn` within + // macro definitions or invocations, because `dyn` + // can legitimately occur as a contextual keyword + // in 2015 code denoting its 2018 meaning, and we + // do not want rustfix to inject bugs into working + // code by rewriting such occurrences. + // + // But if we see `dyn` outside of a macro, we know + // its precise role in the parsed AST and thus are + // assured this is truly an attempt to use it as + // an identifier. + kw::Dyn if !under_macro => (KEYWORD_IDENTS_2018, Edition::Edition2018), + + kw::Gen => (KEYWORD_IDENTS_2024, Edition::Edition2024), - // There are no new keywords yet for the 2018 edition and beyond. _ => return, }; // Don't lint `r#foo`. - if cx.sess().psess.raw_identifier_spans.contains(ident.span) { + if ident.span.edition() >= edition + || cx.sess().psess.raw_identifier_spans.contains(ident.span) + { return; } cx.emit_span_lint( - KEYWORD_IDENTS, + lint, ident.span, - BuiltinKeywordIdents { kw: ident, next: next_edition, suggestion: ident.span }, + BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span }, ); } } @@ -2468,6 +2513,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { ty: Ty<'tcx>, init: InitKind, ) -> Option { + let ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty); + use rustc_type_ir::TyKind::*; match ty.kind() { // Primitive types that don't like 0 as a value. @@ -2475,7 +2522,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { Adt(..) if ty.is_box() => Some("`Box` must be non-null".into()), FnPtr(..) => Some("function pointers must be non-null".into()), Never => Some("the `!` type has no valid value".into()), - RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) => + RawPtr(ty, _) if matches!(ty.kind(), Dynamic(..)) => // raw ptr to dyn Trait { Some("the vtable of a wide raw pointer must be non-null".into()) @@ -2491,7 +2538,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { Some("integers must be initialized".into()) } Float(_) if init == InitKind::Uninit => Some("floats must be initialized".into()), - RawPtr(_) if init == InitKind::Uninit => { + RawPtr(_, _) if init == InitKind::Uninit => { Some("raw pointers must be initialized".into()) } // Recurse and checks for some compound types. (but not unions) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 8c0e5b17fd871..0931bb29963de 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -27,6 +27,7 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_middle::bug; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, PrintError}; @@ -110,7 +111,7 @@ struct LintAlias { struct LintGroup { lint_ids: Vec, - is_loaded: bool, + is_externally_loaded: bool, depr: Option, } @@ -159,7 +160,9 @@ impl LintStore { // Don't display deprecated lint groups. depr.is_none() }) - .map(|(k, LintGroup { lint_ids, is_loaded, .. })| (*k, lint_ids.clone(), *is_loaded)) + .map(|(k, LintGroup { lint_ids, is_externally_loaded, .. })| { + (*k, lint_ids.clone(), *is_externally_loaded) + }) } pub fn register_early_pass( @@ -218,7 +221,7 @@ impl LintStore { .entry(edition.lint_name()) .or_insert(LintGroup { lint_ids: vec![], - is_loaded: lint.is_loaded, + is_externally_loaded: lint.is_externally_loaded, depr: None, }) .lint_ids @@ -231,7 +234,7 @@ impl LintStore { .entry("future_incompatible") .or_insert(LintGroup { lint_ids: vec![], - is_loaded: lint.is_loaded, + is_externally_loaded: lint.is_externally_loaded, depr: None, }) .lint_ids @@ -246,7 +249,7 @@ impl LintStore { alias, LintGroup { lint_ids: vec![], - is_loaded: false, + is_externally_loaded: false, depr: Some(LintAlias { name: lint_name, silent: true }), }, ); @@ -254,21 +257,21 @@ impl LintStore { pub fn register_group( &mut self, - is_loaded: bool, + is_externally_loaded: bool, name: &'static str, deprecated_name: Option<&'static str>, to: Vec, ) { let new = self .lint_groups - .insert(name, LintGroup { lint_ids: to, is_loaded, depr: None }) + .insert(name, LintGroup { lint_ids: to, is_externally_loaded, depr: None }) .is_none(); if let Some(deprecated) = deprecated_name { self.lint_groups.insert( deprecated, LintGroup { lint_ids: vec![], - is_loaded, + is_externally_loaded, depr: Some(LintAlias { name, silent: false }), }, ); @@ -741,7 +744,7 @@ impl<'tcx> LateContext<'tcx> { .filter(|typeck_results| typeck_results.hir_owner == id.owner) .or_else(|| { self.tcx - .has_typeck_results(id.owner.to_def_id()) + .has_typeck_results(id.owner.def_id) .then(|| self.tcx.typeck(id.owner.def_id)) }) .and_then(|typeck_results| typeck_results.type_dependent_def(id)) @@ -937,7 +940,7 @@ impl<'tcx> LateContext<'tcx> { } && let Some(init) = match parent_node { hir::Node::Expr(expr) => Some(expr), - hir::Node::Local(hir::Local { init, .. }) => *init, + hir::Node::LetStmt(hir::LetStmt { init, .. }) => *init, _ => None, } { @@ -982,7 +985,7 @@ impl<'tcx> LateContext<'tcx> { } && let Some(init) = match parent_node { hir::Node::Expr(expr) => Some(expr), - hir::Node::Local(hir::Local { init, .. }) => *init, + hir::Node::LetStmt(hir::LetStmt { init, .. }) => *init, hir::Node::Item(item) => match item.kind { hir::ItemKind::Const(.., body_id) | hir::ItemKind::Static(.., body_id) => { Some(self.tcx.hir().body(body_id).value) diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index a0be1c09c9a3d..8458b53933537 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -2,52 +2,14 @@ #![allow(rustc::untranslatable_diagnostic)] use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; -use rustc_errors::{add_elided_lifetime_in_path_suggestion, Diag}; +use rustc_errors::{elided_lifetime_in_path_suggestion, Diag}; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_middle::middle::stability; -use rustc_session::config::ExpectedValues; use rustc_session::lint::BuiltinLintDiag; use rustc_session::Session; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::BytePos; -const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; - -fn check_cfg_expected_note( - sess: &Session, - possibilities: &[Symbol], - type_: &str, - name: Option, - suffix: &str, -) -> String { - use std::fmt::Write; - - let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { - possibilities.len() - } else { - std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) - }; - - let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); - possibilities.sort(); - - let and_more = possibilities.len().saturating_sub(n_possibilities); - let possibilities = possibilities[..n_possibilities].join("`, `"); - - let mut note = String::with_capacity(50 + possibilities.len()); - - write!(&mut note, "expected {type_}").unwrap(); - if let Some(name) = name { - write!(&mut note, " for `{name}`").unwrap(); - } - write!(&mut note, " are: {suffix}`{possibilities}`").unwrap(); - if and_more > 0 { - write!(&mut note, " and {and_more} more").unwrap(); - } - - note -} +mod check_cfg; pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) { match diagnostic { @@ -112,13 +74,15 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di diag.span_note(span_def, "the macro is defined here"); } BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { - add_elided_lifetime_in_path_suggestion( - sess.source_map(), - diag, - n, - path_span, - incl_angl_brckt, - insertion_span, + diag.subdiagnostic( + sess.dcx(), + elided_lifetime_in_path_suggestion( + sess.source_map(), + n, + path_span, + incl_angl_brckt, + insertion_span, + ), ); } BuiltinLintDiag::UnknownCrateTypes(span, note, sugg) => { @@ -143,7 +107,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}"), @@ -219,242 +183,11 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di diag.help(help); diag.note("see the asm section of Rust By Example for more information"); } - BuiltinLintDiag::UnexpectedCfgName((name, name_span), value) => { - #[allow(rustc::potential_query_instability)] - let possibilities: Vec = - sess.psess.check_config.expecteds.keys().copied().collect(); - - let mut names_possibilities: Vec<_> = if value.is_none() { - // We later sort and display all the possibilities, so the order here does not matter. - #[allow(rustc::potential_query_instability)] - sess.psess - .check_config - .expecteds - .iter() - .filter_map(|(k, v)| match v { - ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k), - _ => None, - }) - .collect() - } else { - Vec::new() - }; - - let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); - let mut is_feature_cfg = name == sym::feature; - - if is_feature_cfg && is_from_cargo { - diag.help("consider defining some features in `Cargo.toml`"); - // Suggest the most probable if we found one - } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { - if let Some(ExpectedValues::Some(best_match_values)) = - sess.psess.check_config.expecteds.get(&best_match) - { - // We will soon sort, so the initial order does not matter. - #[allow(rustc::potential_query_instability)] - let mut possibilities = - best_match_values.iter().flatten().map(Symbol::as_str).collect::>(); - possibilities.sort(); - - let mut should_print_possibilities = true; - if let Some((value, value_span)) = value { - if best_match_values.contains(&Some(value)) { - diag.span_suggestion( - name_span, - "there is a config with a similar name and value", - best_match, - Applicability::MaybeIncorrect, - ); - should_print_possibilities = false; - } else if best_match_values.contains(&None) { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and no value", - best_match, - Applicability::MaybeIncorrect, - ); - should_print_possibilities = false; - } else if let Some(first_value) = possibilities.first() { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and different values", - format!("{best_match} = \"{first_value}\""), - Applicability::MaybeIncorrect, - ); - } else { - diag.span_suggestion( - name_span.to(value_span), - "there is a config with a similar name and different values", - best_match, - Applicability::MaybeIncorrect, - ); - }; - } else { - diag.span_suggestion( - name_span, - "there is a config with a similar name", - best_match, - Applicability::MaybeIncorrect, - ); - } - - if !possibilities.is_empty() && should_print_possibilities { - let possibilities = possibilities.join("`, `"); - diag.help(format!( - "expected values for `{best_match}` are: `{possibilities}`" - )); - } - } else { - diag.span_suggestion( - name_span, - "there is a config with a similar name", - best_match, - Applicability::MaybeIncorrect, - ); - } - - is_feature_cfg |= best_match == sym::feature; - } else { - if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { - names_possibilities.sort(); - for cfg_name in names_possibilities.iter() { - diag.span_suggestion( - name_span, - "found config with similar value", - format!("{cfg_name} = \"{name}\""), - Applicability::MaybeIncorrect, - ); - } - } - if !possibilities.is_empty() { - diag.help_once(check_cfg_expected_note( - sess, - &possibilities, - "names", - None, - "", - )); - } - } - - let inst = if let Some((value, _value_span)) = value { - let pre = if is_from_cargo { "\\" } else { "" }; - format!("cfg({name}, values({pre}\"{value}{pre}\"))") - } else { - format!("cfg({name})") - }; - - if is_from_cargo { - if !is_feature_cfg { - diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); - } - diag.note("see for more information about checking conditional configuration"); - } else { - diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); - diag.note("see for more information about checking conditional configuration"); - } + BuiltinLintDiag::UnexpectedCfgName(name, value) => { + check_cfg::unexpected_cfg_name(sess, diag, name, value) } - BuiltinLintDiag::UnexpectedCfgValue((name, name_span), value) => { - let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) - else { - bug!( - "it shouldn't be possible to have a diagnostic on a value whose name is not in values" - ); - }; - let mut have_none_possibility = false; - // We later sort possibilities if it is not empty, so the - // order here does not matter. - #[allow(rustc::potential_query_instability)] - let possibilities: Vec = values - .iter() - .inspect(|a| have_none_possibility |= a.is_none()) - .copied() - .flatten() - .collect(); - let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); - - // Show the full list if all possible values for a given name, but don't do it - // for names as the possibilities could be very long - if !possibilities.is_empty() { - diag.note(check_cfg_expected_note( - sess, - &possibilities, - "values", - Some(name), - if have_none_possibility { "(none), " } else { "" }, - )); - - if let Some((value, value_span)) = value { - // Suggest the most probable if we found one - if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) - { - diag.span_suggestion( - value_span, - "there is a expected value with a similar name", - format!("\"{best_match}\""), - Applicability::MaybeIncorrect, - ); - } - } else if let &[first_possibility] = &possibilities[..] { - diag.span_suggestion( - name_span.shrink_to_hi(), - "specify a config value", - format!(" = \"{first_possibility}\""), - Applicability::MaybeIncorrect, - ); - } - } else if have_none_possibility { - diag.note(format!("no expected value for `{name}`")); - if let Some((_value, value_span)) = value { - diag.span_suggestion( - name_span.shrink_to_hi().to(value_span), - "remove the value", - "", - Applicability::MaybeIncorrect, - ); - } - } else { - diag.note(format!("no expected values for `{name}`")); - - let sp = if let Some((_value, value_span)) = value { - name_span.to(value_span) - } else { - name_span - }; - diag.span_suggestion(sp, "remove the condition", "", Applicability::MaybeIncorrect); - } - - // We don't want to suggest adding values to well known names - // since those are defined by rustc it-self. Users can still - // do it if they want, but should not encourage them. - let is_cfg_a_well_know_name = sess.psess.check_config.well_known_names.contains(&name); - - let inst = if let Some((value, _value_span)) = value { - let pre = if is_from_cargo { "\\" } else { "" }; - format!("cfg({name}, values({pre}\"{value}{pre}\"))") - } else { - format!("cfg({name})") - }; - - if is_from_cargo { - if name == sym::feature { - if let Some((value, _value_span)) = value { - diag.help(format!( - "consider adding `{value}` as a feature in `Cargo.toml`" - )); - } else { - diag.help("consider defining some features in `Cargo.toml`"); - } - } else if !is_cfg_a_well_know_name { - diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); - } - diag.note("see for more information about checking conditional configuration"); - } else { - if !is_cfg_a_well_know_name { - diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); - } - diag.note("see for more information about checking conditional configuration"); - } + BuiltinLintDiag::UnexpectedCfgValue(name, value) => { + check_cfg::unexpected_cfg_value(sess, diag, name, value) } BuiltinLintDiag::DeprecatedWhereclauseLocation(sugg) => { let left_sp = diag.span.primary_span().unwrap(); @@ -484,10 +217,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di if let Some(deletion_span) = deletion_span { let msg = "elide the single-use lifetime"; let (use_span, replace_lt) = if elide { - let use_span = sess - .source_map() - .span_extend_while(use_span, char::is_whitespace) - .unwrap_or(use_span); + let use_span = sess.source_map().span_extend_while_whitespace(use_span); (use_span, String::new()) } else { (use_span, "'_".to_owned()) diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs new file mode 100644 index 0000000000000..e9a6276192e52 --- /dev/null +++ b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs @@ -0,0 +1,278 @@ +use rustc_errors::{Applicability, Diag}; +use rustc_middle::bug; +use rustc_session::{config::ExpectedValues, Session}; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::{sym, Span, Symbol}; + +const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; + +fn check_cfg_expected_note( + sess: &Session, + possibilities: &[Symbol], + type_: &str, + name: Option, + suffix: &str, +) -> String { + use std::fmt::Write; + + let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { + possibilities.len() + } else { + std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) + }; + + let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::>(); + possibilities.sort(); + + let and_more = possibilities.len().saturating_sub(n_possibilities); + let possibilities = possibilities[..n_possibilities].join("`, `"); + + let mut note = String::with_capacity(50 + possibilities.len()); + + write!(&mut note, "expected {type_}").unwrap(); + if let Some(name) = name { + write!(&mut note, " for `{name}`").unwrap(); + } + write!(&mut note, " are: {suffix}`{possibilities}`").unwrap(); + if and_more > 0 { + write!(&mut note, " and {and_more} more").unwrap(); + } + + note +} + +pub(super) fn unexpected_cfg_name( + sess: &Session, + diag: &mut Diag<'_, ()>, + (name, name_span): (Symbol, Span), + value: Option<(Symbol, Span)>, +) { + #[allow(rustc::potential_query_instability)] + let possibilities: Vec = sess.psess.check_config.expecteds.keys().copied().collect(); + + let mut names_possibilities: Vec<_> = if value.is_none() { + // We later sort and display all the possibilities, so the order here does not matter. + #[allow(rustc::potential_query_instability)] + sess.psess + .check_config + .expecteds + .iter() + .filter_map(|(k, v)| match v { + ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k), + _ => None, + }) + .collect() + } else { + Vec::new() + }; + + let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); + let mut is_feature_cfg = name == sym::feature; + + if is_feature_cfg && is_from_cargo { + diag.help("consider defining some features in `Cargo.toml`"); + // Suggest the most probable if we found one + } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { + if let Some(ExpectedValues::Some(best_match_values)) = + sess.psess.check_config.expecteds.get(&best_match) + { + // We will soon sort, so the initial order does not matter. + #[allow(rustc::potential_query_instability)] + let mut possibilities = + best_match_values.iter().flatten().map(Symbol::as_str).collect::>(); + possibilities.sort(); + + let mut should_print_possibilities = true; + if let Some((value, value_span)) = value { + if best_match_values.contains(&Some(value)) { + diag.span_suggestion( + name_span, + "there is a config with a similar name and value", + best_match, + Applicability::MaybeIncorrect, + ); + should_print_possibilities = false; + } else if best_match_values.contains(&None) { + diag.span_suggestion( + name_span.to(value_span), + "there is a config with a similar name and no value", + best_match, + Applicability::MaybeIncorrect, + ); + should_print_possibilities = false; + } else if let Some(first_value) = possibilities.first() { + diag.span_suggestion( + name_span.to(value_span), + "there is a config with a similar name and different values", + format!("{best_match} = \"{first_value}\""), + Applicability::MaybeIncorrect, + ); + } else { + diag.span_suggestion( + name_span.to(value_span), + "there is a config with a similar name and different values", + best_match, + Applicability::MaybeIncorrect, + ); + }; + } else { + diag.span_suggestion( + name_span, + "there is a config with a similar name", + best_match, + Applicability::MaybeIncorrect, + ); + } + + if !possibilities.is_empty() && should_print_possibilities { + let possibilities = possibilities.join("`, `"); + diag.help(format!("expected values for `{best_match}` are: `{possibilities}`")); + } + } else { + diag.span_suggestion( + name_span, + "there is a config with a similar name", + best_match, + Applicability::MaybeIncorrect, + ); + } + + is_feature_cfg |= best_match == sym::feature; + } else { + if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { + names_possibilities.sort(); + for cfg_name in names_possibilities.iter() { + diag.span_suggestion( + name_span, + "found config with similar value", + format!("{cfg_name} = \"{name}\""), + Applicability::MaybeIncorrect, + ); + } + } + if !possibilities.is_empty() { + diag.help_once(check_cfg_expected_note(sess, &possibilities, "names", None, "")); + } + } + + let inst = if let Some((value, _value_span)) = value { + let pre = if is_from_cargo { "\\" } else { "" }; + format!("cfg({name}, values({pre}\"{value}{pre}\"))") + } else { + format!("cfg({name})") + }; + + if is_from_cargo { + if !is_feature_cfg { + diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); + } + diag.note("see for more information about checking conditional configuration"); + } else { + diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); + diag.note("see for more information about checking conditional configuration"); + } +} + +pub(super) fn unexpected_cfg_value( + sess: &Session, + diag: &mut Diag<'_, ()>, + (name, name_span): (Symbol, Span), + value: Option<(Symbol, Span)>, +) { + let Some(ExpectedValues::Some(values)) = &sess.psess.check_config.expecteds.get(&name) else { + bug!( + "it shouldn't be possible to have a diagnostic on a value whose name is not in values" + ); + }; + let mut have_none_possibility = false; + // We later sort possibilities if it is not empty, so the + // order here does not matter. + #[allow(rustc::potential_query_instability)] + let possibilities: Vec = values + .iter() + .inspect(|a| have_none_possibility |= a.is_none()) + .copied() + .flatten() + .collect(); + let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); + + // Show the full list if all possible values for a given name, but don't do it + // for names as the possibilities could be very long + if !possibilities.is_empty() { + diag.note(check_cfg_expected_note( + sess, + &possibilities, + "values", + Some(name), + if have_none_possibility { "(none), " } else { "" }, + )); + + if let Some((value, value_span)) = value { + // Suggest the most probable if we found one + if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { + diag.span_suggestion( + value_span, + "there is a expected value with a similar name", + format!("\"{best_match}\""), + Applicability::MaybeIncorrect, + ); + } + } else if let &[first_possibility] = &possibilities[..] { + diag.span_suggestion( + name_span.shrink_to_hi(), + "specify a config value", + format!(" = \"{first_possibility}\""), + Applicability::MaybeIncorrect, + ); + } + } else if have_none_possibility { + diag.note(format!("no expected value for `{name}`")); + if let Some((_value, value_span)) = value { + diag.span_suggestion( + name_span.shrink_to_hi().to(value_span), + "remove the value", + "", + Applicability::MaybeIncorrect, + ); + } + } else { + diag.note(format!("no expected values for `{name}`")); + + let sp = if let Some((_value, value_span)) = value { + name_span.to(value_span) + } else { + name_span + }; + diag.span_suggestion(sp, "remove the condition", "", Applicability::MaybeIncorrect); + } + + // We don't want to suggest adding values to well known names + // since those are defined by rustc it-self. Users can still + // do it if they want, but should not encourage them. + let is_cfg_a_well_know_name = sess.psess.check_config.well_known_names.contains(&name); + + let inst = if let Some((value, _value_span)) = value { + let pre = if is_from_cargo { "\\" } else { "" }; + format!("cfg({name}, values({pre}\"{value}{pre}\"))") + } else { + format!("cfg({name})") + }; + + if is_from_cargo { + if name == sym::feature { + if let Some((value, _value_span)) = value { + diag.help(format!("consider adding `{value}` as a feature in `Cargo.toml`")); + } else { + diag.help("consider defining some features in `Cargo.toml`"); + } + } else if !is_cfg_a_well_know_name { + diag.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); + } + diag.note("see for more information about checking conditional configuration"); + } else { + if !is_cfg_a_well_know_name { + diag.help(format!("to expect this configuration use `--check-cfg={inst}`")); + } + diag.note("see for more information about checking conditional configuration"); + } +} diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index 9bc81d2c46aa1..b671dfaa0d1b0 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -6,6 +6,7 @@ use crate::{ use rustc_hir as hir; use rustc_middle::ty; use rustc_session::lint::FutureIncompatibilityReason; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; use rustc_trait_selection::traits::supertraits; diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index 78ac7f9b2354d..60b799f3c7468 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -1,5 +1,6 @@ use rustc_hir::{Arm, Expr, ExprKind, Node}; use rustc_middle::ty; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; use crate::{ diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index d78ec8c0dd348..3f627baf7704f 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -73,10 +73,10 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { self.inlined_check_id(id); debug!("early context: enter_attrs({:?})", attrs); - lint_callback!(self, enter_lint_attrs, attrs); + lint_callback!(self, check_attributes, attrs); ensure_sufficient_stack(|| f(self)); debug!("early context: exit_attrs({:?})", attrs); - lint_callback!(self, exit_lint_attrs, attrs); + lint_callback!(self, check_attributes_post, attrs); self.context.builder.pop(push); } } @@ -99,7 +99,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) { self.with_lint_attrs(it.id, &it.attrs, |cx| { - ast_visit::walk_foreign_item(cx, it); + ast_visit::walk_item(cx, it); }) } diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index a67f1b62fb0d6..958da177eda5d 100644 --- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs +++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs @@ -5,6 +5,7 @@ use crate::{ }; use rustc_hir as hir; use rustc_middle::ty::{visit::TypeVisitableExt, Ty}; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{symbol::sym, Span}; declare_lint! { diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index ee99e824a548b..c23d1221bc866 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -27,7 +27,7 @@ impl Subdiagnostic for OverruledAttributeSub { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { match self { OverruledAttributeSub::DefaultSource { id } => { diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index cb7feea16b538..ce3f45a17e9f6 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -10,6 +10,7 @@ use hir::{Expr, Pat}; use rustc_hir as hir; use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause}; use rustc_middle::ty::{self, List}; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::ObligationCtxt; diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index b995f38f23c6e..48e5683fc0a9b 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -5,6 +5,7 @@ use rustc_hir::def::DefKind; use rustc_middle::query::Providers; use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_session::declare_lint; use rustc_span::{sym, Span, Symbol}; use rustc_target::abi::FIRST_VARIANT; @@ -322,10 +323,10 @@ fn structurally_same_type_impl<'tcx>( (Slice(a_ty), Slice(b_ty)) => { structurally_same_type_impl(seen_types, tcx, param_env, *a_ty, *b_ty, ckind) } - (RawPtr(a_tymut), RawPtr(b_tymut)) => { - a_tymut.mutbl == b_tymut.mutbl + (RawPtr(a_ty, a_mutbl), RawPtr(b_ty, b_mutbl)) => { + a_mutbl == b_mutbl && structurally_same_type_impl( - seen_types, tcx, param_env, a_tymut.ty, b_tymut.ty, ckind, + seen_types, tcx, param_env, *a_ty, *b_ty, ckind, ) } (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 054cfe92a3a48..aa8ca1776dc03 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -7,6 +7,7 @@ use crate::{ }; use ast::util::unicode::{contains_text_flow_control_chars, TEXT_FLOW_CONTROL_CHARS}; use rustc_ast as ast; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{BytePos, Span, Symbol}; declare_lint! { diff --git a/compiler/rustc_lint/src/invalid_from_utf8.rs b/compiler/rustc_lint/src/invalid_from_utf8.rs index 081e3e875302b..0081374922e6c 100644 --- a/compiler/rustc_lint/src/invalid_from_utf8.rs +++ b/compiler/rustc_lint/src/invalid_from_utf8.rs @@ -2,6 +2,7 @@ use std::str::Utf8Error; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::sym; diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 506716e39a1e8..ca188277b9dee 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -15,12 +15,12 @@ //! for all lint attributes. use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; -use rustc_ast as ast; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::{join, Lrc}; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::intravisit as hir_visit; +use rustc_hir::HirId; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::LintPass; @@ -54,7 +54,7 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. - fn with_lint_attrs(&mut self, id: hir::HirId, f: F) + fn with_lint_attrs(&mut self, id: HirId, f: F) where F: FnOnce(&mut Self), { @@ -62,13 +62,13 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { let prev = self.context.last_node_with_lint_attrs; self.context.last_node_with_lint_attrs = id; debug!("late context: enter_attrs({:?})", attrs); - lint_callback!(self, enter_lint_attrs, attrs); + lint_callback!(self, check_attributes, attrs); for attr in attrs { lint_callback!(self, check_attribute, attr); } f(self); debug!("late context: exit_attrs({:?})", attrs); - lint_callback!(self, exit_lint_attrs, attrs); + lint_callback!(self, check_attributes_post, attrs); self.context.last_node_with_lint_attrs = prev; } @@ -82,7 +82,7 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { self.context.param_env = old_param_env; } - fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, n: hir::HirId) { + fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, n: HirId) { lint_callback!(self, check_mod, m, n); hir_visit::walk_mod(self, m, n); } @@ -232,13 +232,13 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_inf(self, inf); } - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, n: hir::HirId) { + fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, n: HirId) { if !self.context.only_module { self.process_mod(m, n); } } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.with_lint_attrs(l.hir_id, |cx| { lint_callback!(cx, check_local, l); hir_visit::walk_local(cx, l); @@ -306,14 +306,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_lifetime(self, lt); } - fn visit_path(&mut self, p: &hir::Path<'tcx>, id: hir::HirId) { + fn visit_path(&mut self, p: &hir::Path<'tcx>, id: HirId) { lint_callback!(self, check_path, p, id); hir_visit::walk_path(self, p); } - - fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) { - lint_callback!(self, check_attribute, attr); - } } // Combines multiple lint passes into a single pass, at runtime. Each diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index de642f373b5c4..ea82fb9f26252 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -5,6 +5,7 @@ use crate::{ use rustc_errors::MultiSpan; use rustc_hir as hir; use rustc_middle::ty; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{sym, Symbol}; declare_lint! { @@ -105,7 +106,7 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable - fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { return; } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 95f312a31b332..12b3d1d2f9e4f 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -22,6 +22,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; use rustc_index::IndexVec; +use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::lint::{ lint_level, reveal_actual_level, LevelAndSource, LintExpectation, LintLevelSource, @@ -191,7 +192,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe levels.add_id(hir::CRATE_HIR_ID); levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID) } - hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), + hir::OwnerNode::Synthetic => unreachable!(), }, } @@ -354,7 +355,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { intravisit::walk_variant(self, v); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.add_id(l.hir_id); intravisit::walk_local(self, l); } @@ -428,7 +429,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<' intravisit::walk_variant(self, v); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.add_id(l.hir_id); intravisit::walk_local(self, l); } @@ -1078,6 +1079,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { feature, GateIssue::Language, lint_from_cli, + None, ); }, ); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 31c80c4d75bb1..a78b410f500c1 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -32,7 +32,6 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extract_if)] -#![feature(generic_nonzero)] #![feature(if_let_guard)] #![feature(iter_order_by)] #![feature(let_chains)] @@ -40,10 +39,6 @@ #![feature(rustc_attrs)] #![allow(internal_features)] -#[macro_use] -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; #[macro_use] extern crate tracing; @@ -313,6 +308,8 @@ fn register_builtins(store: &mut LintStore) { // MACRO_USE_EXTERN_CRATE ); + add_lint_group!("keyword_idents", KEYWORD_IDENTS_2018, KEYWORD_IDENTS_2024); + add_lint_group!( "refining_impl_trait", REFINING_IMPL_TRAIT_REACHABLE, @@ -325,7 +322,7 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("bare_trait_object", "bare_trait_objects"); store.register_renamed("unstable_name_collision", "unstable_name_collisions"); store.register_renamed("unused_doc_comment", "unused_doc_comments"); - store.register_renamed("async_idents", "keyword_idents"); + store.register_renamed("async_idents", "keyword_idents_2018"); store.register_renamed("exceeding_bitshifts", "arithmetic_overflow"); store.register_renamed("redundant_semicolon", "redundant_semicolons"); store.register_renamed("overlapping_patterns", "overlapping_range_endpoints"); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1dac2d89c6bc3..7efa5245baa20 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -274,7 +274,7 @@ impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { // Access to associates types should use `::Assoc`, which does not need a // bound. Let's see if this type does that. @@ -330,7 +330,7 @@ impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { diag.multipart_suggestion( fluent::lint_suggestion, @@ -451,7 +451,7 @@ impl Subdiagnostic for BuiltinUnpermittedTypeInitSub { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { let mut err = self.err; loop { @@ -506,7 +506,7 @@ impl Subdiagnostic for BuiltinClashingExternSub<'_> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { let mut expected_str = DiagStyledString::new(); expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false); @@ -788,7 +788,7 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { for (c, span) in self.spans { diag.span_label(span, format!("{c:?}")); @@ -806,7 +806,7 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { match self { HiddenUnicodeCodepointsDiagSub::Escape { spans } => { @@ -954,7 +954,7 @@ impl Subdiagnostic for NonBindingLetSub { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar; @@ -1240,7 +1240,7 @@ impl Subdiagnostic for NonSnakeCaseDiagSub { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { match self { NonSnakeCaseDiagSub::Label { span } => { @@ -1350,14 +1350,18 @@ pub enum NonLocalDefinitionsDiag { const_anon: Option, }, #[diag(lint_non_local_definitions_macro_rules)] - #[help] - #[note(lint_non_local)] - #[note(lint_exception)] - #[note(lint_non_local_definitions_deprecation)] MacroRules { depth: u32, body_kind_descr: &'static str, body_name: String, + #[help] + help: Option<()>, + #[help(lint_help_doctest)] + doctest_help: Option<()>, + #[note(lint_non_local)] + #[note(lint_exception)] + #[note(lint_non_local_definitions_deprecation)] + notes: (), #[subdiagnostic] cargo_update: Option, }, @@ -1482,7 +1486,7 @@ impl Subdiagnostic for OverflowingBinHexSign { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { match self { OverflowingBinHexSign::Positive => { @@ -1632,11 +1636,13 @@ pub struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> { pub ne: &'a str, pub deref_left: &'a str, pub deref_right: &'a str, + pub l_modifiers: &'a str, + pub r_modifiers: &'a str, #[suggestion_part(code = "{ne}std::ptr::eq({deref_left}")] pub left: Span, - #[suggestion_part(code = ", {deref_right}")] + #[suggestion_part(code = "{l_modifiers}, {deref_right}")] pub middle: Span, - #[suggestion_part(code = ")")] + #[suggestion_part(code = "{r_modifiers})")] pub right: Span, } @@ -1652,11 +1658,13 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { ne: &'a str, deref_left: &'a str, deref_right: &'a str, + l_modifiers: &'a str, + r_modifiers: &'a str, #[suggestion_part(code = "{ne}std::ptr::addr_eq({deref_left}")] left: Span, - #[suggestion_part(code = ", {deref_right}")] + #[suggestion_part(code = "{l_modifiers}, {deref_right}")] middle: Span, - #[suggestion_part(code = ")")] + #[suggestion_part(code = "{r_modifiers})")] right: Span, }, #[multipart_suggestion( @@ -1668,14 +1676,18 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { Cast { deref_left: &'a str, deref_right: &'a str, - #[suggestion_part(code = "{deref_left}")] + paren_left: &'a str, + paren_right: &'a str, + l_modifiers: &'a str, + r_modifiers: &'a str, + #[suggestion_part(code = "({deref_left}")] left_before: Option, - #[suggestion_part(code = " as *const ()")] - left: Span, - #[suggestion_part(code = "{deref_right}")] + #[suggestion_part(code = "{l_modifiers}{paren_left}.cast::<()>()")] + left_after: Span, + #[suggestion_part(code = "({deref_right}")] right_before: Option, - #[suggestion_part(code = " as *const ()")] - right: Span, + #[suggestion_part(code = "{r_modifiers}{paren_right}.cast::<()>()")] + right_after: Span, }, } diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs index 07980cfb6fa0f..e355604e2068c 100644 --- a/compiler/rustc_lint/src/map_unit_fn.rs +++ b/compiler/rustc_lint/src/map_unit_fn.rs @@ -6,6 +6,7 @@ use rustc_middle::{ query::Key, ty::{self, Ty}, }; +use rustc_session::{declare_lint, declare_lint_pass}; declare_lint! { /// The `map_unit_fn` lint checks for `Iterator::map` receive diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs index 9cfdaf0ce2ff6..b93245d58d9d8 100644 --- a/compiler/rustc_lint/src/methods.rs +++ b/compiler/rustc_lint/src/methods.rs @@ -4,6 +4,7 @@ use crate::LateLintPass; use crate::LintContext; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{symbol::sym, Span}; declare_lint! { diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 69d623b547b58..48d140c6b7f36 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -1,6 +1,7 @@ use crate::{LateContext, LateLintPass, LintContext}; use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; declare_lint! { diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 79bc78ae55a8d..9f298a6071c69 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -6,6 +6,7 @@ use crate::{EarlyContext, EarlyLintPass, LintContext}; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordMap; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::symbol::Symbol; use unicode_security::general_security_profile::IdentifierType; diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index a2d07fff5067c..a6057afcbd6b5 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -4,10 +4,12 @@ use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::bug; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_parse_format::{ParseMode, Parser, Piece}; use rustc_session::lint::FutureIncompatibilityReason; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::edition::Edition; use rustc_span::{hygiene, sym, symbol::kw, InnerSpan, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; @@ -53,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for NonPanicFmt { if Some(def_id) == cx.tcx.lang_items().begin_panic_fn() || Some(def_id) == cx.tcx.lang_items().panic_fn() - || f_diagnostic_name == Some(sym::panic_str) + || f_diagnostic_name == Some(sym::panic_str_2015) { if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { if matches!( diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 7c4d92d3ce038..7bdf5ef6af406 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,6 +1,18 @@ -use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, Path, QPath, TyKind}; +use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, TyKind}; +use rustc_hir::{Path, QPath}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::{Obligation, ObligationCause}; +use rustc_middle::ty::{self, Binder, Ty, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_middle::ty::{EarlyBinder, TraitRef, TypeSuperFoldable}; +use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::Span; use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind}; +use rustc_trait_selection::infer::TyCtxtInferExt; +use rustc_trait_selection::traits::error_reporting::ambiguity::{ + compute_applicable_impls_for_diagnostics, CandidateSource, +}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext}; @@ -35,7 +47,7 @@ declare_lint! { /// All nested bodies (functions, enum discriminant, array length, consts) (expect for /// `const _: Ty = { ... }` in top-level module, which is still undecided) are checked. pub NON_LOCAL_DEFINITIONS, - Allow, + Warn, "checks for non-local definitions", report_in_external_macro } @@ -66,7 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { return; } - let parent = cx.tcx.parent(item.owner_id.def_id.into()); + let def_id = item.owner_id.def_id.into(); + let parent = cx.tcx.parent(def_id); let parent_def_kind = cx.tcx.def_kind(parent); let parent_opt_item_name = cx.tcx.opt_item_name(parent); @@ -121,6 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { None }; + // Part 1: Is the Self type local? let self_ty_has_local_parent = match impl_.self_ty.kind { TyKind::Path(QPath::Resolved(_, ty_path)) => { path_has_local_parent(ty_path, cx, parent, parent_parent) @@ -143,6 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { | TyKind::Never | TyKind::Tup(_) | TyKind::Path(_) + | TyKind::Pat(..) | TyKind::AnonAdt(_) | TyKind::OpaqueDef(_, _, _) | TyKind::Typeof(_) @@ -150,45 +165,80 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { | TyKind::Err(_) => false, }; + if self_ty_has_local_parent { + return; + } + + // Part 2: Is the Trait local? let of_trait_has_local_parent = impl_ .of_trait .map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent)) .unwrap_or(false); - // If none of them have a local parent (LOGICAL NOR) this means that - // this impl definition is a non-local definition and so we lint on it. - if !(self_ty_has_local_parent || of_trait_has_local_parent) { - let const_anon = if self.body_depth == 1 - && parent_def_kind == DefKind::Const - && parent_opt_item_name != Some(kw::Underscore) - && let Some(parent) = parent.as_local() - && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) - && let ItemKind::Const(ty, _, _) = item.kind - && let TyKind::Tup(&[]) = ty.kind - { - Some(item.ident.span) - } else { - None - }; - - cx.emit_span_lint( - NON_LOCAL_DEFINITIONS, - item.span, - NonLocalDefinitionsDiag::Impl { - depth: self.body_depth, - body_kind_descr: cx.tcx.def_kind_descr(parent_def_kind, parent), - body_name: parent_opt_item_name - .map(|s| s.to_ident_string()) - .unwrap_or_else(|| "".to_string()), - cargo_update: cargo_update(), - const_anon, - }, - ) + if of_trait_has_local_parent { + return; + } + + // Part 3: Is the impl definition leaking outside it's defining scope? + // + // We always consider inherent impls to be leaking. + let impl_has_enough_non_local_candidates = cx + .tcx + .impl_trait_ref(def_id) + .map(|binder| { + impl_trait_ref_has_enough_non_local_candidates( + cx.tcx, + item.span, + def_id, + binder, + |did| did_has_local_parent(did, cx.tcx, parent, parent_parent), + ) + }) + .unwrap_or(false); + + if impl_has_enough_non_local_candidates { + return; } + + // Get the span of the parent const item ident (if it's a not a const anon). + // + // Used to suggest changing the const item to a const anon. + let span_for_const_anon_suggestion = if self.body_depth == 1 + && parent_def_kind == DefKind::Const + && parent_opt_item_name != Some(kw::Underscore) + && let Some(parent) = parent.as_local() + && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent) + && let ItemKind::Const(ty, _, _) = item.kind + && let TyKind::Tup(&[]) = ty.kind + { + Some(item.ident.span) + } else { + None + }; + + cx.emit_span_lint( + NON_LOCAL_DEFINITIONS, + item.span, + NonLocalDefinitionsDiag::Impl { + depth: self.body_depth, + body_kind_descr: cx.tcx.def_kind_descr(parent_def_kind, parent), + body_name: parent_opt_item_name + .map(|s| s.to_ident_string()) + .unwrap_or_else(|| "".to_string()), + cargo_update: cargo_update(), + const_anon: span_for_const_anon_suggestion, + }, + ) } ItemKind::Macro(_macro, MacroKind::Bang) if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => { + // determining we if are in a doctest context can't currently be determined + // by the code it-self (no specific attrs), but fortunatly rustdoc sets a + // perma-unstable env for libtest so we just re-use that env for now + let is_at_toplevel_doctest = + self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok(); + cx.emit_span_lint( NON_LOCAL_DEFINITIONS, item.span, @@ -199,6 +249,9 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { .map(|s| s.to_ident_string()) .unwrap_or_else(|| "".to_string()), cargo_update: cargo_update(), + help: (!is_at_toplevel_doctest).then_some(()), + doctest_help: is_at_toplevel_doctest.then_some(()), + notes: (), }, ) } @@ -207,6 +260,90 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { } } +// Detecting if the impl definition is leaking outside of it's defining scope. +// +// Rule: for each impl, instantiate all local types with inference vars and +// then assemble candidates for that goal, if there are more than 1 (non-private +// impls), it does not leak. +// +// https://github.com/rust-lang/rust/issues/121621#issuecomment-1976826895 +fn impl_trait_ref_has_enough_non_local_candidates<'tcx>( + tcx: TyCtxt<'tcx>, + infer_span: Span, + trait_def_id: DefId, + binder: EarlyBinder>, + mut did_has_local_parent: impl FnMut(DefId) -> bool, +) -> bool { + let infcx = tcx + .infer_ctxt() + // We use the new trait solver since the obligation we are trying to + // prove here may overflow and those are fatal in the old trait solver. + // Which is unacceptable for a lint. + // + // Thanksfully the part we use here are very similar to the + // new-trait-solver-as-coherence, which is in stabilization. + // + // https://github.com/rust-lang/rust/issues/123573 + .with_next_trait_solver(true) + .build(); + + let trait_ref = binder.instantiate(tcx, infcx.fresh_args_for_item(infer_span, trait_def_id)); + + let trait_ref = trait_ref.fold_with(&mut ReplaceLocalTypesWithInfer { + infcx: &infcx, + infer_span, + did_has_local_parent: &mut did_has_local_parent, + }); + + let poly_trait_obligation = Obligation::new( + tcx, + ObligationCause::dummy(), + ty::ParamEnv::empty(), + Binder::dummy(trait_ref), + ); + + let ambiguities = compute_applicable_impls_for_diagnostics(&infcx, &poly_trait_obligation); + + let mut it = ambiguities.iter().filter(|ambi| match ambi { + CandidateSource::DefId(did) => !did_has_local_parent(*did), + CandidateSource::ParamEnv(_) => unreachable!(), + }); + + let _ = it.next(); + it.next().is_some() +} + +/// Replace every local type by inference variable. +/// +/// ```text +/// as std::cmp::PartialEq>> +/// to +/// as std::cmp::PartialEq>> +/// ``` +struct ReplaceLocalTypesWithInfer<'a, 'tcx, F: FnMut(DefId) -> bool> { + infcx: &'a InferCtxt<'tcx>, + did_has_local_parent: F, + infer_span: Span, +} + +impl<'a, 'tcx, F: FnMut(DefId) -> bool> TypeFolder> + for ReplaceLocalTypesWithInfer<'a, 'tcx, F> +{ + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if let Some(def) = t.ty_adt_def() + && (self.did_has_local_parent)(def.did()) + { + self.infcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span: self.infer_span }) + } else { + t.super_fold_with(self) + } + } +} + /// Given a path and a parent impl def id, this checks if the if parent resolution /// def id correspond to the def id of the parent impl definition. /// @@ -216,16 +353,35 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { /// std::convert::PartialEq> /// ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` +#[inline] fn path_has_local_parent( path: &Path<'_>, cx: &LateContext<'_>, impl_parent: DefId, impl_parent_parent: Option, ) -> bool { - path.res.opt_def_id().is_some_and(|did| { - did.is_local() && { - let res_parent = cx.tcx.parent(did); - res_parent == impl_parent || Some(res_parent) == impl_parent_parent + path.res + .opt_def_id() + .is_some_and(|did| did_has_local_parent(did, cx.tcx, impl_parent, impl_parent_parent)) +} + +/// Given a def id and a parent impl def id, this checks if the parent +/// def id (modulo modules) correspond to the def id of the parent impl definition. +#[inline] +fn did_has_local_parent( + did: DefId, + tcx: TyCtxt<'_>, + impl_parent: DefId, + impl_parent_parent: Option, +) -> bool { + did.is_local() + && if let Some(did_parent) = tcx.opt_parent(did) { + did_parent == impl_parent + || Some(did_parent) == impl_parent_parent + || !did_parent.is_crate_root() + && tcx.def_kind(did_parent) == DefKind::Mod + && did_has_local_parent(did_parent, tcx, impl_parent, impl_parent_parent) + } else { + false } - }) } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index ee863672017d5..dbb0644cd63f6 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -11,6 +11,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{GenericParamKind, PatKind}; use rustc_middle::ty; use rustc_session::config::CrateType; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::{BytePos, Span}; diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 970d411fb0657..91441248e70fd 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -8,6 +8,7 @@ use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::symbol::sym; declare_lint! { diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index cf825be7a55ea..1ea1f496e508e 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -4,6 +4,7 @@ use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{ self, fold::BottomUpFolder, print::TraitPredPrintModifiersAndPath, Ty, TypeFoldable, }; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{symbol::kw, Span}; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -20,7 +21,7 @@ declare_lint! { /// This functionality was removed in #97346, but then rolled back in #99860 /// because it caused regressions. /// - /// We plan on reintroducing this as a hard error, but in the mean time, + /// We plan on reintroducing this as a hard error, but in the meantime, /// this lint serves to warn and suggest fixes for any use-cases which rely /// on this behavior. /// diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 160d42caa9e2a..1f0a8a0b56783 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; declare_tool_lint! { diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 508f3e1ec3106..d8ba84eb7a1a8 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -15,7 +15,7 @@ macro_rules! late_lint_methods { fn check_foreign_item(a: &'tcx rustc_hir::ForeignItem<'tcx>); fn check_item(a: &'tcx rustc_hir::Item<'tcx>); fn check_item_post(a: &'tcx rustc_hir::Item<'tcx>); - fn check_local(a: &'tcx rustc_hir::Local<'tcx>); + fn check_local(a: &'tcx rustc_hir::LetStmt<'tcx>); fn check_block(a: &'tcx rustc_hir::Block<'tcx>); fn check_block_post(a: &'tcx rustc_hir::Block<'tcx>); fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>); @@ -41,13 +41,8 @@ macro_rules! late_lint_methods { fn check_variant(a: &'tcx rustc_hir::Variant<'tcx>); fn check_path(a: &rustc_hir::Path<'tcx>, b: rustc_hir::HirId); fn check_attribute(a: &'tcx rustc_ast::Attribute); - - /// Called when entering a syntax node that can have lint attributes such - /// as `#[allow(...)]`. Called with *all* the attributes of that node. - fn enter_lint_attrs(a: &'tcx [rustc_ast::Attribute]); - - /// Counterpart to `enter_lint_attrs`. - fn exit_lint_attrs(a: &'tcx [rustc_ast::Attribute]); + fn check_attributes(a: &'tcx [rustc_ast::Attribute]); + fn check_attributes_post(a: &'tcx [rustc_ast::Attribute]); ]); ) } @@ -162,16 +157,11 @@ macro_rules! early_lint_methods { fn check_impl_item(a: &rustc_ast::AssocItem); fn check_variant(a: &rustc_ast::Variant); fn check_attribute(a: &rustc_ast::Attribute); + fn check_attributes(a: &[rustc_ast::Attribute]); + fn check_attributes_post(a: &[rustc_ast::Attribute]); fn check_mac_def(a: &rustc_ast::MacroDef); fn check_mac(a: &rustc_ast::MacCall); - /// Called when entering a syntax node that can have lint attributes such - /// as `#[allow(...)]`. Called with *all* the attributes of that node. - fn enter_lint_attrs(a: &[rustc_ast::Attribute]); - - /// Counterpart to `enter_lint_attrs`. - fn exit_lint_attrs(a: &[rustc_ast::Attribute]); - fn enter_where_predicate(a: &rustc_ast::WherePredicate); fn exit_where_predicate(a: &rustc_ast::WherePredicate); ]); diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index 1681ac2f1e5a4..ef08e79e24a0a 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -1,5 +1,6 @@ use crate::{lints::RedundantSemicolonsDiag, EarlyContext, EarlyLintPass, LintContext}; use rustc_ast::{Block, StmtKind}; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; declare_lint! { diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index f386db9d8dbc9..9fed91f72628e 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -1,7 +1,8 @@ use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_middle::ty::layout::LayoutOf as _; -use rustc_middle::ty::{self, layout::TyAndLayout, TypeAndMut}; +use rustc_middle::ty::{self, layout::TyAndLayout}; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext}; @@ -153,7 +154,7 @@ fn is_cast_from_ref_to_mut_ptr<'tcx>( let end_ty = cx.typeck_results().node_type(orig_expr.hir_id); // Bail out early if the end type is **not** a mutable pointer. - if !matches!(end_ty.kind(), ty::RawPtr(TypeAndMut { ty: _, mutbl: Mutability::Mut })) { + if !matches!(end_ty.kind(), ty::RawPtr(_, Mutability::Mut)) { return None; } @@ -183,7 +184,7 @@ fn is_cast_to_bigger_memory_layout<'tcx>( ) -> Option<(TyAndLayout<'tcx>, TyAndLayout<'tcx>, Expr<'tcx>)> { let end_ty = cx.typeck_results().node_type(orig_expr.hir_id); - let ty::RawPtr(TypeAndMut { ty: inner_end_ty, mutbl: _ }) = end_ty.kind() else { + let ty::RawPtr(inner_end_ty, _) = end_ty.kind() else { return None; }; diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 789f154eac5bc..4d1b1dc4fb783 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -3,6 +3,7 @@ use crate::LateContext; use crate::LateLintPass; use crate::LintContext; use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::symbol::sym; declare_lint! { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 51fe08d4af1a9..13b6054586cd5 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -17,11 +17,13 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir as hir; use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; +use rustc_middle::bug; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; +use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{ self, AdtKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -use rustc_middle::ty::{GenericArgsRef, TypeAndMut}; +use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map; use rustc_span::symbol::sym; @@ -657,14 +659,24 @@ fn lint_nan<'tcx>( cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint); } +#[derive(Debug, PartialEq)] +enum ComparisonOp { + BinOp(hir::BinOpKind), + Other, +} + fn lint_wide_pointer<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, - binop: hir::BinOpKind, + cmpop: ComparisonOp, l: &'tcx hir::Expr<'tcx>, r: &'tcx hir::Expr<'tcx>, ) { - let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(usize, bool)> { + let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<( + /* number of refs */ usize, + /* modifiers */ String, + /* is dyn */ bool, + )> { let mut refs = 0; // here we remove any "implicit" references and count the number // of them to correctly suggest the right number of deref @@ -672,14 +684,23 @@ fn lint_wide_pointer<'tcx>( ty = *inner_ty; refs += 1; } - match ty.kind() { - ty::RawPtr(TypeAndMut { mutbl: _, ty }) => (!ty.is_sized(cx.tcx, cx.param_env)) - .then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))), - _ => None, - } + + // get the inner type of a pointer (or akin) + let mut modifiers = String::new(); + ty = match ty.kind() { + ty::RawPtr(ty, _) => *ty, + ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::NonNull, def.did()) => { + modifiers.push_str(".as_ptr()"); + args.type_at(0) + } + _ => return None, + }; + + (!ty.is_sized(cx.tcx, cx.param_env)) + .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))) }; - // PartialEq::{eq,ne} takes references, remove any explicit references + // the left and right operands can have references, remove any explicit references let l = l.peel_borrows(); let r = r.peel_borrows(); @@ -690,10 +711,10 @@ fn lint_wide_pointer<'tcx>( return; }; - let Some((l_ty_refs, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else { + let Some((l_ty_refs, l_modifiers, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else { return; }; - let Some((r_ty_refs, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else { + let Some((r_ty_refs, r_modifiers, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else { return; }; @@ -707,8 +728,8 @@ fn lint_wide_pointer<'tcx>( ); }; - let ne = if binop == hir::BinOpKind::Ne { "!" } else { "" }; - let is_eq_ne = matches!(binop, hir::BinOpKind::Eq | hir::BinOpKind::Ne); + let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" }; + let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne)); let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn; let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo()); @@ -718,6 +739,9 @@ fn lint_wide_pointer<'tcx>( let deref_left = &*"*".repeat(l_ty_refs); let deref_right = &*"*".repeat(r_ty_refs); + let l_modifiers = &*l_modifiers; + let r_modifiers = &*r_modifiers; + cx.emit_span_lint( AMBIGUOUS_WIDE_POINTER_COMPARISONS, e.span, @@ -727,6 +751,8 @@ fn lint_wide_pointer<'tcx>( ne, deref_left, deref_right, + l_modifiers, + r_modifiers, left, middle, right, @@ -737,6 +763,8 @@ fn lint_wide_pointer<'tcx>( ne, deref_left, deref_right, + l_modifiers, + r_modifiers, left, middle, right, @@ -745,12 +773,14 @@ fn lint_wide_pointer<'tcx>( AmbiguousWidePointerComparisonsAddrSuggestion::Cast { deref_left, deref_right, - // those two Options are required for correctness as having - // an empty span and an empty suggestion is not permitted - left_before: (l_ty_refs != 0).then_some(left), - right_before: (r_ty_refs != 0).then(|| r_span.shrink_to_lo()), - left: l_span.shrink_to_hi(), - right, + l_modifiers, + r_modifiers, + paren_left: if l_ty_refs != 0 { ")" } else { "" }, + paren_right: if r_ty_refs != 0 { ")" } else { "" }, + left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()), + left_after: l_span.shrink_to_hi(), + right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()), + right_after: r_span.shrink_to_hi(), } }, }, @@ -773,7 +803,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); } else { lint_nan(cx, e, binop, l, r); - lint_wide_pointer(cx, e, binop.node, l, r); + lint_wide_pointer(cx, e, ComparisonOp::BinOp(binop.node), l, r); } } } @@ -782,16 +812,16 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { if let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) - && let Some(binop) = partialeq_binop(diag_item) => + && let Some(cmpop) = diag_item_cmpop(diag_item) => { - lint_wide_pointer(cx, e, binop, l, r); + lint_wide_pointer(cx, e, cmpop, l, r); } hir::ExprKind::MethodCall(_, l, [r], _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) - && let Some(binop) = partialeq_binop(diag_item) => + && let Some(cmpop) = diag_item_cmpop(diag_item) => { - lint_wide_pointer(cx, e, binop, l, r); + lint_wide_pointer(cx, e, cmpop, l, r); } _ => {} }; @@ -876,14 +906,20 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { ) } - fn partialeq_binop(diag_item: Symbol) -> Option { - if diag_item == sym::cmp_partialeq_eq { - Some(hir::BinOpKind::Eq) - } else if diag_item == sym::cmp_partialeq_ne { - Some(hir::BinOpKind::Ne) - } else { - None - } + fn diag_item_cmpop(diag_item: Symbol) -> Option { + Some(match diag_item { + sym::cmp_ord_max => ComparisonOp::Other, + sym::cmp_ord_min => ComparisonOp::Other, + sym::ord_cmp_method => ComparisonOp::Other, + sym::cmp_partialeq_eq => ComparisonOp::BinOp(hir::BinOpKind::Eq), + sym::cmp_partialeq_ne => ComparisonOp::BinOp(hir::BinOpKind::Ne), + sym::cmp_partialord_cmp => ComparisonOp::Other, + sym::cmp_partialord_ge => ComparisonOp::BinOp(hir::BinOpKind::Ge), + sym::cmp_partialord_gt => ComparisonOp::BinOp(hir::BinOpKind::Gt), + sym::cmp_partialord_le => ComparisonOp::BinOp(hir::BinOpKind::Le), + sym::cmp_partialord_lt => ComparisonOp::BinOp(hir::BinOpKind::Lt), + _ => return None, + }) } } } @@ -985,7 +1021,14 @@ pub fn transparent_newtype_field<'a, 'tcx>( } /// Is type known to be non-null? -fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool { +fn ty_is_known_nonnull<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + mode: CItemKind, +) -> bool { + let ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty); + match ty.kind() { ty::FnPtr(_) => true, ty::Ref(..) => true, @@ -1005,7 +1048,7 @@ fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mode: CItemKind) - def.variants() .iter() .filter_map(|variant| transparent_newtype_field(tcx, variant)) - .any(|field| ty_is_known_nonnull(tcx, field.ty(tcx, args), mode)) + .any(|field| ty_is_known_nonnull(tcx, param_env, field.ty(tcx, args), mode)) } _ => false, } @@ -1013,7 +1056,13 @@ fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mode: CItemKind) - /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type. /// If the type passed in was not scalar, returns None. -fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> { +fn get_nullable_type<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> Option> { + let ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty); + Some(match *ty.kind() { ty::Adt(field_def, field_args) => { let inner_field_ty = { @@ -1029,22 +1078,19 @@ fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> .expect("No non-zst fields in transparent type.") .ty(tcx, field_args) }; - return get_nullable_type(tcx, inner_field_ty); + return get_nullable_type(tcx, param_env, inner_field_ty); } ty::Int(ty) => Ty::new_int(tcx, ty), ty::Uint(ty) => Ty::new_uint(tcx, ty), - ty::RawPtr(ty_mut) => Ty::new_ptr(tcx, ty_mut), + ty::RawPtr(ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl), // As these types are always non-null, the nullable equivalent of - // Option of these types are their raw pointer counterparts. - ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl }), - ty::FnPtr(..) => { - // There is no nullable equivalent for Rust's function pointers -- you - // must use an Option _> to represent it. - ty - } - - // We should only ever reach this case if ty_is_known_nonnull is extended - // to other types. + // `Option` of these types are their raw pointer counterparts. + ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl), + // There is no nullable equivalent for Rust's function pointers, + // you must use an `Option _>` to represent it. + ty::FnPtr(..) => ty, + // We should only ever reach this case if `ty_is_known_nonnull` is + // extended to other types. ref unhandled => { debug!( "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}", @@ -1057,7 +1103,7 @@ fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it /// can, return the type that `ty` can be safely converted to, otherwise return `None`. -/// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`, +/// Currently restricted to function pointers, boxes, references, `core::num::NonZero`, /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. /// FIXME: This duplicates code in codegen. pub(crate) fn repr_nullable_ptr<'tcx>( @@ -1076,7 +1122,7 @@ pub(crate) fn repr_nullable_ptr<'tcx>( _ => return None, }; - if !ty_is_known_nonnull(tcx, field_ty, ckind) { + if !ty_is_known_nonnull(tcx, param_env, field_ty, ckind) { return None; } @@ -1100,10 +1146,10 @@ pub(crate) fn repr_nullable_ptr<'tcx>( WrappingRange { start: 0, end } if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 => { - return Some(get_nullable_type(tcx, field_ty).unwrap()); + return Some(get_nullable_type(tcx, param_env, field_ty).unwrap()); } WrappingRange { start: 1, .. } => { - return Some(get_nullable_type(tcx, field_ty).unwrap()); + return Some(get_nullable_type(tcx, param_env, field_ty).unwrap()); } WrappingRange { start, end } => { unreachable!("Unhandled start and end range: ({}, {})", start, end) @@ -1335,6 +1381,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some(fluent::lint_improper_ctypes_char_help), }, + ty::Pat(..) => FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_pat_reason, + help: Some(fluent::lint_improper_ctypes_pat_help), + }, + ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None } } @@ -1364,7 +1416,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some(fluent::lint_improper_ctypes_tuple_help), }, - ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) + ty::RawPtr(ty, _) | ty::Ref(_, ty, _) if { matches!(self.mode, CItemKind::Definition) && ty.is_sized(self.cx.tcx, self.cx.param_env) @@ -1373,7 +1425,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::RawPtr(ty::TypeAndMut { ty, .. }) + ty::RawPtr(ty, _) if match ty.kind() { ty::Tuple(tuple) => tuple.is_empty(), _ => false, @@ -1382,9 +1434,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { - self.check_type_for_ffi(cache, ty) - } + ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(cache, ty), ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty), diff --git a/compiler/rustc_lint/src/unit_bindings.rs b/compiler/rustc_lint/src/unit_bindings.rs index b74430d8fa09c..8202bfa805a29 100644 --- a/compiler/rustc_lint/src/unit_bindings.rs +++ b/compiler/rustc_lint/src/unit_bindings.rs @@ -1,7 +1,7 @@ use crate::lints::UnitBindingsDiag; use crate::{LateLintPass, LintContext}; use rustc_hir as hir; -use rustc_middle::ty::Ty; +use rustc_session::{declare_lint, declare_lint_pass}; declare_lint! { /// The `unit_bindings` lint detects cases where bindings are useless because they have @@ -46,7 +46,7 @@ declare_lint! { declare_lint_pass!(UnitBindings => [UNIT_BINDINGS]); impl<'tcx> LateLintPass<'tcx> for UnitBindings { - fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { + fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::LetStmt<'tcx>) { // Suppress warning if user: // - explicitly ascribes a type to the pattern // - explicitly wrote `let pat = ();` @@ -56,8 +56,8 @@ impl<'tcx> LateLintPass<'tcx> for UnitBindings { && let Some(init) = local.init && let init_ty = tyck_results.expr_ty(init) && let local_ty = tyck_results.node_type(local.hir_id) - && init_ty == Ty::new_unit(cx.tcx) - && local_ty == Ty::new_unit(cx.tcx) + && init_ty == cx.tcx.types.unit + && local_ty == cx.tcx.types.unit && local.ty.is_none() && !matches!(init.kind, hir::ExprKind::Tup([])) && !matches!(local.pat.kind, hir::PatKind::Tuple([], ..)) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 3e10879e2411a..4d372d612e90d 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -16,6 +16,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::traits::util::elaborate; use rustc_middle::ty::adjustment; use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::symbol::Symbol; use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span}; @@ -176,6 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { | hir::BinOpKind::Shr => Some("bitwise operation"), }, hir::ExprKind::AddrOf(..) => Some("borrow"), + hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"), hir::ExprKind::Unary(..) => Some("unary operation"), _ => None, }; @@ -295,7 +297,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate( cx.tcx, - cx.tcx.explicit_item_bounds(def).instantiate_identity_iter_copied(), + cx.tcx + .explicit_item_super_predicates(def) + .instantiate_identity_iter_copied(), ) // We only care about self bounds for the impl-trait .filter_only_self() @@ -863,7 +867,9 @@ trait UnusedDelimLint { (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true) } - Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { + Match(ref head, _, ast::MatchKind::Prefix) + if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => + { let left = e.span.lo() + rustc_span::BytePos(5); (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true) } @@ -1052,7 +1058,7 @@ impl UnusedParens { avoid_mut: bool, keep_space: (bool, bool), ) { - use ast::{BindingAnnotation, PatKind}; + use ast::{BindingMode, PatKind}; if let PatKind::Paren(inner) = &value.kind { match inner.kind { @@ -1064,16 +1070,20 @@ impl UnusedParens { // Avoid `p0 | .. | pn` if we should. PatKind::Or(..) if avoid_or => return, // Avoid `mut x` and `mut x @ p` if we should: - PatKind::Ident(BindingAnnotation::MUT, ..) if avoid_mut => { + PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => { return; } // 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); } } @@ -1131,7 +1141,7 @@ impl EarlyLintPass for UnusedParens { } return; } - ExprKind::Match(ref _expr, ref arm) => { + ExprKind::Match(ref _expr, ref arm, _) => { for a in arm { if let Some(body) = &a.body { self.check_unused_delims_expr( @@ -1181,7 +1191,7 @@ impl EarlyLintPass for UnusedParens { self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), + Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), @@ -1227,13 +1237,15 @@ impl EarlyLintPass for UnusedParens { ast::TyKind::TraitObject(..) => {} ast::TyKind::BareFn(b) if self.with_self_ty_parens && b.generic_params.len() > 0 => {} - ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} + 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_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9f09f46ea5a9c..c7996c27c2f09 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -38,6 +38,7 @@ declare_lint_pass! { DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DEPRECATED_IN_FUTURE, DEPRECATED_WHERE_CLAUSE_LOCATION, + DEREFERENCING_MUT_BINDING, DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, @@ -68,6 +69,7 @@ declare_lint_pass! { MISSING_FRAGMENT_SPECIFIER, MUST_NOT_SUSPEND, NAMED_ARGUMENTS_USED_POSITIONALLY, + NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, NON_CONTIGUOUS_RANGE_ENDPOINTS, NON_EXHAUSTIVE_OMITTED_PATTERNS, ORDER_DEPENDENT_TRAIT_OBJECTS, @@ -79,6 +81,7 @@ declare_lint_pass! { PROC_MACRO_BACK_COMPAT, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, PUB_USE_OF_PRIVATE_EXTERN_CRATE, + REDUNDANT_LIFETIMES, REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE, RENAMED_AND_REMOVED_LINTS, @@ -99,6 +102,7 @@ declare_lint_pass! { TYVAR_BEHIND_RAW_POINTER, UNCONDITIONAL_PANIC, UNCONDITIONAL_RECURSION, + UNCOVERED_PARAM_IN_PROJECTION, UNDEFINED_NAKED_FUNCTION_ABI, UNEXPECTED_CFGS, UNFULFILLED_LINT_EXPECTATIONS, @@ -1626,6 +1630,42 @@ declare_lint! { "detect mut variables which don't need to be mutable" } +declare_lint! { + /// The `dereferencing_mut_binding` lint detects a `mut x` pattern that resets the binding mode, + /// as this behavior will change in rust 2024. + /// + /// ### Example + /// + /// ```rust + /// # #![warn(dereferencing_mut_binding)] + /// let x = Some(123u32); + /// let _y = match &x { + /// Some(mut x) => { + /// x += 1; + /// x + /// } + /// None => 0, + /// }; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type + /// `u32`, which was deemed surprising. After edition 2024, adding `mut` will not change the + /// type of `x`. This lint warns users of editions before 2024 to update their code. + pub DEREFERENCING_MUT_BINDING, + Allow, + "detects `mut x` bindings that change the type of `x`", + @feature_gate = sym::mut_preserve_binding_mode_2024; + // FIXME uncomment below upon stabilization + /*@future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024), + reference: "123076", + };*/ +} + declare_lint! { /// The `unconditional_recursion` lint detects functions that cannot /// return without calling themselves. @@ -1707,6 +1747,33 @@ declare_lint! { "detects lifetime parameters that are never used" } +declare_lint! { + /// The `redundant_lifetimes` lint detects lifetime parameters that are + /// redundant because they are equal to another named lifetime. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[deny(redundant_lifetimes)] + /// + /// // `'a = 'static`, so all usages of `'a` can be replaced with `'static` + /// pub fn bar<'a: 'static>() {} + /// + /// // `'a = 'b`, so all usages of `'b` can be replaced with `'a` + /// pub fn bar<'a: 'b, 'b: 'a>() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused lifetime parameters may signal a mistake or unfinished code. + /// Consider removing the parameter. + pub REDUNDANT_LIFETIMES, + Allow, + "detects lifetime parameters that are redundant because they are equal to some other named lifetime" +} + declare_lint! { /// The `tyvar_behind_raw_pointer` lint detects raw pointer to an /// inference variable. @@ -2749,7 +2816,7 @@ declare_lint! { /// memory the pointer is allowed to read/write. Casting an integer, which /// doesn't have provenance, to a pointer requires the compiler to assign /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::from_exposed_addr`] for more information about this + /// docs of [`ptr::with_exposed_provenance`] for more information about this /// "exposing"). This penalizes the optimiser and is not well suited for /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI /// platforms). @@ -2757,11 +2824,11 @@ declare_lint! { /// It is much better to use [`ptr::with_addr`] instead to specify the /// provenance you want. If using this function is not possible because the /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::from_exposed_addr`]. + /// [`ptr::with_exposed_provenance`]. /// /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::from_exposed_addr`]: https://doc.rust-lang.org/core/ptr/fn.from_exposed_addr.html + /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html pub FUZZY_PROVENANCE_CASTS, Allow, "a fuzzy integer to pointer cast is used", @@ -2797,17 +2864,17 @@ declare_lint! { /// Since this cast is lossy, it is considered good style to use the /// [`ptr::addr`] method instead, which has a similar effect, but doesn't /// "expose" the pointer provenance. This improves optimisation potential. - /// See the docs of [`ptr::addr`] and [`ptr::expose_addr`] for more information + /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information /// about exposing pointer provenance. /// /// If your code can't comply with strict provenance and needs to expose - /// the provenance, then there is [`ptr::expose_addr`] as an escape hatch, + /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, /// which preserves the behaviour of `as usize` casts while being explicit /// about the semantics. /// /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr - /// [`ptr::expose_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_addr + /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance pub LOSSY_PROVENANCE_CASTS, Allow, "a lossy pointer to integer cast is used", @@ -4179,6 +4246,85 @@ declare_lint! { "named arguments in format used positionally" } +declare_lint! { + /// The `never_type_fallback_flowing_into_unsafe` lint detects cases where never type fallback + /// affects unsafe function calls. + /// + /// ### Never type fallback + /// + /// When the compiler sees a value of type [`!`] it implicitly inserts a coercion (if possible), + /// to allow type check to infer any type: + /// + /// ```ignore (illustrative-and-has-placeholders) + /// // this + /// let x: u8 = panic!(); + /// + /// // is (essentially) turned by the compiler into + /// let x: u8 = absurd(panic!()); + /// + /// // where absurd is a function with the following signature + /// // (it's sound, because `!` always marks unreachable code): + /// fn absurd(_: !) -> T { ... } + // FIXME: use `core::convert::absurd` here instead, once it's merged + /// ``` + /// + /// While it's convenient to be able to use non-diverging code in one of the branches (like + /// `if a { b } else { return }`) this could lead to compilation errors: + /// + /// ```compile_fail + /// // this + /// { panic!() }; + /// + /// // gets turned into this + /// { absurd(panic!()) }; // error: can't infer the type of `absurd` + /// ``` + /// + /// To prevent such errors, compiler remembers where it inserted `absurd` calls, and if it + /// can't infer their type, it sets the type to fallback. `{ absurd::(panic!()) };`. + /// This is what is known as "never type fallback". + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(never_type_fallback_flowing_into_unsafe)] + /// fn main() { + /// if true { + /// // return has type `!` which, is some cases, causes never type fallback + /// return + /// } else { + /// // `zeroed` is an unsafe function, which returns an unbounded type + /// unsafe { std::mem::zeroed() } + /// }; + /// // depending on the fallback, `zeroed` may create `()` (which is completely sound), + /// // or `!` (which is instant undefined behavior) + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Due to historic reasons never type fallback was `()`, meaning that `!` got spontaneously + /// coerced to `()`. There are plans to change that, but they may make the code such as above + /// unsound. Instead of depending on the fallback, you should specify the type explicitly: + /// ``` + /// if true { + /// return + /// } else { + /// // type is explicitly specified, fallback can't hurt us no more + /// unsafe { std::mem::zeroed::<()>() } + /// }; + /// ``` + /// + /// See [Tracking Issue for making `!` fall back to `!`](https://github.com/rust-lang/rust/issues/123748). + /// + /// [`!`]: https://doc.rust-lang.org/core/primitive.never.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + pub NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, + Warn, + "never type fallback affecting unsafe function calls" +} + declare_lint! { /// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field /// (`[u8]`) or string slice field (`str`) is used in a `packed` struct that derives one or @@ -4311,7 +4457,6 @@ declare_lint! { /// ### Example /// /// ```rust,compile_fail - /// # #![feature(type_privacy_lints)] /// # #![allow(unused)] /// #![deny(unnameable_types)] /// mod m { @@ -4328,10 +4473,14 @@ declare_lint! { /// /// It is often expected that if you can obtain an object of type `T`, then /// you can name the type `T` as well, this lint attempts to enforce this rule. + /// The recommended action is to either reexport the type properly to make it nameable, + /// or document that users are not supposed to be able to name it for one reason or another. + /// + /// Besides types, this lint applies to traits because traits can also leak through signatures, + /// and you may obtain objects of their `dyn Trait` or `impl Trait` types. pub UNNAMEABLE_TYPES, Allow, "effective visibility of a type is larger than the area in which it can be named", - @feature_gate = sym::type_privacy_lints; } declare_lint! { @@ -4673,3 +4822,68 @@ declare_lint! { }; crate_level_only } + +declare_lint! { + /// The `uncovered_param_in_projection` lint detects a violation of one of Rust's orphan rules for + /// foreign trait implementations that concerns the use of type parameters inside trait associated + /// type paths ("projections") whose output may not be a local type that is mistakenly considered + /// to "cover" said parameters which is **unsound** and which may be rejected by a future version + /// of the compiler. + /// + /// Originally reported in [#99554]. + /// + /// [#99554]: https://github.com/rust-lang/rust/issues/99554 + /// + /// ### Example + /// + /// ```rust,ignore (dependent) + /// // dependency.rs + /// #![crate_type = "lib"] + /// + /// pub trait Trait {} + /// ``` + /// + /// ```edition2021,ignore (needs dependency) + /// // dependent.rs + /// trait Identity { + /// type Output; + /// } + /// + /// impl Identity for T { + /// type Output = T; + /// } + /// + /// struct Local; + /// + /// impl dependency::Trait for ::Output {} + /// + /// fn main() {} + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + /// --> dependent.rs:11:6 + /// | + /// 11 | impl dependency::Trait for ::Output {} + /// | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Local`) + /// | + /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + /// = note: for more information, see issue #124559 + /// = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type + /// = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait for T0`, where `T0` is the first and `Tn` is the last + /// = note: `#[warn(uncovered_param_in_projection)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// FIXME(fmease): Write explainer. + pub UNCOVERED_PARAM_IN_PROJECTION, + Warn, + "impl contains type parameters that are not covered", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reference: "issue #124559 ", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7f200a7b623d6..ed165188787a5 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,6 +1,3 @@ -#[macro_use] -extern crate rustc_macros; - pub use self::Level::*; use rustc_ast::node_id::NodeId; use rustc_ast::{AttrId, Attribute}; @@ -11,6 +8,7 @@ use rustc_data_structures::stable_hasher::{ use rustc_error_messages::{DiagMessage, MultiSpan}; use rustc_hir::HashStableContext; use rustc_hir::HirId; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; use rustc_span::{sym, symbol::Ident, Span, Symbol}; use rustc_target::spec::abi::Abi; @@ -323,7 +321,8 @@ pub struct Lint { pub future_incompatible: Option, - pub is_loaded: bool, + /// `true` if this lint is being loaded by another tool (e.g. Clippy). + pub is_externally_loaded: bool, /// `Some` if this lint is feature gated, otherwise `None`. pub feature_gate: Option, @@ -468,7 +467,7 @@ impl Lint { default_level: Level::Forbid, desc: "", edition_lint_opts: None, - is_loaded: false, + is_externally_loaded: false, report_in_external_macro: false, future_incompatible: None, feature_gate: None, @@ -817,7 +816,7 @@ macro_rules! declare_lint { name: stringify!($NAME), default_level: $crate::$Level, desc: $desc, - is_loaded: false, + is_externally_loaded: false, $($v: true,)* $(feature_gate: Some($gate),)? $(future_incompatible: Some($crate::FutureIncompatibleInfo { @@ -859,7 +858,7 @@ macro_rules! declare_tool_lint { edition_lint_opts: None, report_in_external_macro: $external, future_incompatible: None, - is_loaded: true, + is_externally_loaded: true, $(feature_gate: Some($gate),)? crate_level_only: false, ..$crate::Lint::default_fields_for_macro() diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 4b0c1229da134..024f6f89a4b7f 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -50,7 +50,7 @@ fn detect_llvm_link() -> (&'static str, &'static str) { fn restore_library_path() { let key = tracked_env_var_os("REAL_LIBRARY_PATH_VAR").expect("REAL_LIBRARY_PATH_VAR"); if let Some(env) = tracked_env_var_os("REAL_LIBRARY_PATH") { - env::set_var(&key, &env); + env::set_var(&key, env); } else { env::remove_var(&key); } @@ -112,28 +112,10 @@ fn main() { restore_library_path(); - let target = env::var("TARGET").expect("TARGET was not set"); let llvm_config = - tracked_env_var_os("LLVM_CONFIG").map(|x| Some(PathBuf::from(x))).unwrap_or_else(|| { - if let Some(dir) = tracked_env_var_os("CARGO_TARGET_DIR").map(PathBuf::from) { - let to_test = dir - .parent() - .unwrap() - .parent() - .unwrap() - .join(&target) - .join("llvm/bin/llvm-config"); - if Command::new(&to_test).output().is_ok() { - return Some(to_test); - } - } - None - }); + PathBuf::from(tracked_env_var_os("LLVM_CONFIG").expect("LLVM_CONFIG was not set")); - if let Some(llvm_config) = &llvm_config { - println!("cargo:rerun-if-changed={}", llvm_config.display()); - } - let llvm_config = llvm_config.unwrap_or_else(|| PathBuf::from("llvm-config")); + println!("cargo:rerun-if-changed={}", llvm_config.display()); // Test whether we're cross-compiling LLVM. This is a pretty rare case // currently where we're producing an LLVM for a different platform than @@ -391,6 +373,12 @@ fn main() { } } + // libc++abi and libunwind have to be specified explicitly on AIX. + if target.contains("aix") { + println!("cargo:rustc-link-lib=c++abi"); + println!("cargo:rustc-link-lib=unwind"); + } + // Libstdc++ depends on pthread which Rust doesn't link on MinGW // since nothing else requires it. if target.ends_with("windows-gnu") { diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp index 40723ff9f5ef2..8871f410e366c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -147,7 +147,7 @@ LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) { Expected NameOrErr = Child->getName(); if (!NameOrErr) { // rustc_codegen_llvm currently doesn't use this error string, but it might be - // useful in the future, and in the mean time this tells LLVM that the + // useful in the future, and in the meantime this tells LLVM that the // error was not ignored and that it shouldn't abort the process. LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str()); return nullptr; @@ -175,7 +175,7 @@ extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) { extern "C" LLVMRustResult LLVMRustWriteArchive(char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers, - bool WriteSymbtab, LLVMRustArchiveKind RustKind) { + bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) { std::vector Members; auto Kind = fromRust(RustKind); @@ -207,7 +207,7 @@ LLVMRustWriteArchive(char *Dst, size_t NumMembers, auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false); #else auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab : SymtabWritingMode::NoSymtab; - auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false); + auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC); #endif if (!Result) return LLVMRustResult::Success; diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 60789b07e54ea..e842f47f48c23 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -1,10 +1,8 @@ #include "LLVMWrapper.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ProfileData/Coverage/CoverageMapping.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/InstrProf.h" -#include "llvm/ADT/ArrayRef.h" - -#include using namespace llvm; @@ -43,6 +41,8 @@ enum class LLVMRustCounterMappingRegionKind { SkippedRegion = 2, GapRegion = 3, BranchRegion = 4, + MCDCDecisionRegion = 5, + MCDCBranchRegion = 6 }; static coverage::CounterMappingRegion::RegionKind @@ -58,15 +58,102 @@ fromRust(LLVMRustCounterMappingRegionKind Kind) { return coverage::CounterMappingRegion::GapRegion; case LLVMRustCounterMappingRegionKind::BranchRegion: return coverage::CounterMappingRegion::BranchRegion; +#if LLVM_VERSION_GE(18, 0) + case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion: + return coverage::CounterMappingRegion::MCDCDecisionRegion; + case LLVMRustCounterMappingRegionKind::MCDCBranchRegion: + return coverage::CounterMappingRegion::MCDCBranchRegion; +#else + case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion: + break; + case LLVMRustCounterMappingRegionKind::MCDCBranchRegion: + break; +#endif } report_fatal_error("Bad LLVMRustCounterMappingRegionKind!"); } +enum LLVMRustMCDCParametersTag { + None = 0, + Decision = 1, + Branch = 2, +}; + +struct LLVMRustMCDCDecisionParameters { + uint32_t BitmapIdx; + uint16_t NumConditions; +}; + +struct LLVMRustMCDCBranchParameters { + int16_t ConditionID; + int16_t ConditionIDs[2]; +}; + +struct LLVMRustMCDCParameters { + LLVMRustMCDCParametersTag Tag; + LLVMRustMCDCDecisionParameters DecisionParameters; + LLVMRustMCDCBranchParameters BranchParameters; +}; + +// LLVM representations for `MCDCParameters` evolved from LLVM 18 to 19. +// Look at representations in 18 +// https://github.com/rust-lang/llvm-project/blob/66a2881a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L253-L263 +// and representations in 19 +// https://github.com/llvm/llvm-project/blob/843cc474faefad1d639f4c44c1cf3ad7dbda76c8/llvm/include/llvm/ProfileData/Coverage/MCDCTypes.h +#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) +static coverage::CounterMappingRegion::MCDCParameters +fromRust(LLVMRustMCDCParameters Params) { + auto parameter = coverage::CounterMappingRegion::MCDCParameters{}; + switch (Params.Tag) { + case LLVMRustMCDCParametersTag::None: + return parameter; + case LLVMRustMCDCParametersTag::Decision: + parameter.BitmapIdx = + static_cast(Params.DecisionParameters.BitmapIdx), + parameter.NumConditions = + static_cast(Params.DecisionParameters.NumConditions); + return parameter; + case LLVMRustMCDCParametersTag::Branch: + parameter.ID = static_cast( + Params.BranchParameters.ConditionID), + parameter.FalseID = + static_cast( + Params.BranchParameters.ConditionIDs[0]), + parameter.TrueID = + static_cast( + Params.BranchParameters.ConditionIDs[1]); + return parameter; + } + report_fatal_error("Bad LLVMRustMCDCParametersTag!"); +} +#elif LLVM_VERSION_GE(19, 0) +static coverage::mcdc::Parameters fromRust(LLVMRustMCDCParameters Params) { + switch (Params.Tag) { + case LLVMRustMCDCParametersTag::None: + return std::monostate(); + case LLVMRustMCDCParametersTag::Decision: + return coverage::mcdc::DecisionParameters( + Params.DecisionParameters.BitmapIdx, + Params.DecisionParameters.NumConditions); + case LLVMRustMCDCParametersTag::Branch: + return coverage::mcdc::BranchParameters( + static_cast( + Params.BranchParameters.ConditionID), + {static_cast( + Params.BranchParameters.ConditionIDs[0]), + static_cast( + Params.BranchParameters.ConditionIDs[1])}); + } + report_fatal_error("Bad LLVMRustMCDCParametersTag!"); +} +#endif + // FFI equivalent of struct `llvm::coverage::CounterMappingRegion` // https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L211-L304 struct LLVMRustCounterMappingRegion { LLVMRustCounter Count; LLVMRustCounter FalseCount; + LLVMRustMCDCParameters MCDCParameters; uint32_t FileID; uint32_t ExpandedFileID; uint32_t LineStart; @@ -103,35 +190,30 @@ fromRust(LLVMRustCounterExprKind Kind) { } extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( - const char *const Filenames[], - size_t FilenamesLen, - const size_t *const Lengths, - size_t LengthsLen, + const char *const Filenames[], size_t FilenamesLen, // String start pointers + const size_t *const Lengths, size_t LengthsLen, // Corresponding lengths RustStringRef BufferOut) { if (FilenamesLen != LengthsLen) { report_fatal_error( "Mismatched lengths in LLVMRustCoverageWriteFilenamesSectionToBuffer"); } - SmallVector FilenameRefs; + SmallVector FilenameRefs; FilenameRefs.reserve(FilenamesLen); for (size_t i = 0; i < FilenamesLen; i++) { FilenameRefs.emplace_back(Filenames[i], Lengths[i]); } - auto FilenamesWriter = - coverage::CoverageFilenamesSectionWriter(ArrayRef(FilenameRefs)); + auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter( + ArrayRef(FilenameRefs)); auto OS = RawRustStringOstream(BufferOut); FilenamesWriter.write(OS); } extern "C" void LLVMRustCoverageWriteMappingToBuffer( - const unsigned *VirtualFileMappingIDs, - unsigned NumVirtualFileMappingIDs, - const LLVMRustCounterExpression *RustExpressions, - unsigned NumExpressions, + const unsigned *VirtualFileMappingIDs, unsigned NumVirtualFileMappingIDs, + const LLVMRustCounterExpression *RustExpressions, unsigned NumExpressions, const LLVMRustCounterMappingRegion *RustMappingRegions, - unsigned NumMappingRegions, - RustStringRef BufferOut) { + unsigned NumMappingRegions, RustStringRef BufferOut) { // Convert from FFI representation to LLVM representation. SmallVector MappingRegions; MappingRegions.reserve(NumMappingRegions); @@ -140,9 +222,10 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( MappingRegions.emplace_back( fromRust(Region.Count), fromRust(Region.FalseCount), #if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) - coverage::CounterMappingRegion::MCDCParameters{}, + // LLVM 19 may move this argument to last. + fromRust(Region.MCDCParameters), #endif - Region.FileID, Region.ExpandedFileID, + Region.FileID, Region.ExpandedFileID, // File IDs, then region info. Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, fromRust(Region.Kind)); } @@ -158,29 +241,25 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( auto CoverageMappingWriter = coverage::CoverageMappingWriter( ArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), - Expressions, - MappingRegions); + Expressions, MappingRegions); auto OS = RawRustStringOstream(BufferOut); CoverageMappingWriter.write(OS); } -extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar( - LLVMValueRef F, - const char *FuncName, - size_t FuncNameLen) { +extern "C" LLVMValueRef +LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName, + size_t FuncNameLen) { auto FuncNameRef = StringRef(FuncName, FuncNameLen); return wrap(createPGOFuncNameVar(*cast(unwrap(F)), FuncNameRef)); } -extern "C" uint64_t LLVMRustCoverageHashByteArray( - const char *Bytes, - size_t NumBytes) { +extern "C" uint64_t LLVMRustCoverageHashByteArray(const char *Bytes, + size_t NumBytes) { auto StrRef = StringRef(Bytes, NumBytes); return IndexedInstrProf::ComputeHash(StrRef); } -static void WriteSectionNameToString(LLVMModuleRef M, - InstrProfSectKind SK, +static void WriteSectionNameToString(LLVMModuleRef M, InstrProfSectKind SK, RustStringRef Str) { auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); auto name = getInstrProfSectionName(SK, TargetTriple.getObjectFormat()); @@ -193,8 +272,9 @@ extern "C" void LLVMRustCoverageWriteMapSectionNameToString(LLVMModuleRef M, WriteSectionNameToString(M, IPSK_covmap, Str); } -extern "C" void LLVMRustCoverageWriteFuncSectionNameToString(LLVMModuleRef M, - RustStringRef Str) { +extern "C" void +LLVMRustCoverageWriteFuncSectionNameToString(LLVMModuleRef M, + RustStringRef Str) { WriteSectionNameToString(M, IPSK_covfun, Str); } @@ -205,5 +285,8 @@ extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) { } extern "C" uint32_t LLVMRustCoverageMappingVersion() { - return coverage::CovMapVersion::Version6; + // This should always be `CurrentVersion`, because that's the version LLVM + // will use when encoding the data we give it. If for some reason we ever + // want to override the version number we _emit_, do it on the Rust side. + return coverage::CovMapVersion::CurrentVersion; } diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 6d578c97f3fe1..a493abbbc7e5a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -91,6 +91,8 @@ enum LLVMRustAttribute { AllocAlign = 39, SanitizeSafeStack = 40, FnRetThunkExtern = 41, + Writable = 42, + DeadOnUnwind = 43, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index f6253068eaa63..067374c026107 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -24,9 +24,7 @@ #include "llvm/Passes/StandardInstrumentations.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" -#if LLVM_VERSION_GE(17, 0) #include "llvm/Support/VirtualFileSystem.h" -#endif #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/FunctionImport.h" @@ -334,14 +332,8 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, std::ostringstream Buf; -#if LLVM_VERSION_GE(17, 0) const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const ArrayRef CPUTable = MCInfo->getAllProcessorDescriptions(); -#else - Buf << "Full target CPU help is not supported by this LLVM version.\n\n"; - SubtargetSubTypeKV TargetCPUKV = { TargetCPU, {{}}, {{}} }; - const ArrayRef CPUTable = TargetCPUKV; -#endif unsigned MaxCPULen = getLongestEntryLength(CPUTable); Buf << "Available CPUs for this target:\n"; @@ -476,10 +468,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.RelaxELFRelocations = RelaxELFRelocations; #endif Options.UseInitArray = UseInitArray; - -#if LLVM_VERSION_LT(17, 0) - Options.ExplicitEmulatedTLS = true; -#endif Options.EmulatedTLS = UseEmulatedTls; if (TrapUnreachable) { @@ -761,16 +749,10 @@ LLVMRustOptimize( } std::optional PGOOpt; -#if LLVM_VERSION_GE(17, 0) auto FS = vfs::getRealFileSystem(); -#endif if (PGOGenPath) { assert(!PGOUsePath && !PGOSampleUsePath); - PGOOpt = PGOOptions(PGOGenPath, "", "", -#if LLVM_VERSION_GE(17, 0) - "", - FS, -#endif + PGOOpt = PGOOptions(PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, PGOOptions::NoCSAction, #if LLVM_VERSION_GE(19, 0) PGOOptions::ColdFuncOpt::Default, @@ -778,33 +760,21 @@ LLVMRustOptimize( DebugInfoForProfiling); } else if (PGOUsePath) { assert(!PGOSampleUsePath); - PGOOpt = PGOOptions(PGOUsePath, "", "", -#if LLVM_VERSION_GE(17, 0) - "", - FS, -#endif + PGOOpt = PGOOptions(PGOUsePath, "", "", "", FS, PGOOptions::IRUse, PGOOptions::NoCSAction, #if LLVM_VERSION_GE(19, 0) PGOOptions::ColdFuncOpt::Default, #endif DebugInfoForProfiling); } else if (PGOSampleUsePath) { - PGOOpt = PGOOptions(PGOSampleUsePath, "", "", -#if LLVM_VERSION_GE(17, 0) - "", - FS, -#endif + PGOOpt = PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse, PGOOptions::NoCSAction, #if LLVM_VERSION_GE(19, 0) PGOOptions::ColdFuncOpt::Default, #endif DebugInfoForProfiling); } else if (DebugInfoForProfiling) { - PGOOpt = PGOOptions("", "", "", -#if LLVM_VERSION_GE(17, 0) - "", - FS, -#endif + PGOOpt = PGOOptions("", "", "", "", FS, PGOOptions::NoAction, PGOOptions::NoCSAction, #if LLVM_VERSION_GE(19, 0) PGOOptions::ColdFuncOpt::Default, @@ -1353,9 +1323,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, ComputeCrossModuleImport( Ret->Index, Ret->ModuleToDefinedGVSummaries, -#if LLVM_VERSION_GE(17, 0) isPrevailing, -#endif Ret->ImportLists, Ret->ExportLists ); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 1632b9e124905..20167a4b45ee2 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -25,6 +25,13 @@ #include +// for raw `write` in the bad-alloc handler +#ifdef _MSC_VER +#include +#else +#include +#endif + //===----------------------------------------------------------------------=== // // This file defines alternate interfaces to core functions that are more @@ -88,8 +95,24 @@ static void FatalErrorHandler(void *UserData, exit(101); } -extern "C" void LLVMRustInstallFatalErrorHandler() { +// Custom error handler for bad-alloc LLVM errors. +// +// It aborts the process without any further allocations, similar to LLVM's +// default except that may be configured to `throw std::bad_alloc()` instead. +static void BadAllocErrorHandler(void *UserData, + const char* Reason, + bool GenCrashDiag) { + const char *OOM = "rustc-LLVM ERROR: out of memory\n"; + (void)!::write(2, OOM, strlen(OOM)); + (void)!::write(2, Reason, strlen(Reason)); + (void)!::write(2, "\n", 1); + abort(); +} + +extern "C" void LLVMRustInstallErrorHandlers() { + install_bad_alloc_error_handler(BadAllocErrorHandler); install_fatal_error_handler(FatalErrorHandler); + install_out_of_memory_new_handler(); } extern "C" void LLVMRustDisableSystemDialogsOnCrash() { @@ -289,6 +312,16 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::SafeStack; case FnRetThunkExtern: return Attribute::FnRetThunkExtern; +#if LLVM_VERSION_GE(18, 0) + case Writable: + return Attribute::Writable; + case DeadOnUnwind: + return Attribute::DeadOnUnwind; +#else + case Writable: + case DeadOnUnwind: + report_fatal_error("Not supported on this LLVM version"); +#endif } report_fatal_error("bad AttributeKind"); } @@ -794,7 +827,7 @@ extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } -extern "C" void LLVMRustAddModuleFlag( +extern "C" void LLVMRustAddModuleFlagU32( LLVMModuleRef M, Module::ModFlagBehavior MergeBehavior, const char *Name, @@ -802,6 +835,16 @@ extern "C" void LLVMRustAddModuleFlag( unwrap(M)->addModuleFlag(MergeBehavior, Name, Value); } +extern "C" void LLVMRustAddModuleFlagString( + LLVMModuleRef M, + Module::ModFlagBehavior MergeBehavior, + const char *Name, + const char *Value, + size_t ValueLen) { + unwrap(M)->addModuleFlag(MergeBehavior, Name, + MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen))); +} + extern "C" bool LLVMRustHasModuleFlag(LLVMModuleRef M, const char *Name, size_t Len) { return unwrap(M)->getModuleFlag(StringRef(Name, Len)) != nullptr; @@ -1489,20 +1532,56 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { delete Bundle; } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, - OperandBundleDef **OpBundles, + OperandBundleDef **OpBundlesIndirect, unsigned NumOpBundles) { Value *Callee = unwrap(Fn); FunctionType *FTy = unwrap(Ty); + + // FIXME: Is there a way around this? + SmallVector OpBundles; + OpBundles.reserve(NumOpBundles); + for (unsigned i = 0; i < NumOpBundles; ++i) { + OpBundles.push_back(*OpBundlesIndirect[i]); + } + return wrap(unwrap(B)->CreateCall( FTy, Callee, ArrayRef(unwrap(Args), NumArgs), - ArrayRef(*OpBundles, NumOpBundles))); + ArrayRef(OpBundles))); } extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { - return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), - (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); + return wrap(llvm::Intrinsic::getDeclaration( + unwrap(M), llvm::Intrinsic::instrprof_increment)); +} + +extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) { +#if LLVM_VERSION_GE(18, 0) + return wrap(llvm::Intrinsic::getDeclaration( + unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters)); +#else + report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions"); +#endif +} + +extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) { +#if LLVM_VERSION_GE(18, 0) + return wrap(llvm::Intrinsic::getDeclaration( + unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update)); +#else + report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions"); +#endif +} + +extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(LLVMModuleRef M) { +#if LLVM_VERSION_GE(18, 0) + return wrap(llvm::Intrinsic::getDeclaration( + unwrap(M), llvm::Intrinsic::instrprof_mcdc_condbitmap_update)); +#else + report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions"); +#endif } extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, @@ -1533,26 +1612,36 @@ extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch, - OperandBundleDef **OpBundles, unsigned NumOpBundles, + OperandBundleDef **OpBundlesIndirect, unsigned NumOpBundles, const char *Name) { Value *Callee = unwrap(Fn); FunctionType *FTy = unwrap(Ty); + + // FIXME: Is there a way around this? + SmallVector OpBundles; + OpBundles.reserve(NumOpBundles); + for (unsigned i = 0; i < NumOpBundles; ++i) { + OpBundles.push_back(*OpBundlesIndirect[i]); + } + return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch), ArrayRef(unwrap(Args), NumArgs), - ArrayRef(*OpBundles, NumOpBundles), + ArrayRef(OpBundles), Name)); } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMBasicBlockRef DefaultDest, LLVMBasicBlockRef *IndirectDests, unsigned NumIndirectDests, LLVMValueRef *Args, unsigned NumArgs, - OperandBundleDef **OpBundles, unsigned NumOpBundles, + OperandBundleDef **OpBundlesIndirect, unsigned NumOpBundles, const char *Name) { Value *Callee = unwrap(Fn); FunctionType *FTy = unwrap(Ty); @@ -1564,11 +1653,18 @@ LLVMRustBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, IndirectDestsUnwrapped.push_back(unwrap(IndirectDests[i])); } + // FIXME: Is there a way around this? + SmallVector OpBundles; + OpBundles.reserve(NumOpBundles); + for (unsigned i = 0; i < NumOpBundles; ++i) { + OpBundles.push_back(*OpBundlesIndirect[i]); + } + return wrap(unwrap(B)->CreateCallBr( FTy, Callee, unwrap(DefaultDest), ArrayRef(IndirectDestsUnwrapped), ArrayRef(unwrap(Args), NumArgs), - ArrayRef(*OpBundles, NumOpBundles), + ArrayRef(OpBundles), Name)); } @@ -1969,7 +2065,11 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler( } } if (DiagnosticHandlerCallback) { +#if LLVM_VERSION_GE(19, 0) + DiagnosticHandlerCallback(&DI, DiagnosticHandlerContext); +#else DiagnosticHandlerCallback(DI, DiagnosticHandlerContext); +#endif return true; } return false; @@ -2129,19 +2229,3 @@ extern "C" LLVMValueRef LLVMConstStringInContext2(LLVMContextRef C, return wrap(ConstantDataArray::getString(*unwrap(C), StringRef(Str, Length), !DontNullTerminate)); } #endif - -// FIXME: Remove when Rust's minimum supported LLVM version reaches 17. -// https://github.com/llvm/llvm-project/commit/35276f16e5a2cae0dfb49c0fbf874d4d2f177acc -#if LLVM_VERSION_LT(17, 0) -extern "C" LLVMValueRef LLVMConstArray2(LLVMTypeRef ElementTy, - LLVMValueRef *ConstantVals, - uint64_t Length) { - ArrayRef V(unwrap(ConstantVals, Length), Length); - return wrap(ConstantArray::get(ArrayType::get(unwrap(ElementTy), Length), V)); -} - -extern "C" LLVMTypeRef LLVMArrayType2(LLVMTypeRef ElementTy, - uint64_t ElementCount) { - return wrap(ArrayType::get(unwrap(ElementTy), ElementCount)); -} -#endif diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 6009a43e98557..3ff86f700a535 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" tracing = "0.1.28" tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } -tracing-tree = "0.2.0" +tracing-tree = "0.3.0" # tidy-alphabetical-end [dev-dependencies] diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index e4b67cde244fb..81257f9be8823 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -17,7 +17,7 @@ //! rustc_log::init_logger(rustc_log::LoggerConfig::from_env("LOG")).unwrap(); //! //! let edition = rustc_span::edition::Edition::Edition2021; -//! rustc_span::create_session_globals_then(edition, || { +//! rustc_span::create_session_globals_then(edition, None, || { //! /* ... */ //! }); //! } diff --git a/compiler/rustc_macros/build.rs b/compiler/rustc_macros/build.rs index 717f8a922457d..62e827371a7f1 100644 --- a/compiler/rustc_macros/build.rs +++ b/compiler/rustc_macros/build.rs @@ -1,7 +1,7 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-env-changed=RUSTC_BOOTSTRAP"); - if !std::env::var("RUSTC_BOOTSTRAP").is_ok() { + if std::env::var("RUSTC_BOOTSTRAP").is_err() { eprintln!( "error: you are attempting to build the compiler without going through bootstrap" ); diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index ced782cdbc016..45236771bce63 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -71,6 +71,7 @@ impl SubdiagnosticDerive { span_field: None, applicability: None, has_suggestion_parts: false, + has_subdiagnostic: false, is_enum, }; builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) @@ -90,7 +91,7 @@ impl SubdiagnosticDerive { fn add_to_diag_with<__G, __F>( self, #diag: &mut rustc_errors::Diag<'_, __G>, - #f: __F + #f: &__F ) where __G: rustc_errors::EmissionGuarantee, __F: rustc_errors::SubdiagMessageOp<__G>, @@ -133,6 +134,10 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> { /// during finalization if still `false`. has_suggestion_parts: bool, + /// Set to true when a `#[subdiagnostic]` field is encountered, used to suppress the error + /// emitted when no subdiagnostic kinds are specified on the variant itself. + has_subdiagnostic: bool, + /// Set to true when this variant is an enum variant rather than just the body of a struct. is_enum: bool, } @@ -373,6 +378,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { Ok(quote! {}) } + "subdiagnostic" => { + let f = &self.parent.f; + let diag = &self.parent.diag; + let binding = &info.binding; + self.has_subdiagnostic = true; + Ok(quote! { #binding.add_to_diag_with(#diag, #f); }) + } _ => { let mut span_attrs = vec![]; if kind_stats.has_multipart_suggestion { @@ -480,18 +492,6 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { pub(crate) fn into_tokens(&mut self) -> Result { let kind_slugs = self.identify_kind()?; - if kind_slugs.is_empty() { - if self.is_enum { - // It's okay for a variant to not be a subdiagnostic at all.. - return Ok(quote! {}); - } else { - // ..but structs should always be _something_. - throw_span_err!( - self.variant.ast().ident.span().unwrap(), - "subdiagnostic kind not specified" - ); - } - }; let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect(); @@ -510,6 +510,19 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { .map(|binding| self.generate_field_attr_code(binding, kind_stats)) .collect(); + if kind_slugs.is_empty() { + if self.is_enum { + // It's okay for a variant to not be a subdiagnostic at all.. + return Ok(quote! {}); + } else if !self.has_subdiagnostic { + // ..but structs should always be _something_. + throw_span_err!( + self.variant.ast().ident.span().unwrap(), + "subdiagnostic kind not specified" + ); + } + }; + let span_field = self.span_field.value_ref(); let diag = &self.parent.diag; diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index eedc508fb1461..c7b7eadbd9d6b 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -144,6 +144,7 @@ decl_derive!( help, note, warning, + subdiagnostic, suggestion, suggestion_short, suggestion_hidden, diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index f456dd09dea53..3d0846ae6deab 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -38,6 +38,9 @@ metadata_crate_dep_multiple = cannot satisfy dependencies so `{$crate_name}` only shows up once .help = having upstream crates all available in one format will likely make this go away +metadata_crate_dep_not_static = + `{$crate_name}` was unavailable as a static crate, preventing fully static linking + metadata_crate_location_unknown_type = extern location for {$crate_name} is of an unknown type: {$path} diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index b544bc8a782c4..888c2427d8f46 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -13,10 +13,10 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; use rustc_errors::DiagCtxt; use rustc_expand::base::SyntaxExtension; use rustc_fs_util::try_canonicalize; -use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, StableCrateIdMap, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_index::IndexVec; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; use rustc_session::config::{self, CrateType, ExternLocation}; use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource}; use rustc_session::lint; @@ -62,9 +62,6 @@ pub struct CStore { /// This crate has a `#[alloc_error_handler]` item. has_alloc_error_handler: bool, - /// The interned [StableCrateId]s. - pub(crate) stable_crate_ids: StableCrateIdMap, - /// Unused externs of the crate unused_externs: Vec, } @@ -165,25 +162,27 @@ impl CStore { }) } - fn intern_stable_crate_id(&mut self, root: &CrateRoot) -> Result { - assert_eq!(self.metas.len(), self.stable_crate_ids.len()); - let num = CrateNum::new(self.stable_crate_ids.len()); - if let Some(&existing) = self.stable_crate_ids.get(&root.stable_crate_id()) { + fn intern_stable_crate_id<'tcx>( + &mut self, + root: &CrateRoot, + tcx: TyCtxt<'tcx>, + ) -> Result, CrateError> { + assert_eq!(self.metas.len(), tcx.untracked().stable_crate_ids.read().len()); + let num = tcx.create_crate_num(root.stable_crate_id()).map_err(|existing| { // Check for (potential) conflicts with the local crate if existing == LOCAL_CRATE { - Err(CrateError::SymbolConflictsCurrent(root.name())) + CrateError::SymbolConflictsCurrent(root.name()) } else if let Some(crate_name1) = self.metas[existing].as_ref().map(|data| data.name()) { let crate_name0 = root.name(); - Err(CrateError::StableCrateIdCollision(crate_name0, crate_name1)) + CrateError::StableCrateIdCollision(crate_name0, crate_name1) } else { - Err(CrateError::NotFound(root.name())) + CrateError::NotFound(root.name()) } - } else { - self.metas.push(None); - self.stable_crate_ids.insert(root.stable_crate_id(), num); - Ok(num) - } + })?; + + self.metas.push(None); + Ok(num) } pub fn has_crate_data(&self, cnum: CrateNum) -> bool { @@ -289,12 +288,7 @@ impl CStore { } } - pub fn new( - metadata_loader: Box, - local_stable_crate_id: StableCrateId, - ) -> CStore { - let mut stable_crate_ids = StableCrateIdMap::default(); - stable_crate_ids.insert(local_stable_crate_id, LOCAL_CRATE); + pub fn new(metadata_loader: Box) -> CStore { CStore { metadata_loader, // We add an empty entry for LOCAL_CRATE (which maps to zero) in @@ -307,7 +301,6 @@ impl CStore { alloc_error_handler_kind: None, has_global_allocator: false, has_alloc_error_handler: false, - stable_crate_ids, unused_externs: Vec::new(), } } @@ -389,6 +382,15 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { None } + // The `dependency` type is determined by the command line arguments(`--extern`) and + // `private_dep`. However, sometimes the directly dependent crate is not specified by + // `--extern`, in this case, `private-dep` is none during loading. This is equivalent to the + // scenario where the command parameter is set to `public-dependency` + fn is_private_dep(&self, name: &str, private_dep: Option) -> bool { + self.sess.opts.externs.get(name).map_or(private_dep.unwrap_or(false), |e| e.is_private_dep) + && private_dep.unwrap_or(true) + } + fn register_crate( &mut self, host_lib: Option, @@ -404,17 +406,11 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { let Library { source, metadata } = lib; let crate_root = metadata.get_root(); let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); - - let private_dep = self - .sess - .opts - .externs - .get(name.as_str()) - .map_or(private_dep.unwrap_or(false), |e| e.is_private_dep) - && private_dep.unwrap_or(true); + let private_dep = self.is_private_dep(name.as_str(), private_dep); // Claim this crate number and cache it - let cnum = self.cstore.intern_stable_crate_id(&crate_root)?; + let feed = self.cstore.intern_stable_crate_id(&crate_root, self.tcx)?; + let cnum = feed.key(); info!( "register crate `{}` (cnum = {}. private_dep = {})", @@ -601,14 +597,17 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { match result { (LoadResult::Previous(cnum), None) => { + // When `private_dep` is none, it indicates the directly dependent crate. If it is + // not specified by `--extern` on command line parameters, it may be + // `private-dependency` when `register_crate` is called for the first time. Then it must be updated to + // `public-dependency` here. + let private_dep = self.is_private_dep(name.as_str(), private_dep); let data = self.cstore.get_crate_data_mut(cnum); if data.is_proc_macro_crate() { dep_kind = CrateDepKind::MacrosOnly; } data.set_dep_kind(cmp::max(data.dep_kind(), dep_kind)); - if let Some(private_dep) = private_dep { - data.update_and_private_dep(private_dep); - } + data.update_and_private_dep(private_dep); Ok(cnum) } (LoadResult::Loaded(library), host_library) => { diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index b80a9e9612aad..4d1bd45541231 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -54,7 +54,7 @@ use crate::creader::CStore; use crate::errors::{ BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, LibRequired, - RequiredPanicStrategy, RlibRequired, RustcLibRequired, TwoPanicRuntimes, + NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcLibRequired, TwoPanicRuntimes, }; use rustc_data_structures::fx::FxHashMap; @@ -123,13 +123,15 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { CrateType::Rlib => Linkage::NotLinked, }; + let mut unavailable_as_static = Vec::new(); + match preferred_linkage { // If the crate is not linked, there are no link-time dependencies. Linkage::NotLinked => return Vec::new(), Linkage::Static => { // Attempt static linkage first. For dylibs and executables, we may be // able to retry below with dynamic linkage. - if let Some(v) = attempt_static(tcx) { + if let Some(v) = attempt_static(tcx, &mut unavailable_as_static) { return v; } @@ -169,11 +171,11 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let src = tcx.used_crate_source(cnum); if src.dylib.is_some() { info!("adding dylib: {}", name); - add_library(tcx, cnum, RequireDynamic, &mut formats); + add_library(tcx, cnum, RequireDynamic, &mut formats, &mut unavailable_as_static); let deps = tcx.dylib_dependency_formats(cnum); for &(depnum, style) in deps.iter() { info!("adding {:?}: {}", style, tcx.crate_name(depnum)); - add_library(tcx, depnum, style, &mut formats); + add_library(tcx, depnum, style, &mut formats, &mut unavailable_as_static); } } } @@ -201,7 +203,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { { assert!(src.rlib.is_some() || src.rmeta.is_some()); info!("adding staticlib: {}", tcx.crate_name(cnum)); - add_library(tcx, cnum, RequireStatic, &mut formats); + add_library(tcx, cnum, RequireStatic, &mut formats, &mut unavailable_as_static); ret[cnum.as_usize() - 1] = Linkage::Static; } } @@ -252,6 +254,7 @@ fn add_library( cnum: CrateNum, link: LinkagePreference, m: &mut FxHashMap, + unavailable_as_static: &mut Vec, ) { match m.get(&cnum) { Some(&link2) => { @@ -263,7 +266,13 @@ fn add_library( // This error is probably a little obscure, but I imagine that it // can be refined over time. if link2 != link || link == RequireStatic { - tcx.dcx().emit_err(CrateDepMultiple { crate_name: tcx.crate_name(cnum) }); + tcx.dcx().emit_err(CrateDepMultiple { + crate_name: tcx.crate_name(cnum), + non_static_deps: unavailable_as_static + .drain(..) + .map(|cnum| NonStaticCrateDep { crate_name: tcx.crate_name(cnum) }) + .collect(), + }); } } None => { @@ -272,7 +281,7 @@ fn add_library( } } -fn attempt_static(tcx: TyCtxt<'_>) -> Option { +fn attempt_static(tcx: TyCtxt<'_>, unavailable: &mut Vec) -> Option { let all_crates_available_as_rlib = tcx .crates(()) .iter() @@ -281,7 +290,11 @@ fn attempt_static(tcx: TyCtxt<'_>) -> Option { if tcx.dep_kind(cnum).macros_only() { return None; } - Some(tcx.used_crate_source(cnum).rlib.is_some()) + let is_rlib = tcx.used_crate_source(cnum).rlib.is_some(); + if !is_rlib { + unavailable.push(cnum); + } + Some(is_rlib) }) .all(|is_rlib| is_rlib); if !all_crates_available_as_rlib { diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 8bf6b665de885..fb0010f2c5d2e 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -4,8 +4,7 @@ use std::{ }; use rustc_errors::{codes::*, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level}; -use rustc_macros::Diagnostic; -use rustc_session::config; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{sym, Span, Symbol}; use rustc_target::spec::{PanicStrategy, TargetTriple}; @@ -38,6 +37,14 @@ pub struct RustcLibRequired<'a> { #[help] pub struct CrateDepMultiple { pub crate_name: Symbol, + #[subdiagnostic] + pub non_static_deps: Vec, +} + +#[derive(Subdiagnostic)] +#[note(metadata_crate_dep_not_static)] +pub struct NonStaticCrateDep { + pub crate_name: Symbol, } #[derive(Diagnostic)] @@ -632,9 +639,7 @@ impl Diagnostic<'_, G> for CannotFindCrate { diag.arg("locator_triple", self.locator_triple.triple()); diag.code(E0463); diag.span(self.span); - if (self.crate_name == sym::std || self.crate_name == sym::core) - && self.locator_triple != TargetTriple::from_triple(config::host_triple()) - { + if self.crate_name == sym::std || self.crate_name == sym::core { if self.missing_core { diag.note(fluent::metadata_target_not_installed); } else { diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index f133a2f5f73b2..c8162a1f0eead 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -6,7 +6,6 @@ #![feature(error_iter)] #![feature(extract_if)] #![feature(coroutines)] -#![feature(generic_nonzero)] #![feature(iter_from_coroutine)] #![feature(let_chains)] #![feature(if_let_guard)] @@ -20,8 +19,6 @@ extern crate proc_macro; -#[macro_use] -extern crate rustc_macros; #[macro_use] extern crate rustc_middle; diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index baa2e1ff60215..58760be921a61 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/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 0467cf2969fc5..2a33088513b75 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1063,6 +1063,20 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ty::EarlyBinder::bind(&*output) } + fn get_explicit_item_super_predicates( + self, + index: DefIndex, + tcx: TyCtxt<'tcx>, + ) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> { + let lazy = self.root.tables.explicit_item_super_predicates.get(self, index); + let output = if lazy.is_default() { + &mut [] + } else { + tcx.arena.alloc_from_iter(lazy.decode((self, tcx))) + }; + ty::EarlyBinder::bind(&*output) + } + fn get_variant( self, kind: DefKind, @@ -1194,7 +1208,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { tcx.arena.alloc_from_iter(self.root.stability_implications.decode(self)) } - /// Iterates over the language items in the given crate. + /// Iterates over the lang items in the given crate. fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, LangItem)] { tcx.arena.alloc_from_iter( self.root @@ -1246,30 +1260,34 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { id: DefIndex, sess: &'a Session, ) -> impl Iterator + 'a { - iter::from_coroutine(move || { - if let Some(data) = &self.root.proc_macro_data { - // If we are loading as a proc macro, we want to return - // the view of this crate as a proc macro crate. - if id == CRATE_DEF_INDEX { - for child_index in data.macros.decode(self) { + iter::from_coroutine( + #[coroutine] + move || { + if let Some(data) = &self.root.proc_macro_data { + // If we are loading as a proc macro, we want to return + // the view of this crate as a proc macro crate. + if id == CRATE_DEF_INDEX { + for child_index in data.macros.decode(self) { + yield self.get_mod_child(child_index, sess); + } + } + } else { + // Iterate over all children. + let non_reexports = + self.root.tables.module_children_non_reexports.get(self, id); + for child_index in non_reexports.unwrap().decode(self) { yield self.get_mod_child(child_index, sess); } - } - } else { - // Iterate over all children. - let non_reexports = self.root.tables.module_children_non_reexports.get(self, id); - for child_index in non_reexports.unwrap().decode(self) { - yield self.get_mod_child(child_index, sess); - } - let reexports = self.root.tables.module_children_reexports.get(self, id); - if !reexports.is_default() { - for reexport in reexports.decode((self, sess)) { - yield reexport; + let reexports = self.root.tables.module_children_reexports.get(self, id); + if !reexports.is_default() { + for reexport in reexports.decode((self, sess)) { + yield reexport; + } } } - } - }) + }, + ) } fn is_ctfe_mir_available(self, id: DefIndex) -> bool { @@ -1590,17 +1608,17 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - // Translate the virtual `/rustc/$hash` prefix back to a real directory - // that should hold actual sources, where possible. - // - // NOTE: if you update this, you might need to also update bootstrap's code for generating - // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. - let virtual_rust_source_base_dir = [ - filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)), - filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()), - ]; - let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| { + // Translate the virtual `/rustc/$hash` prefix back to a real directory + // that should hold actual sources, where possible. + // + // NOTE: if you update this, you might need to also update bootstrap's code for generating + // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`. + let virtual_rust_source_base_dir = [ + filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)), + filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()), + ]; + debug!( "try_to_translate_virtual_to_real(name={:?}): \ virtual_rust_source_base_dir={:?}, real_rust_source_base_dir={:?}", diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 1c59af5158981..531b2e05411a0 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -21,7 +21,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_session::cstore::{CrateStore, ExternCrate}; use rustc_session::{Session, StableCrateId}; -use rustc_span::hygiene::{ExpnHash, ExpnId}; +use rustc_span::hygiene::ExpnId; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; @@ -206,10 +206,12 @@ impl IntoArgs for (CrateNum, SimplifiedType) { provide! { tcx, def_id, other, cdata, explicit_item_bounds => { cdata.get_explicit_item_bounds(def_id.index, tcx) } + explicit_item_super_predicates => { cdata.get_explicit_item_super_predicates(def_id.index, tcx) } explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } super_predicates_of => { table } + implied_predicates_of => { table } type_of => { table } type_alias_is_lazy => { cdata.root.tables.type_alias_is_lazy.get(cdata, def_id.index) } variances_of => { table } @@ -275,18 +277,6 @@ provide! { tcx, def_id, other, cdata, .map(|lazy| lazy.decode((cdata, tcx))) .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) } - implied_predicates_of => { - cdata - .root - .tables - .implied_predicates_of - .get(cdata, def_id.index) - .map(|lazy| lazy.decode((cdata, tcx))) - .unwrap_or_else(|| { - debug_assert_eq!(tcx.def_kind(def_id), DefKind::Trait); - tcx.super_predicates_of(def_id) - }) - } associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array } @@ -388,6 +378,7 @@ provide! { tcx, def_id, other, cdata, } pub(in crate::rmeta) fn provide(providers: &mut Providers) { + provide_cstore_hooks(providers); // FIXME(#44234) - almost all of these queries have no sub-queries and // therefore no actual inputs, they're just reading tables calculated in // resolve! Does this work? Unsure! That's what the issue is about @@ -638,13 +629,6 @@ impl CrateStore for CStore { self.get_crate_data(cnum).root.stable_crate_id } - fn stable_crate_id_to_crate_num(&self, stable_crate_id: StableCrateId) -> CrateNum { - *self - .stable_crate_ids - .get(&stable_crate_id) - .unwrap_or_else(|| bug!("uninterned StableCrateId: {stable_crate_id:?}")) - } - /// Returns the `DefKey` for a given `DefId`. This indicates the /// parent `DefId` as well as some idea of what kind of data the /// `DefId` refers to. @@ -659,26 +643,33 @@ impl CrateStore for CStore { fn def_path_hash(&self, def: DefId) -> DefPathHash { self.get_crate_data(def.krate).def_path_hash(def.index) } +} - fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId { - let def_index = self.get_crate_data(cnum).def_path_hash_to_def_index(hash); +fn provide_cstore_hooks(providers: &mut Providers) { + providers.hooks.def_path_hash_to_def_id_extern = |tcx, hash, stable_crate_id| { + // If this is a DefPathHash from an upstream crate, let the CrateStore map + // it to a DefId. + let cstore = CStore::from_tcx(tcx.tcx); + let cnum = *tcx + .untracked() + .stable_crate_ids + .read() + .get(&stable_crate_id) + .unwrap_or_else(|| bug!("uninterned StableCrateId: {stable_crate_id:?}")); + assert_ne!(cnum, LOCAL_CRATE); + let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash); DefId { krate: cnum, index: def_index } - } - - fn expn_hash_to_expn_id( - &self, - sess: &Session, - cnum: CrateNum, - index_guess: u32, - hash: ExpnHash, - ) -> ExpnId { - self.get_crate_data(cnum).expn_hash_to_expn_id(sess, index_guess, hash) - } + }; - fn import_source_files(&self, sess: &Session, cnum: CrateNum) { - let cdata = self.get_crate_data(cnum); + providers.hooks.expn_hash_to_expn_id = |tcx, cnum, index_guess, hash| { + let cstore = CStore::from_tcx(tcx.tcx); + cstore.get_crate_data(cnum).expn_hash_to_expn_id(tcx.sess, index_guess, hash) + }; + providers.hooks.import_source_files = |tcx, cnum| { + let cstore = CStore::from_tcx(tcx.tcx); + let cdata = cstore.get_crate_data(cnum); for file_index in 0..cdata.root.source_map.size() { - cdata.imported_source_file(file_index as u32, sess); + cdata.imported_source_file(file_index as u32, tcx.sess); } - } + }; } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d8cfceab460a0..c057f7e921e7f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -549,17 +549,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { match source_file.name { FileName::Real(ref original_file_name) => { - let adapted_file_name = if self.tcx.sess.should_prefer_remapped_for_codegen() { - source_map.path_mapping().to_embeddable_absolute_path( - original_file_name.clone(), - working_directory, - ) - } else { - source_map.path_mapping().to_local_embeddable_absolute_path( - original_file_name.clone(), - working_directory, - ) - }; + // FIXME: This should probably to conditionally remapped under + // a RemapPathScopeComponents but which one? + let adapted_file_name = source_map + .path_mapping() + .to_embeddable_absolute_path(original_file_name.clone(), working_directory); adapted_source_file.name = FileName::Real(adapted_file_name); } @@ -817,8 +811,8 @@ struct AnalyzeAttrState { #[inline] fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool { let mut should_encode = false; - if rustc_feature::is_builtin_only_local(attr.name_or_empty()) { - // Attributes marked local-only don't need to be encoded for downstream crates. + if !rustc_feature::encode_cross_crate(attr.name_or_empty()) { + // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. } else if attr.doc_str().is_some() { // We keep all doc comments reachable to rustdoc because they might be "imported" into // downstream crates if they use `#[doc(inline)]` to copy an item's documentation into @@ -1067,14 +1061,11 @@ fn should_encode_mir( // Full-fledged functions + closures DefKind::AssocFn | DefKind::Fn | DefKind::Closure => { let generics = tcx.generics_of(def_id); - let mut opt = tcx.sess.opts.unstable_opts.always_encode_mir + let opt = tcx.sess.opts.unstable_opts.always_encode_mir || (tcx.sess.opts.output_types.should_codegen() && reachable_set.contains(&def_id) && (generics.requires_monomorphization(tcx) || tcx.cross_crate_inlinable(def_id))); - if let Some(intrinsic) = tcx.intrinsic(def_id) { - opt &= !intrinsic.must_be_overridden; - } // The function has a `const` modifier or is in a `#[const_trait]`. let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id()) || tcx.is_const_default_method(def_id.to_def_id()); @@ -1438,6 +1429,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Trait = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id)); + record!(self.tables.implied_predicates_of[def_id] <- self.tcx.implied_predicates_of(def_id)); let module_children = self.tcx.module_children_local(local_id); record_array!(self.tables.module_children_non_reexports[def_id] <- @@ -1494,6 +1486,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); + self.encode_explicit_item_super_predicates(def_id); self.tables .is_type_alias_impl_trait .set(def_id.index, self.tcx.is_type_alias_impl_trait(def_id)); @@ -1602,6 +1595,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds); } + fn encode_explicit_item_super_predicates(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_explicit_item_super_predicates({:?})", def_id); + let bounds = self.tcx.explicit_item_super_predicates(def_id).skip_binder(); + record_defaulted_array!(self.tables.explicit_item_super_predicates[def_id] <- bounds); + } + #[instrument(level = "debug", skip(self))] fn encode_info_for_assoc_item(&mut self, def_id: DefId) { let tcx = self.tcx; @@ -1614,6 +1613,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { AssocItemContainer::TraitContainer => { if let ty::AssocKind::Type = item.kind { self.encode_explicit_item_bounds(def_id); + self.encode_explicit_item_super_predicates(def_id); } } AssocItemContainer::ImplContainer => { @@ -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()); } @@ -2201,7 +2201,7 @@ impl Decodable for EncodedMetadata { let mmap = if len > 0 { let mut mmap = MmapMut::map_anon(len).unwrap(); for _ in 0..len { - (&mut mmap[..]).write(&[d.read_u8()]).unwrap(); + (&mut mmap[..]).write_all(&[d.read_u8()]).unwrap(); } mmap.flush().unwrap(); Some(mmap.make_read_only().unwrap()) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 8aa31ef564f55..c9cb2f5a2405c 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -2,6 +2,7 @@ use crate::creader::CrateMetadataRef; use decoder::Metadata; use def_path_hash_map::DefPathHashMapRef; use rustc_data_structures::fx::FxHashMap; +use rustc_macros::{Decodable, Encodable, TyDecodable, TyEncodable}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::lib_features::FeatureStability; use table::TableBuilder; @@ -17,6 +18,7 @@ use rustc_hir::definitions::DefKey; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; +use rustc_macros::{MetadataDecodable, MetadataEncodable}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; @@ -387,6 +389,7 @@ define_tables! { // corresponding DefPathHash. def_path_hashes: Table, explicit_item_bounds: Table, Span)>>, + explicit_item_super_predicates: Table, Span)>>, inferred_outlives_of: Table, Span)>>, inherent_impls: Table>, associated_types_for_impl_traits_in_associated_fn: Table>, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 96c58ef411b71..d1cdabc293dd8 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -6,10 +6,10 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +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 } @@ -25,6 +25,7 @@ rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_query_system = { path = "../rustc_query_system" } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index c90427256b85f..1371926873751 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -35,7 +35,6 @@ macro_rules! arena_types { )>, [] crate_for_resolver: rustc_data_structures::steal::Steal<(rustc_ast::Crate, rustc_ast::AttrVec)>, [] resolutions: rustc_middle::ty::ResolverGlobalCtxt, - [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult, [decode] code_region: rustc_middle::mir::coverage::CodeRegion, [] const_allocs: rustc_middle::mir::interpret::Allocation, [] region_scope_tree: rustc_middle::middle::region::ScopeTree, @@ -91,6 +90,7 @@ macro_rules! arena_types { [decode] attribute: rustc_ast::Attribute, [] name_set: rustc_data_structures::unord::UnordSet, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, + [] pats: rustc_middle::ty::PatternKind<'tcx>, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 39d82c489d53f..3c5bf6eb82461 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -194,9 +194,10 @@ impl DepNodeExt for DepNode { /// has been removed. fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option { if tcx.fingerprint_style(self.kind) == FingerprintStyle::DefPathHash { - Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into()), &mut || { - panic!("Failed to extract DefId: {:?} {}", self.kind, self.hash) - })) + Some(tcx.def_path_hash_to_def_id( + DefPathHash(self.hash.into()), + &("Failed to extract DefId", self.kind, self.hash), + )) } else { None } @@ -390,9 +391,10 @@ impl<'tcx> DepNodeParams> for HirId { let (local_hash, local_id) = Fingerprint::from(dep_node.hash).split(); let def_path_hash = DefPathHash::new(tcx.stable_crate_id(LOCAL_CRATE), local_hash); let def_id = tcx - .def_path_hash_to_def_id(def_path_hash, &mut || { - panic!("Failed to extract HirId: {:?} {}", dep_node.kind, dep_node.hash) - }) + .def_path_hash_to_def_id( + def_path_hash, + &("Failed to extract HirId", dep_node.kind, dep_node.hash), + ) .expect_local(); let local_id = local_id .as_u64() diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 0d3cffd204770..6e622b0405f0a 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -1,7 +1,7 @@ use std::fmt; use rustc_errors::{codes::*, DiagArgName, DiagArgValue, DiagMessage}; -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use crate::ty::Ty; diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 5043bd855ccb9..c0c773c6285c2 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -13,51 +13,13 @@ use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::*; -use rustc_index::Idx; +use rustc_hir_pretty as pprust_hir; use rustc_middle::hir::nested_filter; use rustc_span::def_id::StableCrateId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{ErrorGuaranteed, Span}; use rustc_target::spec::abi::Abi; -#[inline] -pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> { - match node { - Node::Item(Item { - owner_id, - kind: ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), - .. - }) - | Node::TraitItem(TraitItem { - owner_id, - kind: - TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)), - .. - }) - | Node::ImplItem(ImplItem { - owner_id, - kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body), - .. - }) => Some((owner_id.def_id, *body)), - - Node::Expr(Expr { kind: ExprKind::Closure(Closure { def_id, body, .. }), .. }) => { - Some((*def_id, *body)) - } - - Node::AnonConst(constant) => Some((constant.def_id, constant.body)), - Node::ConstBlock(constant) => Some((constant.def_id, constant.body)), - - _ => None, - } -} - -fn is_body_owner(node: Node<'_>, hir_id: HirId) -> bool { - match associated_body(node) { - Some((_, b)) => b.hir_id == hir_id, - None => false, - } -} - // FIXME: the structure was necessary in the past but now it // only serves as "namespace" for HIR-related methods, and can be // removed if all the methods are reasonably renamed and moved to tcx @@ -107,27 +69,23 @@ impl<'hir> Iterator for ParentOwnerIterator<'hir> { fn next(&mut self) -> Option { if self.current_id.local_id.index() != 0 { - self.current_id.local_id = ItemLocalId::new(0); + self.current_id.local_id = ItemLocalId::ZERO; let node = self.map.tcx.hir_owner_node(self.current_id.owner); return Some((self.current_id.owner, node)); } if self.current_id == CRATE_HIR_ID { return None; } - loop { - // There are nodes that do not have entries, so we need to skip them. - let parent_id = self.map.def_key(self.current_id.owner.def_id).parent; - let parent_id = parent_id.map_or(CRATE_OWNER_ID, |local_def_index| { - let def_id = LocalDefId { local_def_index }; - self.map.tcx.local_def_id_to_hir_id(def_id).owner - }); - self.current_id = HirId::make_owner(parent_id.def_id); + let parent_id = self.map.def_key(self.current_id.owner.def_id).parent; + let parent_id = parent_id.map_or(CRATE_OWNER_ID, |local_def_index| { + let def_id = LocalDefId { local_def_index }; + self.map.tcx.local_def_id_to_hir_id(def_id).owner + }); + self.current_id = HirId::make_owner(parent_id.def_id); - // If this `HirId` doesn't have an entry, skip it and look for its `parent_id`. - let node = self.map.tcx.hir_owner_node(self.current_id.owner); - return Some((self.current_id.owner, node)); - } + let node = self.map.tcx.hir_owner_node(self.current_id.owner); + return Some((self.current_id.owner, node)); } } @@ -175,7 +133,7 @@ impl<'tcx> TyCtxt<'tcx> { /// If calling repeatedly and iterating over parents, prefer [`Map::parent_iter`]. pub fn parent_hir_id(self, hir_id: HirId) -> HirId { let HirId { owner, local_id } = hir_id; - if local_id == ItemLocalId::from_u32(0) { + if local_id == ItemLocalId::ZERO { self.hir_owner_parent(owner) } else { let parent_local_id = self.hir_owner_nodes(owner).nodes[local_id].parent; @@ -208,12 +166,12 @@ impl<'hir> Map<'hir> { #[inline] pub fn items(self) -> impl Iterator + 'hir { - self.tcx.hir_crate_items(()).items.iter().copied() + self.tcx.hir_crate_items(()).free_items.iter().copied() } #[inline] pub fn module_items(self, module: LocalModDefId) -> impl Iterator + 'hir { - self.tcx.hir_module_items(module).items() + self.tcx.hir_module_items(module).free_items() } pub fn def_key(self, def_id: LocalDefId) -> DefKey { @@ -273,7 +231,7 @@ impl<'hir> Map<'hir> { #[track_caller] pub fn enclosing_body_owner(self, hir_id: HirId) -> LocalDefId { for (_, node) in self.parent_iter(hir_id) { - if let Some((def_id, _)) = associated_body(node) { + if let Some((def_id, _)) = node.associated_body() { return def_id; } } @@ -286,20 +244,18 @@ impl<'hir> Map<'hir> { /// item (possibly associated), a closure, or a `hir::AnonConst`. pub fn body_owner(self, BodyId { hir_id }: BodyId) -> HirId { let parent = self.tcx.parent_hir_id(hir_id); - assert!(is_body_owner(self.tcx.hir_node(parent), hir_id), "{hir_id:?}"); + assert_eq!(self.tcx.hir_node(parent).body_id().unwrap().hir_id, hir_id, "{hir_id:?}"); parent } pub fn body_owner_def_id(self, BodyId { hir_id }: BodyId) -> LocalDefId { - associated_body(self.tcx.parent_hir_node(hir_id)).unwrap().0 + self.tcx.parent_hir_node(hir_id).associated_body().unwrap().0 } /// Given a `LocalDefId`, returns the `BodyId` associated with it, /// if the node is a body owner, otherwise returns `None`. pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option { - let node = self.tcx.hir_node_by_def_id(id); - let (_, body_id) = associated_body(node)?; - Some(body_id) + self.tcx.hir_node_by_def_id(id).body_id() } /// Given a body owner's id, returns the `BodyId` associated with it. @@ -462,7 +418,7 @@ impl<'hir> Map<'hir> { V: Visitor<'hir>, { let krate = self.tcx.hir_crate_items(()); - walk_list!(visitor, visit_item, krate.items().map(|id| self.item(id))); + walk_list!(visitor, visit_item, krate.free_items().map(|id| self.item(id))); walk_list!(visitor, visit_trait_item, krate.trait_items().map(|id| self.trait_item(id))); walk_list!(visitor, visit_impl_item, krate.impl_items().map(|id| self.impl_item(id))); walk_list!( @@ -480,7 +436,7 @@ impl<'hir> Map<'hir> { V: Visitor<'hir>, { let module = self.tcx.hir_module_items(module); - walk_list!(visitor, visit_item, module.items().map(|id| self.item(id))); + walk_list!(visitor, visit_item, module.free_items().map(|id| self.item(id))); walk_list!(visitor, visit_trait_item, module.trait_items().map(|id| self.trait_item(id))); walk_list!(visitor, visit_impl_item, module.impl_items().map(|id| self.impl_item(id))); walk_list!( @@ -611,7 +567,7 @@ impl<'hir> Map<'hir> { } // Ignore `return`s on the first iteration Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. }) - | Node::Local(_) => { + | Node::LetStmt(_) => { return None; } _ => {} @@ -950,11 +906,12 @@ impl<'hir> Map<'hir> { Node::Lifetime(lifetime) => lifetime.ident.span, Node::GenericParam(param) => param.span, Node::Infer(i) => i.span, - Node::Local(local) => local.span, + Node::LetStmt(local) => local.span, Node::Crate(item) => item.spans.inner_span, Node::WhereBoundPredicate(pred) => pred.span, Node::ArrayLenInfer(inf) => inf.span, - Node::AssocOpaqueTy(..) => unreachable!(), + Node::PreciseCapturingNonLifetimeArg(param) => param.ident.span, + Node::Synthetic => unreachable!(), Node::Err(span) => *span, } } @@ -1044,6 +1001,12 @@ impl<'hir> intravisit::Map<'hir> for Map<'hir> { } } +impl<'tcx> pprust_hir::PpAnn for TyCtxt<'tcx> { + fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { + pprust_hir::PpAnn::nested(&(&self.hir() as &dyn intravisit::Map<'_>), state, nested) + } +} + pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { let krate = tcx.hir_crate(()); let hir_body_hash = krate.opt_hir_hash.expect("HIR hash missing while computing crate hash"); @@ -1207,7 +1170,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::Arm(_) => node_str("arm"), Node::Block(_) => node_str("block"), Node::Infer(_) => node_str("infer"), - Node::Local(_) => node_str("local"), + Node::LetStmt(_) => node_str("local"), Node::Ctor(ctor) => format!( "{id} (ctor {})", ctor.ctor_def_id().map_or("".into(), |def_id| path_str(def_id)), @@ -1219,8 +1182,9 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::Crate(..) => String::from("(root_crate)"), Node::WhereBoundPredicate(_) => node_str("where bound predicate"), Node::ArrayLenInfer(_) => node_str("array len infer"), - Node::AssocOpaqueTy(..) => unreachable!(), + Node::Synthetic => unreachable!(), Node::Err(_) => node_str("error"), + Node::PreciseCapturingNonLifetimeArg(_param) => node_str("parameter"), } } @@ -1241,7 +1205,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod } = collector; return ModuleItems { submodules: submodules.into_boxed_slice(), - items: items.into_boxed_slice(), + free_items: items.into_boxed_slice(), trait_items: trait_items.into_boxed_slice(), impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), @@ -1270,7 +1234,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { return ModuleItems { submodules: submodules.into_boxed_slice(), - items: items.into_boxed_slice(), + free_items: items.into_boxed_slice(), trait_items: trait_items.into_boxed_slice(), impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), @@ -1314,7 +1278,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { } fn visit_item(&mut self, item: &'hir Item<'hir>) { - if associated_body(Node::Item(item)).is_some() { + if Node::Item(item).associated_body().is_some() { self.body_owners.push(item.owner_id.def_id); } @@ -1355,7 +1319,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { } fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) { - if associated_body(Node::TraitItem(item)).is_some() { + if Node::TraitItem(item).associated_body().is_some() { self.body_owners.push(item.owner_id.def_id); } @@ -1364,7 +1328,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { } fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) { - if associated_body(Node::ImplItem(item)).is_some() { + if Node::ImplItem(item).associated_body().is_some() { self.body_owners.push(item.owner_id.def_id); } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index f9fa8ac2f7aa5..34748afa863d0 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -15,6 +15,7 @@ use rustc_data_structures::sync::{try_par_for_each_in, DynSend, DynSync}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::*; +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId}; /// Gather the LocalDefId for each item-like within a module, including items contained within @@ -22,7 +23,7 @@ use rustc_span::{ErrorGuaranteed, ExpnId}; #[derive(Debug, HashStable, Encodable, Decodable)] pub struct ModuleItems { submodules: Box<[OwnerId]>, - items: Box<[ItemId]>, + free_items: Box<[ItemId]>, trait_items: Box<[TraitItemId]>, impl_items: Box<[ImplItemId]>, foreign_items: Box<[ForeignItemId]>, @@ -30,14 +31,22 @@ pub struct ModuleItems { } impl ModuleItems { - pub fn items(&self) -> impl Iterator + '_ { - self.items.iter().copied() + /// Returns all non-associated locally defined items in all modules. + /// + /// Note that this does *not* include associated items of `impl` blocks! It also does not + /// include foreign items. If you want to e.g. get all functions, use `definitions()` below. + /// + /// However, this does include the `impl` blocks themselves. + pub fn free_items(&self) -> impl Iterator + '_ { + self.free_items.iter().copied() } pub fn trait_items(&self) -> impl Iterator + '_ { self.trait_items.iter().copied() } + /// Returns all items that are associated with some `impl` block (both inherent and trait impl + /// blocks). pub fn impl_items(&self) -> impl Iterator + '_ { self.impl_items.iter().copied() } @@ -47,7 +56,7 @@ impl ModuleItems { } pub fn owners(&self) -> impl Iterator + '_ { - self.items + self.free_items .iter() .map(|id| id.owner_id) .chain(self.trait_items.iter().map(|id| id.owner_id)) @@ -63,7 +72,7 @@ impl ModuleItems { &self, f: impl Fn(ItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, ) -> Result<(), ErrorGuaranteed> { - try_par_for_each_in(&self.items[..], |&id| f(id)) + try_par_for_each_in(&self.free_items[..], |&id| f(id)) } pub fn par_trait_items( @@ -166,8 +175,12 @@ pub fn provide(providers: &mut Providers) { let parent_owner_id = tcx.local_def_id_to_hir_id(parent_def_id).owner; HirId { owner: parent_owner_id, - local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id].unwrap().parenting - [&owner_id.def_id], + local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id] + .unwrap() + .parenting + .get(&owner_id.def_id) + .copied() + .unwrap_or(ItemLocalId::ZERO), } }) }; diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 32f3a177508f7..1ac35314ead2f 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -2,6 +2,7 @@ use crate::ty; use crate::ty::Ty; use rustc_hir::HirId; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_target::abi::{FieldIdx, VariantIdx}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index b984df3646e61..d35b9fc46e4dd 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -6,8 +6,10 @@ use crate::mir; use crate::query::TyCtxtAt; use crate::ty::{Ty, TyCtxt}; -use rustc_span::def_id::LocalDefId; -use rustc_span::DUMMY_SP; +use rustc_hir::def_id::{DefId, DefPathHash}; +use rustc_session::StableCrateId; +use rustc_span::def_id::{CrateNum, LocalDefId}; +use rustc_span::{ExpnHash, ExpnId, DUMMY_SP}; macro_rules! declare_hooks { ($($(#[$attr:meta])*hook $name:ident($($arg:ident: $K:ty),*) -> $V:ty;)*) => { @@ -16,7 +18,6 @@ macro_rules! declare_hooks { $( $(#[$attr])* #[inline(always)] - #[must_use] pub fn $name(self, $($arg: $K,)*) -> $V { self.at(DUMMY_SP).$name($($arg,)*) @@ -28,7 +29,6 @@ macro_rules! declare_hooks { $( $(#[$attr])* #[inline(always)] - #[must_use] #[instrument(level = "debug", skip(self), ret)] pub fn $name(self, $($arg: $K,)*) -> $V { @@ -47,12 +47,7 @@ macro_rules! declare_hooks { impl Default for Providers { fn default() -> Self { Providers { - $($name: |_, $($arg,)*| bug!( - "`tcx.{}{:?}` cannot be called as `{}` was never assigned to a provider function.\n", - stringify!($name), - ($($arg,)*), - stringify!($name), - ),)* + $($name: |_, $($arg,)*| default_hook(stringify!($name), &($($arg,)*))),* } } } @@ -77,4 +72,41 @@ declare_hooks! { /// /// (Eligible functions might nevertheless be skipped for other reasons.) hook is_eligible_for_coverage(key: LocalDefId) -> bool; + + /// Create the MIR for a given `DefId` - this includes + /// unreachable code. + /// You do not want to call this yourself, instead use the cached version + /// via `mir_built` + hook build_mir(key: LocalDefId) -> mir::Body<'tcx>; + + /// Imports all `SourceFile`s from the given crate into the current session. + /// This normally happens automatically when we decode a `Span` from + /// that crate's metadata - however, the incr comp cache needs + /// to trigger this manually when decoding a foreign `Span` + hook import_source_files(key: CrateNum) -> (); + + hook expn_hash_to_expn_id( + cnum: CrateNum, + index_guess: u32, + hash: ExpnHash + ) -> ExpnId; + + /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation + /// session, if it still exists. This is used during incremental compilation to + /// turn a deserialized `DefPathHash` into its current `DefId`. + /// Will fetch a DefId from a DefPathHash for a foreign crate. + hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> DefId; + + /// Create a THIR tree for debugging. + hook thir_tree(key: LocalDefId) -> String; + + /// Create a list-like THIR representation for debugging. + hook thir_flat(key: LocalDefId) -> String; +} + +#[cold] +fn default_hook(name: &str, args: &dyn std::fmt::Debug) -> ! { + bug!( + "`tcx.{name}{args:?}` cannot be called as `{name}` was never assigned to a provider function" + ) } diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 7b65c11bc3c82..aee97d772229b 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -23,7 +23,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lock; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_type_ir::Canonical as IrCanonical; use rustc_type_ir::CanonicalVarInfo as IrCanonicalVarInfo; pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind}; @@ -82,7 +82,7 @@ impl CanonicalVarValues<'_> { } pub fn is_identity_modulo_regions(&self) -> bool { - let mut var = ty::BoundVar::from_u32(0); + let mut var = ty::BoundVar::ZERO; for arg in self.var_values { match arg.unpack() { ty::GenericArgKind::Lifetime(r) => { @@ -153,11 +153,6 @@ pub struct QueryResponse<'tcx, R> { pub var_values: CanonicalVarValues<'tcx>, pub region_constraints: QueryRegionConstraints<'tcx>, pub certainty: Certainty, - /// List of opaque types which we tried to compare to another type. - /// Inside the query we don't know yet whether the opaque type actually - /// should get its hidden type inferred. So we bubble the opaque type - /// and the type it was compared against upwards and let the query caller - /// handle it. pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, pub value: R, } @@ -330,6 +325,7 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> { max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: key, + defining_opaque_types: ty::List::empty(), }; } @@ -340,6 +336,14 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> { match self.map.borrow().entry(key) { Entry::Occupied(e) => { let (canonical, var_values) = e.get(); + if cfg!(debug_assertions) { + let mut state = state.clone(); + let rerun_canonical = canonicalize_op(tcx, key, &mut state); + assert_eq!(rerun_canonical, *canonical); + let OriginalQueryValues { var_values: rerun_var_values, universe_map } = state; + assert_eq!(universe_map.len(), 1); + assert_eq!(**var_values, *rerun_var_values); + } state.var_values.extend_from_slice(var_values); *canonical } diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 1384611e14679..f74f71b6b378a 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -4,6 +4,7 @@ pub mod unify_key; use crate::ty::Region; use crate::ty::{OpaqueTypeKey, Ty}; use rustc_data_structures::sync::Lrc; +use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_span::Span; /// Requires that `region` must be equal to one of the regions in `choice_regions`. diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 84b428297dbb7..105be21f27279 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -1,7 +1,6 @@ use crate::ty::{self, Ty, TyCtxt}; use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue}; use rustc_span::def_id::DefId; -use rustc_span::symbol::Symbol; use rustc_span::Span; use std::cmp; use std::marker::PhantomData; @@ -106,16 +105,11 @@ impl ToType for ty::FloatVarValue { #[derive(Copy, Clone, Debug)] pub struct ConstVariableOrigin { - pub kind: ConstVariableOriginKind, pub span: Span, -} - -/// Reasons to create a const inference variable -#[derive(Copy, Clone, Debug)] -pub enum ConstVariableOriginKind { - MiscVariable, - ConstInference, - ConstParameterDefinition(Symbol, DefId), + /// `DefId` of the const parameter this was instantiated for, if any. + /// + /// This should only be used for diagnostics. + pub param_def_id: Option, } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index c3e4a03ad1677..04fd4c8d0f7b9 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -24,20 +24,19 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![cfg_attr(bootstrap, feature(exhaustive_patterns))] -#![cfg_attr(not(bootstrap), feature(min_exhaustive_patterns))] +#![feature(min_exhaustive_patterns)] #![feature(rustdoc_internals)] #![feature(allocator_api)] #![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(closure_track_caller)] #![feature(core_intrinsics)] #![feature(const_type_name)] #![feature(discriminant_kind)] #![feature(coroutines)] -#![feature(generic_nonzero)] +#![feature(stmt_expr_attributes)] #![feature(if_let_guard)] -#![feature(inline_const)] #![feature(iter_from_coroutine)] #![feature(negative_impls)] #![feature(never_type)] @@ -48,7 +47,6 @@ #![feature(trusted_len)] #![feature(type_alias_impl_trait)] #![feature(strict_provenance)] -#![feature(associated_type_bounds)] #![feature(rustc_attrs)] #![feature(control_flow_enum)] #![feature(trait_upcasting)] @@ -65,16 +63,8 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#[macro_use] -extern crate bitflags; -#[macro_use] -extern crate rustc_macros; -#[macro_use] -extern crate rustc_data_structures; #[macro_use] extern crate tracing; -#[macro_use] -extern crate smallvec; #[cfg(test)] mod tests; diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index d8d6899f05704..086582e60a3c0 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; use rustc_errors::{Diag, DiagMessage, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; +use rustc_macros::HashStable; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS}, FutureIncompatibilityReason, Level, Lint, LintId, @@ -398,8 +399,22 @@ pub fn lint_level( } } - // Finally, run `decorate`. - decorate(&mut err); + // Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly), + // so we need to make sure when we do call `decorate` that the diagnostic is eventually + // emitted or we'll get a `must_produce_diag` ICE. + // + // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors: + // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)` + // or `Error`, then the diagnostic will be emitted regardless of CLI options. + // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by + // `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic + // will be emitted if `can_emit_warnings` is true. + let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings(); + + if !skip { + decorate(&mut err); + } + explain_lint_level_source(lint, level, src, &mut err); err.emit() } diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index 0ee97a6bed037..f70ef51107f25 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -11,12 +11,18 @@ /// [`span_bug`]: crate::span_bug #[macro_export] macro_rules! bug { - () => ( $crate::bug!("impossible case reached") ); - ($msg:expr) => ({ $crate::util::bug::bug_fmt(::std::format_args!($msg)) }); - ($msg:expr,) => ({ $crate::bug!($msg) }); - ($fmt:expr, $($arg:tt)+) => ({ + () => ( + $crate::bug!("impossible case reached") + ); + ($msg:expr) => ( + $crate::util::bug::bug_fmt(::std::format_args!($msg)) + ); + ($msg:expr,) => ( + $crate::bug!($msg) + ); + ($fmt:expr, $($arg:tt)+) => ( $crate::util::bug::bug_fmt(::std::format_args!($fmt, $($arg)+)) - }); + ); } /// A macro for triggering an ICE with a span. @@ -30,11 +36,15 @@ macro_rules! bug { /// [`DiagCtxt::span_delayed_bug`]: rustc_errors::DiagCtxt::span_delayed_bug #[macro_export] macro_rules! span_bug { - ($span:expr, $msg:expr) => ({ $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) }); - ($span:expr, $msg:expr,) => ({ $crate::span_bug!($span, $msg) }); - ($span:expr, $fmt:expr, $($arg:tt)+) => ({ + ($span:expr, $msg:expr) => ( + $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) + ); + ($span:expr, $msg:expr,) => ( + $crate::span_bug!($span, $msg) + ); + ($span:expr, $fmt:expr, $($arg:tt)+) => ( $crate::util::bug::span_bug_fmt($span, ::std::format_args!($fmt, $($arg)+)) - }); + ); } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs index 674402cb4bf9b..589f274eb1766 100644 --- a/compiler/rustc_middle/src/metadata.rs +++ b/compiler/rustc_middle/src/metadata.rs @@ -1,7 +1,7 @@ use crate::ty; use rustc_hir::def::Res; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::def_id::DefId; use rustc_span::symbol::Ident; use smallvec::SmallVec; diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 7d6d39a2a8a39..3fa5054baed1e 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,6 +1,8 @@ use crate::mir::mono::Linkage; use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::Symbol; +use rustc_target::abi::Align; use rustc_target::spec::SanitizerSet; #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] @@ -42,12 +44,12 @@ pub struct CodegenFnAttrs { pub instruction_set: Option, /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be /// aligned to. - pub alignment: Option, + pub alignment: Option, } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub struct CodegenFnAttrFlags(u32); -bitflags! { +bitflags::bitflags! { impl CodegenFnAttrFlags: u32 { /// `#[cold]`: a hint to LLVM that this function, when called, is never on /// the hot path. diff --git a/compiler/rustc_middle/src/middle/debugger_visualizer.rs b/compiler/rustc_middle/src/middle/debugger_visualizer.rs index a0497d805dad0..74a5dfb040099 100644 --- a/compiler/rustc_middle/src/middle/debugger_visualizer.rs +++ b/compiler/rustc_middle/src/middle/debugger_visualizer.rs @@ -1,4 +1,5 @@ use rustc_data_structures::sync::Lrc; +use rustc_macros::{Decodable, Encodable, HashStable}; use std::path::PathBuf; #[derive(HashStable)] diff --git a/compiler/rustc_middle/src/middle/dependency_format.rs b/compiler/rustc_middle/src/middle/dependency_format.rs index e079843bfbc3c..e7d0cffc85cfb 100644 --- a/compiler/rustc_middle/src/middle/dependency_format.rs +++ b/compiler/rustc_middle/src/middle/dependency_format.rs @@ -4,6 +4,7 @@ //! For all the gory details, see the provider of the `dependency_formats` //! query. +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_session::config::CrateType; /// A list of dependencies for a certain crate type. diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index e30b6b203d73c..3b6eecb7fa041 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -1,7 +1,7 @@ use crate::ty::GenericArgsRef; use crate::ty::{self, Ty, TyCtxt}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_macros::HashStable; +use rustc_macros::{Decodable, Encodable, HashStable, TyDecodable, TyEncodable}; /// The SymbolExportLevel of a symbols specifies from which kinds of crates /// the symbol will be exported. `C` symbols will be exported from any @@ -43,6 +43,7 @@ pub enum ExportedSymbol<'tcx> { NonGeneric(DefId), Generic(DefId, GenericArgsRef<'tcx>), DropGlue(Ty<'tcx>), + AsyncDropGlueCtorShim(Ty<'tcx>), ThreadLocalShim(DefId), NoDefId(ty::SymbolName<'tcx>), } @@ -59,6 +60,9 @@ impl<'tcx> ExportedSymbol<'tcx> { ExportedSymbol::DropGlue(ty) => { tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty)) } + ExportedSymbol::AsyncDropGlueCtorShim(ty) => { + tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty)) + } ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance { def: ty::InstanceDef::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index a4e193ba2c942..4fd1c1f4a1b7d 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -1,4 +1,4 @@ -//! Detecting language items. +//! Detecting lang items. //! //! Language items are items that represent concepts intrinsic to the language //! itself. Examples are: diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index bdb2270611a9e..5c395afadd7f0 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -5,6 +5,7 @@ pub mod exported_symbols; pub mod lang_items; pub mod lib_features { use rustc_data_structures::unord::UnordMap; + use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::{symbol::Symbol, Span}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 500536a9e9ed4..46520d69e1838 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -2,7 +2,7 @@ //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. use crate::ty::{TyCtxt, Visibility}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::DefKind; use rustc_macros::HashStable; @@ -90,7 +90,7 @@ impl EffectiveVisibility { /// Holds a map of effective visibilities for reachable HIR nodes. #[derive(Clone, Debug)] pub struct EffectiveVisibilities { - map: FxHashMap, + map: FxIndexMap, } impl EffectiveVisibilities { @@ -130,9 +130,8 @@ impl EffectiveVisibilities { eff_vis: &EffectiveVisibility, tcx: TyCtxt<'_>, ) { - use std::collections::hash_map::Entry; match self.map.entry(def_id) { - Entry::Occupied(mut occupied) => { + IndexEntry::Occupied(mut occupied) => { let old_eff_vis = occupied.get_mut(); for l in Level::all_levels() { let vis_at_level = eff_vis.at_level(l); @@ -145,7 +144,7 @@ impl EffectiveVisibilities { } old_eff_vis } - Entry::Vacant(vacant) => vacant.insert(*eff_vis), + IndexEntry::Vacant(vacant) => vacant.insert(*eff_vis), }; } diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index b9914f6cb7a00..de07ba9700ac1 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -10,8 +10,8 @@ use crate::ty::TyCtxt; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; -use rustc_hir::{HirIdMap, Node}; -use rustc_macros::HashStable; +use rustc_hir::{HirId, HirIdMap, Node}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::{Span, DUMMY_SP}; use std::fmt; @@ -153,7 +153,7 @@ rustc_index::newtype_index! { } // compilation error if size of `ScopeData` is not the same as a `u32` -static_assert_size!(ScopeData, 4); +rustc_data_structures::static_assert_size!(ScopeData, 4); impl Scope { /// Returns an item-local ID associated with this scope. @@ -164,10 +164,10 @@ impl Scope { self.id } - pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option { + pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option { scope_tree .root_body - .map(|hir_id| hir::HirId { owner: hir_id.owner, local_id: self.item_local_id() }) + .map(|hir_id| HirId { owner: hir_id.owner, local_id: self.item_local_id() }) } /// Returns the span of this `Scope`. Note that in general the @@ -207,7 +207,7 @@ pub type ScopeDepth = u32; #[derive(Default, Debug, HashStable)] pub struct ScopeTree { /// If not empty, this body is the root of this region hierarchy. - pub root_body: Option, + pub root_body: Option, /// Maps from a scope ID to the enclosing scope id; /// this is usually corresponding to the lexical nesting, though @@ -341,11 +341,7 @@ impl ScopeTree { self.var_map.insert(var, lifetime); } - pub fn record_rvalue_candidate( - &mut self, - var: hir::HirId, - candidate_type: RvalueCandidateType, - ) { + pub fn record_rvalue_candidate(&mut self, var: HirId, candidate_type: RvalueCandidateType) { debug!("record_rvalue_candidate(var={var:?}, type={candidate_type:?})"); match &candidate_type { RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. } diff --git a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs index 610afd95f3c6d..d0103f622313b 100644 --- a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs +++ b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_hir::{ItemLocalId, OwnerId}; -use rustc_macros::HashStable; +use rustc_macros::{Decodable, Encodable, HashStable, TyDecodable, TyEncodable}; #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, HashStable)] pub enum ResolvedArg { diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index c393287da95c9..67bd53f53daeb 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -14,6 +14,7 @@ use rustc_feature::GateIssue; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_hir::{self as hir, HirId}; +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; use rustc_session::lint::{BuiltinLintDiag, Level, Lint, LintBuffer}; diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 3ecd5b9cd3456..f9398b254c7b1 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -1,5 +1,5 @@ use crate::mir::traversal::Postorder; -use crate::mir::{BasicBlock, BasicBlockData, Successors, Terminator, TerminatorKind, START_BLOCK}; +use crate::mir::{BasicBlock, BasicBlockData, Terminator, TerminatorKind, START_BLOCK}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph; @@ -7,6 +7,7 @@ use rustc_data_structures::graph::dominators::{dominators, Dominators}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::OnceLock; use rustc_index::{IndexSlice, IndexVec}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use smallvec::SmallVec; @@ -141,42 +142,30 @@ impl<'tcx> std::ops::Deref for BasicBlocks<'tcx> { impl<'tcx> graph::DirectedGraph for BasicBlocks<'tcx> { type Node = BasicBlock; -} -impl<'tcx> graph::WithNumNodes for BasicBlocks<'tcx> { #[inline] fn num_nodes(&self) -> usize { self.basic_blocks.len() } } -impl<'tcx> graph::WithStartNode for BasicBlocks<'tcx> { +impl<'tcx> graph::StartNode for BasicBlocks<'tcx> { #[inline] fn start_node(&self) -> Self::Node { START_BLOCK } } -impl<'tcx> graph::WithSuccessors for BasicBlocks<'tcx> { +impl<'tcx> graph::Successors for BasicBlocks<'tcx> { #[inline] - fn successors(&self, node: Self::Node) -> >::Iter { + fn successors(&self, node: Self::Node) -> impl Iterator { self.basic_blocks[node].terminator().successors() } } -impl<'a, 'b> graph::GraphSuccessors<'b> for BasicBlocks<'a> { - type Item = BasicBlock; - type Iter = Successors<'b>; -} - -impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for BasicBlocks<'tcx> { - type Item = BasicBlock; - type Iter = std::iter::Copied>; -} - -impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> { +impl<'tcx> graph::Predecessors for BasicBlocks<'tcx> { #[inline] - fn predecessors(&self, node: Self::Node) -> >::Iter { + fn predecessors(&self, node: Self::Node) -> impl Iterator { self.predecessors()[node].iter().copied() } } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 5feee3c3ba10f..d025dc360a248 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -1,8 +1,9 @@ use std::fmt::{self, Debug, Display, Formatter}; use rustc_hir::def_id::DefId; -use rustc_session::RemapFileNameExt; -use rustc_span::Span; +use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; +use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{HasDataLayout, Size}; use crate::mir::interpret::{alloc_range, AllocId, ConstAllocation, ErrorHandled, Scalar}; @@ -70,8 +71,8 @@ pub enum ConstValue<'tcx> { }, } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(ConstValue<'_>, 24); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(ConstValue<'_>, 24); impl<'tcx> ConstValue<'tcx> { #[inline] @@ -87,7 +88,7 @@ impl<'tcx> ConstValue<'tcx> { } pub fn try_to_bits(&self, size: Size) -> Option { - self.try_to_scalar_int()?.to_bits(size).ok() + self.try_to_scalar_int()?.try_to_bits(size).ok() } pub fn try_to_bool(&self) -> Option { @@ -238,12 +239,28 @@ impl<'tcx> Const<'tcx> { } } + /// Determines whether we need to add this const to `required_consts`. This is the case if and + /// only if evaluating it may error. + #[inline] + pub fn is_required_const(&self) -> bool { + match self { + Const::Ty(c) => match c.kind() { + ty::ConstKind::Value(_) => false, // already a value, cannot error + _ => true, + }, + Const::Val(..) => false, // already a value, cannot error + Const::Unevaluated(..) => true, + } + } + #[inline] pub fn try_to_scalar(self) -> Option { match self { Const::Ty(c) => match c.kind() { ty::ConstKind::Value(valtree) if c.ty().is_primitive() => { // A valtree of a type where leaves directly represent the scalar const value. + // Just checking whether it is a leaf is insufficient as e.g. references are leafs + // but the leaf value is the value they point to, not the reference itself! Some(valtree.unwrap_leaf().into()) } _ => None, @@ -255,12 +272,22 @@ impl<'tcx> Const<'tcx> { #[inline] pub fn try_to_scalar_int(self) -> Option { - self.try_to_scalar()?.try_to_int().ok() + // This is equivalent to `self.try_to_scalar()?.try_to_int().ok()`, but measurably faster. + match self { + Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x), + Const::Ty(c) => match c.kind() { + ty::ConstKind::Value(valtree) if c.ty().is_primitive() => { + Some(valtree.unwrap_leaf()) + } + _ => None, + }, + _ => None, + } } #[inline] pub fn try_to_bits(self, size: Size) -> Option { - self.try_to_scalar_int()?.to_bits(size).ok() + self.try_to_scalar_int()?.try_to_bits(size).ok() } #[inline] @@ -273,7 +300,7 @@ impl<'tcx> Const<'tcx> { self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - span: Option, + span: Span, ) -> Result, ErrorHandled> { match self { Const::Ty(c) => { @@ -293,7 +320,7 @@ impl<'tcx> Const<'tcx> { /// Normalizes the constant to a value or an error if possible. #[inline] pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { - match self.eval(tcx, param_env, None) { + match self.eval(tcx, param_env, DUMMY_SP) { Ok(val) => Self::Val(val, self.ty()), Err(ErrorHandled::Reported(guar, _span)) => { Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty())) @@ -313,10 +340,10 @@ impl<'tcx> Const<'tcx> { // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that // are valtree leaves, and *not* on references. (References should return the // pointer here, which valtrees don't represent.) - let val = c.eval(tcx, param_env, None).ok()?; + let val = c.eval(tcx, param_env, DUMMY_SP).ok()?; Some(val.unwrap_leaf().into()) } - _ => self.eval(tcx, param_env, None).ok()?.try_to_scalar(), + _ => self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar(), } } @@ -334,7 +361,7 @@ impl<'tcx> Const<'tcx> { let int = self.try_eval_scalar_int(tcx, param_env)?; let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size; - int.to_bits(size).ok() + int.try_to_bits(size).ok() } /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. @@ -456,7 +483,7 @@ impl<'tcx> Const<'tcx> { } /// An unevaluated (potentially generic) constant used in MIR. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable)] #[derive(Hash, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct UnevaluatedConst<'tcx> { pub def: DefId, @@ -516,7 +543,11 @@ impl<'tcx> TyCtxt<'tcx> { let caller = self.sess.source_map().lookup_char_pos(topmost.lo()); self.const_caller_location( rustc_span::symbol::Symbol::intern( - &caller.file.name.for_codegen(self.sess).to_string_lossy(), + &caller + .file + .name + .for_scope(self.sess, RemapPathScopeComponents::MACRO) + .to_string_lossy(), ), caller.line as u32, caller.col_display as u32 + 1, diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 645a417c32250..9d9ca22247ad6 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -1,7 +1,7 @@ //! Metadata from source code coverage analysis and instrumentation. use rustc_index::IndexVec; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::{Span, Symbol}; use std::fmt::{self, Debug, Formatter}; @@ -33,10 +33,6 @@ rustc_index::newtype_index! { pub struct CounterId {} } -impl CounterId { - pub const START: Self = Self::from_u32(0); -} - rustc_index::newtype_index! { /// ID of a coverage-counter expression. Values ascend from 0. /// @@ -55,8 +51,23 @@ rustc_index::newtype_index! { pub struct ExpressionId {} } -impl ExpressionId { - pub const START: Self = Self::from_u32(0); +rustc_index::newtype_index! { + /// ID of a mcdc condition. Used by llvm to check mcdc coverage. + /// + /// Note for future: the max limit of 0xFFFF is probably too loose. Actually llvm does not + /// support decisions with too many conditions (7 and more at LLVM 18 while may be hundreds at 19) + /// and represents it with `int16_t`. This max value may be changed once we could + /// figure out an accurate limit. + #[derive(HashStable)] + #[encodable] + #[orderable] + #[max = 0xFFFF] + #[debug_format = "ConditionId({})"] + pub struct ConditionId {} +} + +impl ConditionId { + pub const NONE: Self = Self::from_u32(0); } /// Enum that can hold a constant zero value, the ID of an physical coverage @@ -88,14 +99,13 @@ pub enum CoverageKind { /// Marks a span that might otherwise not be represented in MIR, so that /// coverage instrumentation can associate it with its enclosing block/BCB. /// - /// Only used by the `InstrumentCoverage` pass, and has no effect during - /// codegen. + /// Should be erased before codegen (at some point after `InstrumentCoverage`). SpanMarker, /// Marks its enclosing basic block with an ID that can be referred to by /// side data in [`BranchInfo`]. /// - /// Has no effect during codegen. + /// Should be erased before codegen (at some point after `InstrumentCoverage`). BlockMarker { id: BlockMarkerId }, /// Marks the point in MIR control flow represented by a coverage counter. @@ -115,6 +125,22 @@ pub enum CoverageKind { /// mappings. Intermediate expressions with no direct mappings are /// retained/zeroed based on whether they are transitively used.) ExpressionUsed { id: ExpressionId }, + + /// Marks the point in MIR control flow represented by a evaluated condition. + /// + /// This is eventually lowered to `llvm.instrprof.mcdc.condbitmap.update` in LLVM IR. + /// + /// If this statement does not survive MIR optimizations, the condition would never be + /// taken as evaluated. + CondBitmapUpdate { id: ConditionId, value: bool, decision_depth: u16 }, + + /// Marks the point in MIR control flow represented by a evaluated decision. + /// + /// This is eventually lowered to `llvm.instrprof.mcdc.tvbitmap.update` in LLVM IR. + /// + /// If this statement does not survive MIR optimizations, the decision would never be + /// taken as evaluated. + TestVectorBitmapUpdate { bitmap_idx: u32, decision_depth: u16 }, } impl Debug for CoverageKind { @@ -125,6 +151,18 @@ impl Debug for CoverageKind { BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()), CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()), ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()), + CondBitmapUpdate { id, value, decision_depth } => { + write!( + fmt, + "CondBitmapUpdate({:?}, {:?}, depth={:?})", + id.index(), + value, + decision_depth + ) + } + TestVectorBitmapUpdate { bitmap_idx, decision_depth } => { + write!(fmt, "TestVectorUpdate({:?}, depth={:?})", bitmap_idx, decision_depth) + } } } } @@ -181,16 +219,23 @@ pub enum MappingKind { Code(CovTerm), /// Associates a branch region with separate counters for true and false. Branch { true_term: CovTerm, false_term: CovTerm }, + /// Associates a branch region with separate counters for true and false. + MCDCBranch { true_term: CovTerm, false_term: CovTerm, mcdc_params: ConditionInfo }, + /// Associates a decision region with a bitmap and number of conditions. + MCDCDecision(DecisionInfo), } impl MappingKind { /// Iterator over all coverage terms in this mapping kind. pub fn terms(&self) -> impl Iterator { - let one = |a| std::iter::once(a).chain(None); - let two = |a, b| std::iter::once(a).chain(Some(b)); + let zero = || None.into_iter().chain(None); + let one = |a| Some(a).into_iter().chain(None); + let two = |a, b| Some(a).into_iter().chain(Some(b)); match *self { Self::Code(term) => one(term), Self::Branch { true_term, false_term } => two(true_term, false_term), + Self::MCDCBranch { true_term, false_term, .. } => two(true_term, false_term), + Self::MCDCDecision(_) => zero(), } } @@ -202,6 +247,12 @@ impl MappingKind { Self::Branch { true_term, false_term } => { Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) } } + Self::MCDCBranch { true_term, false_term, mcdc_params } => Self::MCDCBranch { + true_term: map_fn(true_term), + false_term: map_fn(false_term), + mcdc_params, + }, + Self::MCDCDecision(param) => Self::MCDCDecision(param), } } } @@ -221,9 +272,12 @@ pub struct Mapping { pub struct FunctionCoverageInfo { pub function_source_hash: u64, pub num_counters: usize, - + pub mcdc_bitmap_bytes: u32, pub expressions: IndexVec, pub mappings: Vec, + /// The depth of the deepest decision is used to know how many + /// temp condbitmaps should be allocated for the function. + pub mcdc_max_decision_depth: u16, } /// Branch information recorded during THIR-to-MIR lowering, and stored in MIR. @@ -235,6 +289,8 @@ pub struct BranchInfo { /// data structures without having to scan the entire body first. pub num_block_markers: usize, pub branch_spans: Vec, + pub mcdc_branch_spans: Vec, + pub mcdc_decision_spans: Vec, } #[derive(Clone, Debug)] @@ -244,3 +300,49 @@ pub struct BranchSpan { pub true_marker: BlockMarkerId, pub false_marker: BlockMarkerId, } + +#[derive(Copy, Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct ConditionInfo { + pub condition_id: ConditionId, + pub true_next_id: ConditionId, + pub false_next_id: ConditionId, +} + +impl Default for ConditionInfo { + fn default() -> Self { + Self { + condition_id: ConditionId::NONE, + true_next_id: ConditionId::NONE, + false_next_id: ConditionId::NONE, + } + } +} + +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct MCDCBranchSpan { + pub span: Span, + /// If `None`, this actually represents a normal branch span inserted for + /// code that was too complex for MC/DC. + pub condition_info: Option, + pub true_marker: BlockMarkerId, + pub false_marker: BlockMarkerId, + pub decision_depth: u16, +} + +#[derive(Copy, Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct DecisionInfo { + pub bitmap_idx: u32, + pub conditions_num: u16, +} + +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct MCDCDecisionSpan { + pub span: Span, + pub conditions_num: usize, + pub end_markers: Vec, + pub decision_depth: u16, +} diff --git a/compiler/rustc_middle/src/mir/generic_graphviz.rs b/compiler/rustc_middle/src/mir/generic_graphviz.rs index 299b50525cb1e..809d4cdce8dec 100644 --- a/compiler/rustc_middle/src/mir/generic_graphviz.rs +++ b/compiler/rustc_middle/src/mir/generic_graphviz.rs @@ -5,7 +5,7 @@ use std::io::{self, Write}; pub struct GraphvizWriter< 'a, - G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, + G: graph::DirectedGraph + graph::Successors + graph::StartNode, NodeContentFn: Fn(::Node) -> Vec, EdgeLabelsFn: Fn(::Node) -> Vec, > { @@ -19,7 +19,7 @@ pub struct GraphvizWriter< impl< 'a, - G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, + G: graph::DirectedGraph + graph::Successors + graph::StartNode, NodeContentFn: Fn(::Node) -> Vec, EdgeLabelsFn: Fn(::Node) -> Vec, > GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn> diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 4047891d7697b..9cc5c6a2ee940 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -14,6 +14,7 @@ use either::{Left, Right}; use rustc_ast::Mutability; use rustc_data_structures::intern::Interned; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ @@ -37,9 +38,16 @@ pub trait AllocBytes: /// Create a zeroed `AllocBytes` of the specified size and alignment. /// Returns `None` if we ran out of memory on the host. fn zeroed(size: Size, _align: Align) -> Option; + + /// Gives direct access to the raw underlying storage. + /// + /// Crucially this pointer is compatible with: + /// - other pointers retunred by this method, and + /// - references returned from `deref()`, as long as there was no write. + fn as_mut_ptr(&mut self) -> *mut u8; } -// Default `bytes` for `Allocation` is a `Box<[u8]>`. +/// Default `bytes` for `Allocation` is a `Box`. impl AllocBytes for Box<[u8]> { fn from_bytes<'a>(slice: impl Into>, _align: Align) -> Self { Box::<[u8]>::from(slice.into()) @@ -51,6 +59,11 @@ impl AllocBytes for Box<[u8]> { let bytes = unsafe { bytes.assume_init() }; Some(bytes) } + + fn as_mut_ptr(&mut self) -> *mut u8 { + // Carefully avoiding any intermediate references. + ptr::addr_of_mut!(**self).cast() + } } /// This type represents an Allocation in the Miri/CTFE core engine. @@ -399,10 +412,6 @@ impl Allocation /// Byte accessors. impl Allocation { - pub fn base_addr(&self) -> *const u8 { - self.bytes.as_ptr() - } - /// This is the entirely abstraction-violating way to just grab the raw bytes without /// caring about provenance or initialization. /// @@ -452,13 +461,14 @@ impl Allocation Ok(self.get_bytes_unchecked(range)) } - /// Just calling this already marks everything as defined and removes provenance, - /// so be sure to actually put data there! + /// This is the entirely abstraction-violating way to just get mutable access to the raw bytes. + /// Just calling this already marks everything as defined and removes provenance, so be sure to + /// actually overwrite all the data there! /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods /// on `InterpCx` instead. - pub fn get_bytes_mut( + pub fn get_bytes_unchecked_for_overwrite( &mut self, cx: &impl HasDataLayout, range: AllocRange, @@ -469,8 +479,9 @@ impl Allocation Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]) } - /// A raw pointer variant of `get_bytes_mut` that avoids invalidating existing aliases into this memory. - pub fn get_bytes_mut_ptr( + /// A raw pointer variant of `get_bytes_unchecked_for_overwrite` that avoids invalidating existing immutable aliases + /// into this memory. + pub fn get_bytes_unchecked_for_overwrite_ptr( &mut self, cx: &impl HasDataLayout, range: AllocRange, @@ -479,10 +490,19 @@ impl Allocation self.provenance.clear(range, cx)?; assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check + // Cruciall, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`. let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize()); let len = range.end().bytes_usize() - range.start.bytes_usize(); Ok(ptr::slice_from_raw_parts_mut(begin_ptr, len)) } + + /// This gives direct mutable access to the entire buffer, just exposing their internal state + /// without reseting anything. Directly exposes `AllocBytes::as_mut_ptr`. Only works if + /// `OFFSET_IS_ADDR` is true. + pub fn get_bytes_unchecked_raw_mut(&mut self) -> *mut u8 { + assert!(Prov::OFFSET_IS_ADDR); + self.bytes.as_mut_ptr() + } } /// Reading and writing. @@ -589,7 +609,8 @@ impl Allocation }; let endian = cx.data_layout().endian; - let dst = self.get_bytes_mut(cx, range)?; + // Yes we do overwrite all the bytes in `dst`. + let dst = self.get_bytes_unchecked_for_overwrite(cx, range)?; write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also store some provenance. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs index 2c6bb908f39f6..d60db775ff08a 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs @@ -5,6 +5,7 @@ use std::hash; use std::iter; use std::ops::Range; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_serialize::{Decodable, Encodable}; use rustc_target::abi::Size; use rustc_type_ir::{TyDecoder, TyEncoder}; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 9459af490e326..e974279f1917c 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -4,6 +4,7 @@ use std::cmp; use rustc_data_structures::sorted_map::SortedMap; +use rustc_macros::HashStable; use rustc_target::abi::{HasDataLayout, Size}; use super::{alloc_range, AllocError, AllocRange, AllocResult, CtfeProvenance, Provenance}; diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 751d6de83f92c..383241465c3d1 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -2,12 +2,12 @@ use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar}; use crate::error; use crate::mir::{ConstAlloc, ConstValue}; -use crate::ty::{layout, tls, Ty, TyCtxt, ValTree}; +use crate::ty::{self, layout, tls, Ty, TyCtxt, ValTree}; use rustc_ast_ir::Mutability; use rustc_data_structures::sync::Lock; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_session::CtfeBacktrace; use rustc_span::{def_id::DefId, Span, DUMMY_SP}; use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange}; @@ -88,8 +88,8 @@ pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; /// This is needed in `thir::pattern::lower_inline_const`. pub type EvalToValTreeResult<'tcx> = Result>, ErrorHandled>; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(InterpErrorInfo<'_>, 8); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(InterpErrorInfo<'_>, 8); /// Packages the kind of error we got from the const code interpreter /// up with a Rust-level backtrace of where the error occurred. @@ -344,6 +344,11 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidFunctionPointer(Pointer), /// Using a pointer-not-to-a-vtable as vtable pointer. InvalidVTablePointer(Pointer), + /// Using a vtable for the wrong trait. + InvalidVTableTrait { + expected_trait: &'tcx ty::List>, + vtable_trait: Option>, + }, /// Using a string that is not valid UTF-8, InvalidStr(std::str::Utf8Error), /// Using uninitialized data where it is not allowed. @@ -414,34 +419,86 @@ impl From for ExpectedKind { #[derive(Debug)] pub enum ValidationErrorKind<'tcx> { - PointerAsInt { expected: ExpectedKind }, + PointerAsInt { + expected: ExpectedKind, + }, PartialPointer, - PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> }, - PtrToStatic { ptr_kind: PointerKind }, + PtrToUninhabited { + ptr_kind: PointerKind, + ty: Ty<'tcx>, + }, + PtrToStatic { + ptr_kind: PointerKind, + }, ConstRefToMutable, ConstRefToExtern, MutableRefToImmutable, UnsafeCellInImmutable, NullFnPtr, NeverVal, - NullablePtrOutOfRange { range: WrappingRange, max_value: u128 }, - PtrOutOfRange { range: WrappingRange, max_value: u128 }, - OutOfRange { value: String, range: WrappingRange, max_value: u128 }, - UninhabitedVal { ty: Ty<'tcx> }, - InvalidEnumTag { value: String }, + NullablePtrOutOfRange { + range: WrappingRange, + max_value: u128, + }, + PtrOutOfRange { + range: WrappingRange, + max_value: u128, + }, + OutOfRange { + value: String, + range: WrappingRange, + max_value: u128, + }, + UninhabitedVal { + ty: Ty<'tcx>, + }, + InvalidEnumTag { + value: String, + }, UninhabitedEnumVariant, - Uninit { expected: ExpectedKind }, - InvalidVTablePtr { value: String }, - InvalidMetaSliceTooLarge { ptr_kind: PointerKind }, - InvalidMetaTooLarge { ptr_kind: PointerKind }, - UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 }, - NullPtr { ptr_kind: PointerKind }, - DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String }, - DanglingPtrOutOfBounds { ptr_kind: PointerKind }, - DanglingPtrUseAfterFree { ptr_kind: PointerKind }, - InvalidBool { value: String }, - InvalidChar { value: String }, - InvalidFnPtr { value: String }, + Uninit { + expected: ExpectedKind, + }, + InvalidVTablePtr { + value: String, + }, + InvalidMetaWrongTrait { + expected_trait: &'tcx ty::List>, + vtable_trait: Option>, + }, + InvalidMetaSliceTooLarge { + ptr_kind: PointerKind, + }, + InvalidMetaTooLarge { + ptr_kind: PointerKind, + }, + UnalignedPtr { + ptr_kind: PointerKind, + required_bytes: u64, + found_bytes: u64, + }, + NullPtr { + ptr_kind: PointerKind, + }, + DanglingPtrNoProvenance { + ptr_kind: PointerKind, + pointer: String, + }, + DanglingPtrOutOfBounds { + ptr_kind: PointerKind, + }, + DanglingPtrUseAfterFree { + ptr_kind: PointerKind, + }, + InvalidBool { + value: String, + }, + InvalidChar { + value: String, + }, + InvalidFnPtr { + value: String, + }, } /// Error information for when the program did something that might (or might not) be correct @@ -482,6 +539,8 @@ pub enum ResourceExhaustionInfo { MemoryExhausted, /// The address space (of the target) is full. AddressSpaceFull, + /// The compiler got an interrupt signal (a user ran out of patience). + Interrupted, } /// A trait for machine-specific errors (or other "machine stop" conditions). diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index f9edbb3c5ae21..ee3cdf36820de 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -131,7 +131,7 @@ use rustc_data_structures::sync::{HashMapExt, Lock}; use rustc_data_structures::tiny_list::TinyList; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_serialize::{Decodable, Encodable}; use rustc_target::abi::{AddressSpace, Endian, HasDataLayout}; @@ -671,11 +671,11 @@ pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result { - source.read(&mut buf)?; + source.read_exact(&mut buf[..source.len()])?; Ok(u128::from_le_bytes(buf)) } Endian::Big => { - source.read(&mut buf[16 - source.len()..])?; + source.read_exact(&mut buf[16 - source.len()..])?; Ok(u128::from_be_bytes(buf)) } }; diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index e2767ee298958..a0acacc844fd5 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -1,6 +1,7 @@ use super::{AllocId, InterpResult}; -use rustc_macros::HashStable; +use rustc_data_structures::static_assert_size; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_target::abi::{HasDataLayout, Size}; use std::{fmt, num::NonZero}; diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 643b61c1de3d7..04d6301116eea 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -24,7 +24,7 @@ impl<'tcx> TyCtxt<'tcx> { let instance = ty::Instance::new(def_id, args); let cid = GlobalId { instance, promoted: None }; let param_env = self.param_env(def_id).with_reveal_all_normalized(self); - self.const_eval_global_id(param_env, cid, None) + self.const_eval_global_id(param_env, cid, DUMMY_SP) } /// Resolves and evaluates a constant. /// @@ -40,7 +40,7 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, ct: mir::UnevaluatedConst<'tcx>, - span: Option, + span: Span, ) -> EvalToConstValueResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference // variables. We reject those here since `resolve` @@ -73,7 +73,7 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, ct: ty::UnevaluatedConst<'tcx>, - span: Option, + span: Span, ) -> EvalToValTreeResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference // variables. We reject those here since `resolve` @@ -130,7 +130,7 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, - span: Option, + span: Span, ) -> EvalToConstValueResult<'tcx> { self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span) } @@ -141,12 +141,12 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, cid: GlobalId<'tcx>, - span: Option, + span: Span, ) -> EvalToConstValueResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid)); - if let Some(span) = span { + if !span.is_dummy() { // The query doesn't know where it is being invoked, so we need to fix the span. self.at(span).eval_to_const_value_raw(inputs).map_err(|e| e.with_span(span)) } else { @@ -160,13 +160,13 @@ impl<'tcx> TyCtxt<'tcx> { self, param_env: ty::ParamEnv<'tcx>, cid: GlobalId<'tcx>, - span: Option, + span: Span, ) -> EvalToValTreeResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid)); debug!(?inputs); - if let Some(span) = span { + if !span.is_dummy() { // The query doesn't know where it is being invoked, so we need to fix the span. self.at(span).eval_to_valtree(inputs).map_err(|e| e.with_span(span)) } else { diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 24d4a79c7d795..c5c87c506b77f 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -6,7 +6,7 @@ use rustc_apfloat::{ ieee::{Double, Half, Quad, Single}, Float, }; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_target::abi::{HasDataLayout, Size}; use crate::ty::ScalarInt; @@ -37,8 +37,8 @@ pub enum Scalar { Ptr(Pointer, u8), } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(Scalar, 24); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(Scalar, 24); // We want the `Debug` output to be readable as it is used by `derive(Debug)` for // all the Miri types. @@ -236,7 +236,7 @@ impl Scalar { ) -> Result>, ScalarSizeMismatch> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); Ok(match self { - Scalar::Int(int) => Left(int.to_bits(target_size).map_err(|size| { + Scalar::Int(int) => Left(int.try_to_bits(target_size).map_err(|size| { ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes() } })?), Scalar::Ptr(ptr, sz) => { @@ -300,6 +300,11 @@ impl<'tcx, Prov: Provenance> Scalar { } } + #[inline(always)] + pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> { + self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into()) + } + #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn assert_int(self) -> ScalarInt { @@ -311,16 +316,13 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline] pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); - self.try_to_int() - .map_err(|_| err_unsup!(ReadPointerAsInt(None)))? - .to_bits(target_size) - .map_err(|size| { - err_ub!(ScalarSizeMismatch(ScalarSizeMismatch { - target_size: target_size.bytes(), - data_size: size.bytes(), - })) - .into() - }) + self.to_scalar_int()?.try_to_bits(target_size).map_err(|size| { + err_ub!(ScalarSizeMismatch(ScalarSizeMismatch { + target_size: target_size.bytes(), + data_size: size.bytes(), + })) + .into() + }) } #[inline(always)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d57ffc0f8b5c5..25cc9ac47c83a 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -17,9 +17,12 @@ use rustc_data_structures::captures::Captures; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; -use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind}; -use rustc_hir::{self as hir, HirId}; +use rustc_hir::{ + self as hir, BindingMode, ByRef, CoroutineDesugaring, CoroutineKind, HirId, ImplicitSelfKind, +}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_session::Session; +use rustc_span::source_map::Spanned; use rustc_target::abi::{FieldIdx, VariantIdx}; use polonius_engine::Atom; @@ -27,7 +30,6 @@ pub use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::dominators::Dominators; -use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::bit_set::BitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_serialize::{Decodable, Encodable}; @@ -44,6 +46,7 @@ use std::ops::{Index, IndexMut}; use std::{iter, mem}; pub use self::query::*; +use self::visit::TyContext; pub use basic_blocks::BasicBlocks; mod basic_blocks; @@ -278,13 +281,6 @@ pub struct CoroutineInfo<'tcx> { /// using `run_passes`. pub by_move_body: Option>, - /// The body of the coroutine, modified to take its upvars by mutable ref rather than by - /// immutable ref. - /// - /// FIXME(async_closures): This is literally the same body as the parent body. Find a better - /// way to represent the by-mut signature (or cap the closure-kind of the coroutine). - pub by_mut_body: Option>, - /// The layout of a coroutine. This field is populated after the state transform pass. pub coroutine_layout: Option>, @@ -305,13 +301,27 @@ impl<'tcx> CoroutineInfo<'tcx> { yield_ty: Some(yield_ty), resume_ty: Some(resume_ty), by_move_body: None, - by_mut_body: None, coroutine_drop: None, coroutine_layout: None, } } } +/// Some item that needs to monomorphize successfully for a MIR body to be considered well-formed. +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeFoldable, TypeVisitable)] +pub enum MentionedItem<'tcx> { + /// A function that gets called. We don't necessarily know its precise type yet, since it can be + /// hidden behind a generic. + Fn(Ty<'tcx>), + /// A type that has its drop shim called. + Drop(Ty<'tcx>), + /// Unsizing casts might require vtables, so we have to record them. + UnsizeCast { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> }, + /// A closure that is coerced to a function pointer. + Closure(Ty<'tcx>), +} + /// The lowered representation of a single function. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct Body<'tcx> { @@ -375,8 +385,24 @@ pub struct Body<'tcx> { /// Constants that are required to evaluate successfully for this MIR to be well-formed. /// We hold in this field all the constants we are not able to evaluate yet. + /// + /// This is soundness-critical, we make a guarantee that all consts syntactically mentioned in a + /// function have successfully evaluated if the function ever gets executed at runtime. pub required_consts: Vec>, + /// Further items that were mentioned in this function and hence *may* become monomorphized, + /// depending on optimizations. We use this to avoid optimization-dependent compile errors: the + /// collector recursively traverses all "mentioned" items and evaluates all their + /// `required_consts`. + /// + /// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee. + /// All that's relevant is that this set is optimization-level-independent, and that it includes + /// everything that the collector would consider "used". (For example, we currently compute this + /// set after drop elaboration, so some drop calls that can never be reached are not considered + /// "mentioned".) See the documentation of `CollectionMode` in + /// `compiler/rustc_monomorphize/src/collector.rs` for more context. + pub mentioned_items: Vec>>, + /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. /// /// Note that this does not actually mean that this body is not computable right now. @@ -453,6 +479,7 @@ impl<'tcx> Body<'tcx> { var_debug_info, span, required_consts: Vec::new(), + mentioned_items: Vec::new(), is_polymorphic: false, injection_phase: None, tainted_by_errors, @@ -482,6 +509,7 @@ impl<'tcx> Body<'tcx> { spread_arg: None, span: DUMMY_SP, required_consts: Vec::new(), + mentioned_items: Vec::new(), var_debug_info: Vec::new(), is_polymorphic: false, injection_phase: None, @@ -576,6 +604,17 @@ impl<'tcx> Body<'tcx> { } } + pub fn span_for_ty_context(&self, ty_context: TyContext) -> Span { + match ty_context { + TyContext::UserTy(span) => span, + TyContext::ReturnTy(source_info) + | TyContext::LocalDecl { source_info, .. } + | TyContext::YieldTy(source_info) + | TyContext::ResumeTy(source_info) => source_info.span, + TyContext::Location(loc) => self.source_info(loc).span, + } + } + /// Returns the return type; it always return first element from `local_decls` array. #[inline] pub fn return_ty(&self) -> Ty<'tcx> { @@ -614,8 +653,9 @@ impl<'tcx> Body<'tcx> { self.coroutine.as_ref().and_then(|coroutine| coroutine.resume_ty) } + /// Prefer going through [`TyCtxt::coroutine_layout`] rather than using this directly. #[inline] - pub fn coroutine_layout(&self) -> Option<&CoroutineLayout<'tcx>> { + pub fn coroutine_layout_raw(&self) -> Option<&CoroutineLayout<'tcx>> { self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_layout.as_ref()) } @@ -628,10 +668,6 @@ impl<'tcx> Body<'tcx> { self.coroutine.as_ref()?.by_move_body.as_ref() } - pub fn coroutine_by_mut_body(&self) -> Option<&Body<'tcx>> { - self.coroutine.as_ref()?.by_mut_body.as_ref() - } - #[inline] pub fn coroutine_kind(&self) -> Option { self.coroutine.as_ref().map(|coroutine| coroutine.coroutine_kind) @@ -650,57 +686,6 @@ impl<'tcx> Body<'tcx> { self.injection_phase.is_some() } - /// Finds which basic blocks are actually reachable for a specific - /// monomorphization of this body. - /// - /// This is allowed to have false positives; just because this says a block - /// is reachable doesn't mean that's necessarily true. It's thus always - /// legal for this to return a filled set. - /// - /// Regardless, the [`BitSet::domain_size`] of the returned set will always - /// exactly match the number of blocks in the body so that `contains` - /// checks can be done without worrying about panicking. - /// - /// This is mostly useful because it lets us skip lowering the `false` side - /// of `if ::CONST`, as well as `intrinsics::debug_assertions`. - pub fn reachable_blocks_in_mono( - &self, - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - ) -> BitSet { - let mut set = BitSet::new_empty(self.basic_blocks.len()); - self.reachable_blocks_in_mono_from(tcx, instance, &mut set, START_BLOCK); - set - } - - fn reachable_blocks_in_mono_from( - &self, - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - set: &mut BitSet, - bb: BasicBlock, - ) { - if !set.insert(bb) { - return; - } - - let data = &self.basic_blocks[bb]; - - if let Some((bits, targets)) = Self::try_const_mono_switchint(tcx, instance, data) { - let target = targets.target_for_value(bits); - ensure_sufficient_stack(|| { - self.reachable_blocks_in_mono_from(tcx, instance, set, target) - }); - return; - } - - for target in data.terminator().successors() { - ensure_sufficient_stack(|| { - self.reachable_blocks_in_mono_from(tcx, instance, set, target) - }); - } - } - /// If this basic block ends with a [`TerminatorKind::SwitchInt`] for which we can evaluate the /// dimscriminant in monomorphization, we return the discriminant bits and the /// [`SwitchTargets`], just so the caller doesn't also have to match on the terminator. @@ -717,10 +702,7 @@ impl<'tcx> Body<'tcx> { env, crate::ty::EarlyBinder::bind(constant.const_), ); - let Some(bits) = mono_literal.try_eval_bits(tcx, env) else { - bug!("Couldn't evaluate constant {:?} in mono {:?}", constant, instance); - }; - bits + mono_literal.try_eval_bits(tcx, env) }; let TerminatorKind::SwitchInt { discr, targets } = &block.terminator().kind else { @@ -730,7 +712,7 @@ impl<'tcx> Body<'tcx> { // If this is a SwitchInt(const _), then we can just evaluate the constant and return. let discr = match discr { Operand::Constant(constant) => { - let bits = eval_mono_const(constant); + let bits = eval_mono_const(constant)?; return Some((bits, targets)); } Operand::Move(place) | Operand::Copy(place) => place, @@ -740,8 +722,8 @@ impl<'tcx> Body<'tcx> { // _1 = const _ // SwitchInt(_1) // - // And MIR for if intrinsics::debug_assertions() looks like this: - // _1 = cfg!(debug_assertions) + // And MIR for if intrinsics::ub_checks() looks like this: + // _1 = UbChecks() // SwitchInt(_1) // // So we're going to try to recognize this pattern. @@ -762,11 +744,9 @@ impl<'tcx> Body<'tcx> { } match rvalue { - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => { - Some((tcx.sess.opts.debug_assertions as u128, targets)) - } + Rvalue::NullaryOp(NullOp::UbChecks, _) => Some((tcx.sess.ub_checks() as u128, targets)), Rvalue::Use(Operand::Constant(constant)) => { - let bits = eval_mono_const(constant); + let bits = eval_mono_const(constant)?; Some((bits, targets)) } _ => None, @@ -808,17 +788,6 @@ impl<'tcx> Body<'tcx> { } } -#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)] -pub enum Safety { - Safe, - /// Unsafe because of compiler-generated unsafe code, like `await` desugaring - BuiltinUnsafe, - /// Unsafe because of an unsafe fn - FnUnsafe, - /// Unsafe because of an `unsafe` block - ExplicitUnsafe(hir::HirId), -} - impl<'tcx> Index for Body<'tcx> { type Output = BasicBlockData<'tcx>; @@ -957,8 +926,8 @@ pub enum LocalKind { #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct VarBindingForm<'tcx> { - /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? - pub binding_mode: ty::BindingMode, + /// Is variable bound via `x`, `mut x`, `ref x`, `ref mut x`, `mut ref x`, or `mut ref mut x`? + pub binding_mode: BindingMode, /// If an explicit type was provided for this variable binding, /// this holds the source Span of that type. /// @@ -1183,7 +1152,7 @@ impl<'tcx> LocalDecl<'tcx> { self.local_info(), LocalInfo::User( BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), + binding_mode: BindingMode(ByRef::No, _), opt_ty_info: _, opt_match_place: _, pat_span: _, @@ -1200,7 +1169,7 @@ impl<'tcx> LocalDecl<'tcx> { self.local_info(), LocalInfo::User( BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), + binding_mode: BindingMode(ByRef::No, _), opt_ty_info: _, opt_match_place: _, pat_span: _, @@ -1573,9 +1542,7 @@ pub struct SourceScopeData<'tcx> { #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct SourceScopeLocalData { /// An `HirId` with lint levels equivalent to this scope's lint levels. - pub lint_root: hir::HirId, - /// The unsafe block that contains this node. - pub safety: Safety, + pub lint_root: HirId, } /// A collection of projections into user types. @@ -1844,18 +1811,16 @@ impl DefLocation { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(BasicBlockData<'_>, 144); static_assert_size!(LocalDecl<'_>, 40); - static_assert_size!(SourceScopeData<'_>, 72); + static_assert_size!(SourceScopeData<'_>, 64); static_assert_size!(Statement<'_>, 32); - static_assert_size!(StatementKind<'_>, 16); static_assert_size!(Terminator<'_>, 112); - static_assert_size!(TerminatorKind<'_>, 96); static_assert_size!(VarDebugInfo<'_>, 88); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 43e1318a75ab1..6d4585cfbc66a 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -9,6 +9,7 @@ use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::ItemId; use rustc_index::Idx; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::config::OptLevel; use rustc_span::symbol::Symbol; @@ -65,7 +66,9 @@ impl<'tcx> MonoItem<'tcx> { match instance.def { // "Normal" functions size estimate: the number of // statements, plus one for the terminator. - InstanceDef::Item(..) | InstanceDef::DropGlue(..) => { + InstanceDef::Item(..) + | InstanceDef::DropGlue(..) + | InstanceDef::AsyncDropGlueCtorShim(..) => { let mir = tcx.instance_mir(instance.def); mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum() } @@ -406,7 +409,8 @@ impl<'tcx> CodegenUnit<'tcx> { | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) | InstanceDef::ThreadLocalShim(..) - | InstanceDef::FnPtrAddrShim(..) => None, + | InstanceDef::FnPtrAddrShim(..) + | InstanceDef::AsyncDropGlueCtorShim(..) => None, } } MonoItem::Static(def_id) => def_id.as_local().map(Idx::index), diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 94751c4476157..5aaa1c30cade0 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::interpret::{ Provenance, }; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, *}; +use rustc_middle::mir::*; use rustc_target::abi::Size; const INDENT: &str = " "; @@ -126,7 +126,7 @@ fn dump_matched_mir_node<'tcx, F>( Some(promoted) => write!(file, "::{promoted:?}`")?, } writeln!(file, " {disambiguator} {pass_name}")?; - if let Some(ref layout) = body.coroutine_layout() { + if let Some(ref layout) = body.coroutine_layout_raw() { writeln!(file, "/* coroutine_layout = {layout:#?} */")?; } writeln!(file)?; @@ -187,6 +187,17 @@ fn dump_path<'tcx>( })); s } + ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => { + // Unfortunately, pretty-printed typed are not very filename-friendly. + // We dome some filtering. + let mut s = ".".to_owned(); + s.extend(ty.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s + } _ => String::new(), }; @@ -475,7 +486,8 @@ fn write_coverage_branch_info( branch_info: &coverage::BranchInfo, w: &mut dyn io::Write, ) -> io::Result<()> { - let coverage::BranchInfo { branch_spans, .. } = branch_info; + let coverage::BranchInfo { branch_spans, mcdc_branch_spans, mcdc_decision_spans, .. } = + branch_info; for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans { writeln!( @@ -483,7 +495,33 @@ fn write_coverage_branch_info( "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", )?; } - if !branch_spans.is_empty() { + + for coverage::MCDCBranchSpan { + span, + condition_info, + true_marker, + false_marker, + decision_depth, + } in mcdc_branch_spans + { + writeln!( + w, + "{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?}, depth: {decision_depth:?} }} => {span:?}", + condition_info.map(|info| info.condition_id) + )?; + } + + for coverage::MCDCDecisionSpan { span, conditions_num, end_markers, decision_depth } in + mcdc_decision_spans + { + writeln!( + w, + "{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}" + )?; + } + + if !branch_spans.is_empty() || !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty() + { writeln!(w)?; } @@ -711,7 +749,7 @@ impl Debug for Statement<'_> { AscribeUserType(box (ref place, ref c_ty), ref variance) => { write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})") } - Coverage(box mir::Coverage { ref kind }) => write!(fmt, "Coverage::{kind:?}"), + Coverage(ref kind) => write!(fmt, "Coverage::{kind:?}"), Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"), ConstEvalCounter => write!(fmt, "ConstEvalCounter"), Nop => write!(fmt, "nop"), @@ -944,7 +982,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { NullOp::SizeOf => write!(fmt, "SizeOf({t})"), NullOp::AlignOf => write!(fmt, "AlignOf({t})"), NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), - NullOp::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"), + NullOp::UbChecks => write!(fmt, "UbChecks()"), } } ThreadLocalRef(did) => ty::tls::with(|tcx| { @@ -954,7 +992,8 @@ impl<'tcx> Debug for Rvalue<'tcx> { Ref(region, borrow_kind, ref place) => { let kind_str = match borrow_kind { BorrowKind::Shared => "", - BorrowKind::Fake => "fake ", + BorrowKind::Fake(FakeBorrowKind::Deep) => "fake ", + BorrowKind::Fake(FakeBorrowKind::Shallow) => "fake shallow ", BorrowKind::Mut { .. } => "mut ", }; @@ -978,12 +1017,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) => { @@ -1079,6 +1113,15 @@ impl<'tcx> Debug for Rvalue<'tcx> { struct_fmt.finish() }), + + AggregateKind::RawPtr(pointee_ty, mutability) => { + let kind_str = match mutability { + Mutability::Mut => "mut", + Mutability::Not => "const", + }; + with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?; + fmt_tuple(fmt, "") + } } } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 8bd872c1b19e1..9a7af6135e46f 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -3,12 +3,11 @@ use crate::mir; use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::unord::UnordSet; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_index::bit_set::BitMatrix; use rustc_index::{Idx, IndexVec}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; @@ -18,67 +17,6 @@ use std::fmt::{self, Debug}; use super::{ConstValue, SourceInfo}; -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] -pub enum UnsafetyViolationKind { - /// Unsafe operation outside `unsafe`. - General, - /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. - /// Has to be handled as a lint for backwards compatibility. - UnsafeFn, -} - -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] -pub enum UnsafetyViolationDetails { - CallToUnsafeFunction, - UseOfInlineAssembly, - InitializingTypeWith, - CastOfPointerToInt, - UseOfMutableStatic, - UseOfExternStatic, - DerefOfRawPointer, - AccessToUnionField, - MutationOfLayoutConstrainedField, - BorrowOfLayoutConstrainedField, - CallToFunctionWith { - /// Target features enabled in callee's `#[target_feature]` but missing in - /// caller's `#[target_feature]`. - missing: Vec, - /// Target features in `missing` that are enabled at compile time - /// (e.g., with `-C target-feature`). - build_enabled: Vec, - }, -} - -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] -pub struct UnsafetyViolation { - pub source_info: SourceInfo, - pub lint_root: hir::HirId, - pub kind: UnsafetyViolationKind, - pub details: UnsafetyViolationDetails, -} - -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] -pub enum UnusedUnsafe { - /// `unsafe` block contains no unsafe operations - /// > ``unnecessary `unsafe` block`` - Unused, - /// `unsafe` block nested under another (used) `unsafe` block - /// > ``… because it's nested under this `unsafe` block`` - InUnsafeBlock(hir::HirId), -} - -#[derive(TyEncodable, TyDecodable, HashStable, Debug)] -pub struct UnsafetyCheckResult { - /// Violations that are propagated *upwards* from this function. - pub violations: Vec, - - /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint. - pub used_unsafe_blocks: UnordSet, - - /// This is `Some` iff the item is not a closure. - pub unused_unsafes: Option>, -} - rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] @@ -276,7 +214,7 @@ pub struct ClosureOutlivesRequirement<'tcx> { } // Make sure this enum doesn't unintentionally grow -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// Outlives-constraints can be categorized to determine whether and why they @@ -284,8 +222,15 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// order of the category, thereby influencing diagnostic output. /// /// See also `rustc_const_eval::borrow_check::constraints`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] +#[derive(derivative::Derivative)] +#[derivative( + PartialOrd, + Ord, + PartialOrd = "feature_allow_slow_enum", + Ord = "feature_allow_slow_enum" +)] pub enum ConstraintCategory<'tcx> { Return(ReturnConstraint), Yield, @@ -295,6 +240,7 @@ pub enum ConstraintCategory<'tcx> { Cast { /// Whether this is an unsizing cast and if yes, this contains the target type. /// Region variables are erased to ReErased. + #[derivative(PartialOrd = "ignore", Ord = "ignore")] unsize_to: Option>, }, @@ -304,7 +250,7 @@ pub enum ConstraintCategory<'tcx> { ClosureBounds, /// Contains the function type if available. - CallArgument(Option>), + CallArgument(#[derivative(PartialOrd = "ignore", Ord = "ignore")] Option>), CopyBound, SizedBound, Assignment, @@ -416,4 +362,8 @@ pub struct CoverageIdsInfo { /// InstrumentCoverage MIR pass, if the highest-numbered counter increments /// were removed by MIR optimizations. pub max_counter_id: mir::coverage::CounterId, + + /// Coverage codegen for mcdc needs to know the size of the global bitmap so that it can + /// set the `bytemap-bytes` argument of the `llvm.instrprof.mcdc.tvbitmap.update` intrinsic. + pub mcdc_bitmap_bytes: u32, } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index f929a5cec2533..375f1f15a39ed 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -236,6 +236,11 @@ impl<'tcx> PlaceRef<'tcx> { } } + #[inline] + pub fn to_place(&self, tcx: TyCtxt<'tcx>) -> Place<'tcx> { + Place { local: self.local, projection: tcx.mk_place_elems(self.projection) } + } + #[inline] pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> { if let &[ref proj_base @ .., elem] = self.projection { @@ -409,7 +414,7 @@ impl<'tcx> Rvalue<'tcx> { // Pointer to int casts may be side-effects due to exposing the provenance. // While the model is undecided, we should be conservative. See // - Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false, + Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => false, Rvalue::Use(_) | Rvalue::CopyForDeref(_) @@ -426,7 +431,7 @@ impl<'tcx> Rvalue<'tcx> { | CastKind::FnPtrToPtr | CastKind::PtrToPtr | CastKind::PointerCoercion(_) - | CastKind::PointerFromExposedAddress + | CastKind::PointerWithExposedProvenance | CastKind::DynStar | CastKind::Transmute, _, @@ -446,7 +451,7 @@ impl<'tcx> Rvalue<'tcx> { impl BorrowKind { pub fn mutability(&self) -> Mutability { match *self { - BorrowKind::Shared | BorrowKind::Fake => Mutability::Not, + BorrowKind::Shared | BorrowKind::Fake(_) => Mutability::Not, BorrowKind::Mut { .. } => Mutability::Mut, } } @@ -454,7 +459,7 @@ impl BorrowKind { pub fn allows_two_phase_borrow(&self) -> bool { match *self { BorrowKind::Shared - | BorrowKind::Fake + | BorrowKind::Fake(_) | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => { false } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 0ab797134c0af..4278ce823d0e0 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -17,6 +17,7 @@ use rustc_data_structures::packed::Pu128; use rustc_hir::def_id::DefId; use rustc_hir::CoroutineKind; use rustc_index::IndexVec; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::source_map::Spanned; use rustc_target::abi::{FieldIdx, VariantIdx}; @@ -124,6 +125,7 @@ pub enum AnalysisPhase { /// * [`TerminatorKind::FalseEdge`] /// * [`StatementKind::FakeRead`] /// * [`StatementKind::AscribeUserType`] + /// * [`StatementKind::Coverage`] with [`CoverageKind::BlockMarker`] or [`CoverageKind::SpanMarker`] /// * [`Rvalue::Ref`] with `BorrowKind::Fake` /// /// Furthermore, `Deref` projections must be the first projection within any place (if they @@ -164,13 +166,16 @@ pub enum BorrowKind { /// Data must be immutable and is aliasable. Shared, - /// The immediately borrowed place must be immutable, but projections from - /// it don't need to be. For example, a shallow borrow of `a.b` doesn't - /// conflict with a mutable borrow of `a.b.c`. + /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either + /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]). /// - /// This is used when lowering matches: when matching on a place we want to - /// ensure that place have the same value from the start of the match until - /// an arm is selected. This prevents this code from compiling: + /// This is used when lowering index expressions and matches. This is used to prevent code like + /// the following from compiling: + /// ```compile_fail,E0510 + /// let mut x: &[_] = &[[0, 1]]; + /// let y: &[_] = &[]; + /// let _ = x[0][{x = y; 1}]; + /// ``` /// ```compile_fail,E0510 /// let mut x = &Some(0); /// match *x { @@ -179,11 +184,8 @@ pub enum BorrowKind { /// Some(_) => (), /// } /// ``` - /// This can't be a shared borrow because mutably borrowing (*x as Some).0 - /// should not prevent `if let None = x { ... }`, for example, because the - /// mutating `(*x as Some).0` can't affect the discriminant of `x`. /// We can also report errors with this kind of borrow differently. - Fake, + Fake(FakeBorrowKind), /// Data is mutable and not aliasable. Mut { kind: MutBorrowKind }, @@ -239,6 +241,57 @@ pub enum MutBorrowKind { ClosureCapture, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Hash, HashStable)] +pub enum FakeBorrowKind { + /// A shared shallow borrow. The immediately borrowed place must be immutable, but projections + /// from it don't need to be. For example, a shallow borrow of `a.b` doesn't conflict with a + /// mutable borrow of `a.b.c`. + /// + /// This is used when lowering matches: when matching on a place we want to ensure that place + /// have the same value from the start of the match until an arm is selected. This prevents this + /// code from compiling: + /// ```compile_fail,E0510 + /// let mut x = &Some(0); + /// match *x { + /// None => (), + /// Some(_) if { x = &None; false } => (), + /// Some(_) => (), + /// } + /// ``` + /// This can't be a shared borrow because mutably borrowing `(*x as Some).0` should not checking + /// the discriminant or accessing other variants, because the mutating `(*x as Some).0` can't + /// affect the discriminant of `x`. E.g. the following is allowed: + /// ```rust + /// let mut x = Some(0); + /// match x { + /// Some(_) + /// if { + /// if let Some(ref mut y) = x { + /// *y += 1; + /// }; + /// true + /// } => {} + /// _ => {} + /// } + /// ``` + Shallow, + /// A shared (deep) borrow. Data must be immutable and is aliasable. + /// + /// This is used when lowering deref patterns, where shallow borrows wouldn't prevent something + /// like: + // ```compile_fail + // let mut b = Box::new(false); + // match b { + // deref!(true) => {} // not reached because `*b == false` + // _ if { *b = true; false } => {} // not reached because the guard is `false` + // deref!(false) => {} // not reached because the guard changed it + // // UB because we reached the unreachable. + // } + // ``` + Deep, +} + /////////////////////////////////////////////////////////////////////////// // Statements @@ -372,7 +425,7 @@ pub enum StatementKind<'tcx> { /// /// Interpreters and codegen backends that don't support coverage instrumentation /// can usually treat this as a no-op. - Coverage(Box), + Coverage(CoverageKind), /// Denotes a call to an intrinsic that does not require an unwind path and always returns. /// This avoids adding a new block and a terminator for simple intrinsics. @@ -516,12 +569,6 @@ pub enum FakeReadCause { ForIndex, } -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct Coverage { - pub kind: CoverageKind, -} - #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub struct CopyNonOverlapping<'tcx> { @@ -1314,11 +1361,11 @@ pub enum Rvalue<'tcx> { pub enum CastKind { /// An exposing pointer to address cast. A cast between a pointer and an integer type, or /// between a function pointer and an integer type. - /// See the docs on `expose_addr` for more details. - PointerExposeAddress, + /// See the docs on `expose_provenance` for more details. + PointerExposeProvenance, /// An address-to-pointer cast that picks up an exposed provenance. - /// See the docs on `from_exposed_addr` for more details. - PointerFromExposedAddress, + /// See the docs on `with_exposed_provenance` for more details. + PointerWithExposedProvenance, /// Pointer related casts that are done by coercions. Note that reference-to-raw-ptr casts are /// translated into `&raw mut/const *r`, i.e., they are not actually casts. PointerCoercion(PointerCoercion), @@ -1356,6 +1403,21 @@ pub enum AggregateKind<'tcx> { Closure(DefId, GenericArgsRef<'tcx>), Coroutine(DefId, GenericArgsRef<'tcx>), CoroutineClosure(DefId, GenericArgsRef<'tcx>), + + /// Construct a raw pointer from the data pointer and metadata. + /// + /// The `Ty` here is the type of the *pointee*, not the pointer itself. + /// The `Mutability` indicates whether this produces a `*const` or `*mut`. + /// + /// The [`Rvalue::Aggregate`] operands for thus must be + /// + /// 0. A raw pointer of matching mutability with any [`core::ptr::Thin`] pointee + /// 1. A value of the appropriate [`core::ptr::Pointee::Metadata`] type + /// + /// *Both* operands must always be included, even the unit value if this is + /// creating a thin pointer. If you're just converting between thin pointers, + /// you may want an [`Rvalue::Cast`] with [`CastKind::PtrToPtr`] instead. + RawPtr(Ty<'tcx>, Mutability), } #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] @@ -1366,16 +1428,9 @@ pub enum NullOp<'tcx> { AlignOf, /// Returns the offset of a field OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), - /// Returns whether we want to check for library UB or language UB at monomorphization time. - /// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in - /// const-eval/Miri, because the interpreter has its own better checks for language UB. - UbCheck(UbKind), -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] -pub enum UbKind { - LanguageUb, - LibraryUb, + /// Returns whether we should perform some UB-checking at runtime. + /// See the `ub_checks` intrinsic docs for details. + UbChecks, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -1450,19 +1505,32 @@ pub enum BinOp { Ge, /// The `>` operator (greater than) Gt, + /// The `<=>` operator (three-way comparison, like `Ord::cmp`) + /// + /// This is supported only on the integer types and `char`, always returning + /// [`rustc_hir::LangItem::OrderingEnum`] (aka [`std::cmp::Ordering`]). + /// + /// [`Rvalue::BinaryOp`]`(BinOp::Cmp, A, B)` returns + /// - `Ordering::Less` (`-1_i8`, as a Scalar) if `A < B` + /// - `Ordering::Equal` (`0_i8`, as a Scalar) if `A == B` + /// - `Ordering::Greater` (`+1_i8`, as a Scalar) if `A > B` + Cmp, /// The `ptr.offset` operator Offset, } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; + use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(AggregateKind<'_>, 32); static_assert_size!(Operand<'_>, 24); static_assert_size!(Place<'_>, 16); static_assert_size!(PlaceElem<'_>, 24); static_assert_size!(Rvalue<'_>, 40); + static_assert_size!(StatementKind<'_>, 16); + static_assert_size!(TerminatorKind<'_>, 96); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 0c29fe57d4fc9..3881723c5ec9c 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -14,8 +14,8 @@ pub struct PlaceTy<'tcx> { } // At least on 64 bit systems, `PlaceTy` should not be larger than two or three pointers. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(PlaceTy<'_>, 16); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(PlaceTy<'_>, 16); impl<'tcx> PlaceTy<'tcx> { #[inline] @@ -170,11 +170,11 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did), Rvalue::Ref(reg, bk, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; - Ty::new_ref(tcx, reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() }) + Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy()) } Rvalue::AddressOf(mutability, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; - Ty::new_ptr(tcx, ty::TypeAndMut { ty: place_ty, mutbl: mutability }) + Ty::new_ptr(tcx, place_ty, mutability) } Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, @@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize } - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool, + Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool, Rvalue::Aggregate(ref ak, ref ops) => match **ak { AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), AggregateKind::Tuple => { @@ -206,6 +206,7 @@ impl<'tcx> Rvalue<'tcx> { AggregateKind::CoroutineClosure(did, args) => { Ty::new_coroutine_closure(tcx, did, args) } + AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability), }, Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty), Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, @@ -276,6 +277,11 @@ impl<'tcx> BinOp { &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => { tcx.types.bool } + &BinOp::Cmp => { + // these should be integer-like types of the same size. + assert_eq!(lhs_ty, rhs_ty); + tcx.ty_ordering_enum(None) + } } } } @@ -288,7 +294,7 @@ impl BorrowKind { // We have no type corresponding to a shallow borrow, so use // `&` as an approximation. - BorrowKind::Fake => hir::Mutability::Not, + BorrowKind::Fake(_) => hir::Mutability::Not, } } } @@ -312,7 +318,8 @@ impl BinOp { BinOp::Gt => hir::BinOpKind::Gt, BinOp::Le => hir::BinOpKind::Le, BinOp::Ge => hir::BinOpKind::Ge, - BinOp::AddUnchecked + BinOp::Cmp + | BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked | BinOp::ShlUnchecked diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 94f8cba6fb578..f95afb199f7f9 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -1,10 +1,10 @@ /// Functionality for terminators and helper types that appear in terminators. use rustc_hir::LangItem; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use super::TerminatorKind; use rustc_data_structures::packed::Pu128; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use std::slice; use super::*; @@ -85,6 +85,12 @@ impl SwitchTargets { self.values.push(value); self.targets.insert(self.targets.len() - 1, bb); } + + /// Returns true if all targets (including the fallback target) are distinct. + #[inline] + pub fn is_distinct(&self) -> bool { + self.targets.iter().collect::>().len() == self.targets.len() + } } pub struct SwitchTargetsIter<'a> { @@ -149,44 +155,45 @@ impl AssertKind { matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..)) } - /// Get the message that is printed at runtime when this assertion fails. + /// Get the lang item that is invoked to print a static message when this assert fires. /// /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by /// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference) - /// instead of printing a static message. - pub fn description(&self) -> &'static str { + /// instead of printing a static message. Those have dynamic arguments that aren't present for + /// the rest of the messages here. + pub fn panic_function(&self) -> LangItem { use AssertKind::*; match self { - Overflow(BinOp::Add, _, _) => "attempt to add with overflow", - Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow", - Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow", - Overflow(BinOp::Div, _, _) => "attempt to divide with overflow", - Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow", - OverflowNeg(_) => "attempt to negate with overflow", - Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow", - Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow", + Overflow(BinOp::Add, _, _) => LangItem::PanicAddOverflow, + Overflow(BinOp::Sub, _, _) => LangItem::PanicSubOverflow, + Overflow(BinOp::Mul, _, _) => LangItem::PanicMulOverflow, + Overflow(BinOp::Div, _, _) => LangItem::PanicDivOverflow, + Overflow(BinOp::Rem, _, _) => LangItem::PanicRemOverflow, + OverflowNeg(_) => LangItem::PanicNegOverflow, + Overflow(BinOp::Shr, _, _) => LangItem::PanicShrOverflow, + Overflow(BinOp::Shl, _, _) => LangItem::PanicShlOverflow, Overflow(op, _, _) => bug!("{:?} cannot overflow", op), - DivisionByZero(_) => "attempt to divide by zero", - RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero", - ResumedAfterReturn(CoroutineKind::Coroutine(_)) => "coroutine resumed after completion", + DivisionByZero(_) => LangItem::PanicDivZero, + RemainderByZero(_) => LangItem::PanicRemZero, + ResumedAfterReturn(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumed, ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { - "`async fn` resumed after completion" + LangItem::PanicAsyncFnResumed } ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { - "`async gen fn` resumed after completion" + LangItem::PanicAsyncGenFnResumed } ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { - "`gen fn` should just keep returning `None` after completion" + LangItem::PanicGenFnNone } - ResumedAfterPanic(CoroutineKind::Coroutine(_)) => "coroutine resumed after panicking", + ResumedAfterPanic(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedPanic, ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { - "`async fn` resumed after panicking" + LangItem::PanicAsyncFnResumedPanic } ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { - "`async gen fn` resumed after panicking" + LangItem::PanicAsyncGenFnResumedPanic } ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { - "`gen fn` should just keep returning `None` after panicking" + LangItem::PanicGenFnNonePanic } BoundsCheck { .. } | MisalignedPointerDereference { .. } => { @@ -198,7 +205,7 @@ impl AssertKind { /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing. /// /// Needs to be kept in sync with the run-time behavior (which is defined by - /// `AssertKind::description` and the lang items mentioned in its docs). + /// `AssertKind::panic_function` and the lang items mentioned in its docs). /// Note that we deliberately show more details here than we do at runtime, such as the actual /// numbers that overflowed -- it is much easier to do so here than at runtime. pub fn fmt_assert_args(&self, f: &mut W) -> fmt::Result @@ -246,20 +253,44 @@ impl AssertKind { Overflow(BinOp::Shl, _, r) => { write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}") } + Overflow(op, _, _) => bug!("{:?} cannot overflow", op), MisalignedPointerDereference { required, found } => { write!( f, "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}" ) } - _ => write!(f, "\"{}\"", self.description()), + ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { + write!(f, "\"coroutine resumed after completion\"") + } + ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + write!(f, "\"`async fn` resumed after completion\"") + } + ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + write!(f, "\"`async gen fn` resumed after completion\"") + } + ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + write!(f, "\"`gen fn` should just keep returning `None` after completion\"") + } + ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { + write!(f, "\"coroutine resumed after panicking\"") + } + ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + write!(f, "\"`async fn` resumed after panicking\"") + } + ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + write!(f, "\"`async gen fn` resumed after panicking\"") + } + ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + write!(f, "\"`gen fn` should just keep returning `None` after panicking\"") + } } } /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval). /// /// Needs to be kept in sync with the run-time behavior (which is defined by - /// `AssertKind::description` and the lang items mentioned in its docs). + /// `AssertKind::panic_function` and the lang items mentioned in its docs). /// Note that we deliberately show more details here than we do at runtime, such as the actual /// numbers that overflowed -- it is much easier to do so here than at runtime. pub fn diagnostic_message(&self) -> DiagMessage { diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 0a938bcd31562..245e9096bad47 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -245,7 +245,7 @@ pub fn reachable<'a, 'tcx>( /// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`. pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet { let mut iter = preorder(body); - iter.by_ref().for_each(drop); + while let Some(_) = iter.next() {} iter.visited } @@ -279,3 +279,97 @@ pub fn reverse_postorder<'a, 'tcx>( { body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb])) } + +/// Traversal of a [`Body`] that tries to avoid unreachable blocks in a monomorphized [`Instance`]. +/// +/// This is allowed to have false positives; blocks may be visited even if they are not actually +/// reachable. +/// +/// Such a traversal is mostly useful because it lets us skip lowering the `false` side +/// of `if ::CONST`, as well as [`NullOp::UbChecks`]. +/// +/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks +pub fn mono_reachable<'a, 'tcx>( + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> MonoReachable<'a, 'tcx> { + MonoReachable::new(body, tcx, instance) +} + +/// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a +/// convenience function to run that traversal then extract its set of reached blocks. +pub fn mono_reachable_as_bitset<'a, 'tcx>( + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> BitSet { + let mut iter = mono_reachable(body, tcx, instance); + while let Some(_) = iter.next() {} + iter.visited +} + +pub struct MonoReachable<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + visited: BitSet, + // Other traversers track their worklist in a Vec. But we don't care about order, so we can + // store ours in a BitSet and thus save allocations because BitSet has a small size + // optimization. + worklist: BitSet, +} + +impl<'a, 'tcx> MonoReachable<'a, 'tcx> { + pub fn new( + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + ) -> MonoReachable<'a, 'tcx> { + let mut worklist = BitSet::new_empty(body.basic_blocks.len()); + worklist.insert(START_BLOCK); + MonoReachable { + body, + tcx, + instance, + visited: BitSet::new_empty(body.basic_blocks.len()), + worklist, + } + } + + fn add_work(&mut self, blocks: impl IntoIterator) { + for block in blocks.into_iter() { + if !self.visited.contains(block) { + self.worklist.insert(block); + } + } + } +} + +impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> { + type Item = (BasicBlock, &'a BasicBlockData<'tcx>); + + fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { + while let Some(idx) = self.worklist.iter().next() { + self.worklist.remove(idx); + if !self.visited.insert(idx) { + continue; + } + + let data = &self.body[idx]; + + if let Some((bits, targets)) = + Body::try_const_mono_switchint(self.tcx, self.instance, data) + { + let target = targets.target_for_value(bits); + self.add_work([target]); + } else { + self.add_work(data.terminator().successors()); + } + + return Some((idx, data)); + } + + None + } +} diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 845b17175505d..d97abc3f1904f 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -156,10 +156,10 @@ macro_rules! make_mir_visitor { fn visit_coverage( &mut self, - coverage: & $($mutability)? Coverage, + kind: & $($mutability)? coverage::CoverageKind, location: Location, ) { - self.super_coverage(coverage, location); + self.super_coverage(kind, location); } fn visit_retag( @@ -341,18 +341,23 @@ macro_rules! make_mir_visitor { ty::InstanceDef::Intrinsic(_def_id) | ty::InstanceDef::VTableShim(_def_id) | - ty::InstanceDef::ReifyShim(_def_id) | + ty::InstanceDef::ReifyShim(_def_id, _) | ty::InstanceDef::Virtual(_def_id, _) | ty::InstanceDef::ThreadLocalShim(_def_id) | ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } | - ty::InstanceDef::ConstructCoroutineInClosureShim { coroutine_closure_def_id: _def_id, target_kind: _ } | - ty::InstanceDef::CoroutineKindShim { coroutine_def_id: _def_id, target_kind: _ } | + ty::InstanceDef::ConstructCoroutineInClosureShim { + coroutine_closure_def_id: _def_id, + receiver_by_ref: _, + } | + ty::InstanceDef::CoroutineKindShim { coroutine_def_id: _def_id } | + ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, None) | ty::InstanceDef::DropGlue(_def_id, None) => {} ty::InstanceDef::FnPtrShim(_def_id, ty) | ty::InstanceDef::DropGlue(_def_id, Some(ty)) | ty::InstanceDef::CloneShim(_def_id, ty) | - ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => { + ty::InstanceDef::FnPtrAddrShim(_def_id, ty) | + ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, Some(ty)) => { // FIXME(eddyb) use a better `TyContext` here. self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } @@ -650,7 +655,7 @@ macro_rules! make_mir_visitor { BorrowKind::Shared => PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow ), - BorrowKind::Fake => PlaceContext::NonMutatingUse( + BorrowKind::Fake(_) => PlaceContext::NonMutatingUse( NonMutatingUseContext::FakeBorrow ), BorrowKind::Mut { .. } => @@ -748,6 +753,9 @@ macro_rules! make_mir_visitor { ) => { self.visit_args(coroutine_closure_args, location); } + AggregateKind::RawPtr(ty, _) => { + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } } for operand in operands { @@ -800,7 +808,7 @@ macro_rules! make_mir_visitor { } fn super_coverage(&mut self, - _coverage: & $($mutability)? Coverage, + _kind: & $($mutability)? coverage::CoverageKind, _location: Location) { } @@ -1276,6 +1284,8 @@ pub enum NonMutatingUseContext { /// Shared borrow. SharedBorrow, /// A fake borrow. + /// FIXME: do we need to distinguish shallow and deep fake borrows? In fact, do we need to + /// distinguish fake and normal deep borrows? FakeBorrow, /// AddressOf for *const pointer. AddressOf, diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 33ee3371605ae..320d49ea64674 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -67,6 +67,10 @@ impl EraseType for &'_ ty::List { type Result = [u8; size_of::<&'static ty::List<()>>()]; } +impl EraseType for &'_ ty::ListWithCachedTypeInfo { + type Result = [u8; size_of::<&'static ty::ListWithCachedTypeInfo<()>>()]; +} + impl EraseType for &'_ rustc_index::IndexSlice { type Result = [u8; size_of::<&'static rustc_index::IndexSlice>()]; } @@ -234,6 +238,7 @@ trivial! { Option, Option, Option, + Option, Option, Option, Option, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 69d3974184db0..faa137019cb92 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -9,10 +9,10 @@ use crate::ty::{self, Ty, TyCtxt}; use crate::ty::{GenericArg, GenericArgsRef}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, ModDefId, LOCAL_CRATE}; use rustc_hir::hir_id::{HirId, OwnerId}; -use rustc_query_system::query::DefIdCacheSelector; -use rustc_query_system::query::{DefaultCacheSelector, SingleCacheSelector, VecCacheSelector}; +use rustc_query_system::query::{DefIdCache, DefaultCache, SingleCache, VecCache}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi; /// Placeholder for `CrateNum`'s "local" counterpart #[derive(Copy, Clone, Debug)] @@ -21,7 +21,7 @@ pub struct LocalCrate; /// The `Key` trait controls what types can legally be used as the key /// for a query. pub trait Key: Sized { - // N.B. Most of the keys down below have `type CacheSelector = DefaultCacheSelector;`, + // N.B. Most of the keys down below have `type Cache = DefaultCache;`, // it would be reasonable to use associated type defaults, to remove the duplication... // // ...But r-a doesn't support them yet and using a default here causes r-a to not infer @@ -29,7 +29,7 @@ pub trait Key: Sized { // type defaults, please restrain from using them here <3 // // r-a issue: - type CacheSelector; + type Cache; /// In the event that a cycle occurs, if no explicit span has been /// given for a query with key `self`, what span should we use? @@ -55,7 +55,7 @@ pub trait AsLocalKey: Key { } impl Key for () { - type CacheSelector = SingleCacheSelector; + type Cache = SingleCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -63,7 +63,7 @@ impl Key for () { } impl<'tcx> Key for ty::InstanceDef<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) @@ -80,7 +80,7 @@ impl<'tcx> AsLocalKey for ty::InstanceDef<'tcx> { } impl<'tcx> Key for ty::Instance<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) @@ -88,7 +88,7 @@ impl<'tcx> Key for ty::Instance<'tcx> { } impl<'tcx> Key for mir::interpret::GlobalId<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.instance.default_span(tcx) @@ -96,7 +96,7 @@ impl<'tcx> Key for mir::interpret::GlobalId<'tcx> { } impl<'tcx> Key for (Ty<'tcx>, Option>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -104,7 +104,7 @@ impl<'tcx> Key for (Ty<'tcx>, Option>) { } impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP @@ -112,7 +112,7 @@ impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> { } impl Key for CrateNum { - type CacheSelector = VecCacheSelector; + type Cache = VecCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -129,7 +129,7 @@ impl AsLocalKey for CrateNum { } impl Key for OwnerId { - type CacheSelector = VecCacheSelector; + type Cache = VecCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.to_def_id().default_span(tcx) @@ -141,7 +141,7 @@ impl Key for OwnerId { } impl Key for LocalDefId { - type CacheSelector = VecCacheSelector; + type Cache = VecCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.to_def_id().default_span(tcx) @@ -153,7 +153,7 @@ impl Key for LocalDefId { } impl Key for DefId { - type CacheSelector = DefIdCacheSelector; + type Cache = DefIdCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(*self) @@ -175,7 +175,7 @@ impl AsLocalKey for DefId { } impl Key for LocalModDefId { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(*self) @@ -188,7 +188,7 @@ impl Key for LocalModDefId { } impl Key for ModDefId { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(*self) @@ -210,7 +210,7 @@ impl AsLocalKey for ModDefId { } impl Key for SimplifiedType { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -218,7 +218,7 @@ impl Key for SimplifiedType { } impl Key for (DefId, DefId) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) @@ -226,7 +226,7 @@ impl Key for (DefId, DefId) { } impl<'tcx> Key for (ty::Instance<'tcx>, LocalDefId) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) @@ -234,7 +234,7 @@ impl<'tcx> Key for (ty::Instance<'tcx>, LocalDefId) { } impl Key for (DefId, LocalDefId) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) @@ -242,7 +242,7 @@ impl Key for (DefId, LocalDefId) { } impl Key for (LocalDefId, DefId) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) @@ -250,7 +250,7 @@ impl Key for (LocalDefId, DefId) { } impl Key for (LocalDefId, LocalDefId) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) @@ -258,7 +258,7 @@ impl Key for (LocalDefId, LocalDefId) { } impl Key for (DefId, Ident) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.0) @@ -271,7 +271,7 @@ impl Key for (DefId, Ident) { } impl Key for (LocalDefId, LocalDefId, Ident) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) @@ -279,7 +279,7 @@ impl Key for (LocalDefId, LocalDefId, Ident) { } impl Key for (CrateNum, DefId) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) @@ -296,7 +296,7 @@ impl AsLocalKey for (CrateNum, DefId) { } impl Key for (CrateNum, SimplifiedType) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -313,7 +313,7 @@ impl AsLocalKey for (CrateNum, SimplifiedType) { } impl Key for (DefId, SimplifiedType) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) @@ -321,7 +321,7 @@ impl Key for (DefId, SimplifiedType) { } impl<'tcx> Key for GenericArgsRef<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -329,7 +329,7 @@ impl<'tcx> Key for GenericArgsRef<'tcx> { } impl<'tcx> Key for (DefId, GenericArgsRef<'tcx>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) @@ -337,7 +337,7 @@ impl<'tcx> Key for (DefId, GenericArgsRef<'tcx>) { } impl<'tcx> Key for (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { (self.0).def.default_span(tcx) @@ -345,7 +345,7 @@ impl<'tcx> Key for (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>) { } impl<'tcx> Key for (LocalDefId, DefId, GenericArgsRef<'tcx>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) @@ -353,7 +353,7 @@ impl<'tcx> Key for (LocalDefId, DefId, GenericArgsRef<'tcx>) { } impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.1.def_id) @@ -361,7 +361,7 @@ impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) { } impl<'tcx> Key for ty::PolyTraitRef<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) @@ -369,7 +369,7 @@ impl<'tcx> Key for ty::PolyTraitRef<'tcx> { } impl<'tcx> Key for ty::PolyExistentialTraitRef<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.def_id()) @@ -377,7 +377,7 @@ impl<'tcx> Key for ty::PolyExistentialTraitRef<'tcx> { } impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(self.0.def_id()) @@ -385,7 +385,7 @@ impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) { } impl<'tcx> Key for GenericArg<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -393,7 +393,7 @@ impl<'tcx> Key for GenericArg<'tcx> { } impl<'tcx> Key for ty::Const<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -401,7 +401,7 @@ impl<'tcx> Key for ty::Const<'tcx> { } impl<'tcx> Key for Ty<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -417,7 +417,7 @@ impl<'tcx> Key for Ty<'tcx> { } impl<'tcx> Key for TyAndLayout<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -425,15 +425,15 @@ impl<'tcx> Key for TyAndLayout<'tcx> { } impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } } -impl<'tcx> Key for &'tcx ty::List> { - type CacheSelector = DefaultCacheSelector; +impl<'tcx> Key for ty::Clauses<'tcx> { + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -441,7 +441,7 @@ impl<'tcx> Key for &'tcx ty::List> { } impl<'tcx> Key for ty::ParamEnv<'tcx> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -449,7 +449,7 @@ impl<'tcx> Key for ty::ParamEnv<'tcx> { } impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.value.default_span(tcx) @@ -461,7 +461,7 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> { } impl Key for Symbol { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP @@ -469,7 +469,7 @@ impl Key for Symbol { } impl Key for Option { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP @@ -479,7 +479,7 @@ impl Key for Option { /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. impl<'tcx, T: Clone> Key for Canonical<'tcx, T> { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP @@ -487,7 +487,7 @@ impl<'tcx, T: Clone> Key for Canonical<'tcx, T> { } impl Key for (Symbol, u32, u32) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP @@ -495,7 +495,15 @@ impl Key for (Symbol, u32, u32) { } impl<'tcx> Key for (DefId, Ty<'tcx>, GenericArgsRef<'tcx>, ty::ParamEnv<'tcx>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + +impl<'tcx> Key for (Ty<'tcx>, abi::VariantIdx) { + type Cache = DefaultCache; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP @@ -503,7 +511,7 @@ impl<'tcx> Key for (DefId, Ty<'tcx>, GenericArgsRef<'tcx>, ty::ParamEnv<'tcx>) { } impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { DUMMY_SP @@ -511,7 +519,7 @@ impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) { } impl<'tcx> Key for (ty::PolyFnSig<'tcx>, &'tcx ty::List>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -519,7 +527,7 @@ impl<'tcx> Key for (ty::PolyFnSig<'tcx>, &'tcx ty::List>) { } impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.0.default_span(tcx) @@ -527,7 +535,7 @@ impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List>) { } impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP @@ -535,7 +543,7 @@ impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) { } impl Key for HirId { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.hir().span(*self) @@ -547,8 +555,21 @@ impl Key for HirId { } } +impl Key for (LocalDefId, HirId) { + type Cache = DefaultCache; + + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + tcx.hir().span(self.1) + } + + #[inline(always)] + fn key_as_def_id(&self) -> Option { + Some(self.0.into()) + } +} + impl<'tcx> Key for (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>) { - type CacheSelector = DefaultCacheSelector; + type Cache = DefaultCache; // Just forward to `Ty<'tcx>` diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 865299e15c803..c2f7a227f6661 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -72,8 +72,9 @@ use rustc_hir::def_id::{ use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate}; use rustc_index::IndexVec; +use rustc_macros::rustc_queries; use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::{try_get_cached, CacheSelector, QueryCache, QueryMode, QueryState}; +use rustc_query_system::query::{try_get_cached, QueryCache, QueryMode, QueryState}; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{CrateDepKind, CrateSource}; use rustc_session::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib}; @@ -343,11 +344,14 @@ rustc_queries! { } } - /// Returns the list of bounds that can be used for - /// `SelectionCandidate::ProjectionCandidate(_)` and - /// `ProjectionTyCandidate::TraitDef`. - /// Specifically this is the bounds written on the trait's type - /// definition, or those after the `impl` keyword + /// Returns the list of bounds that are required to be satsified + /// by a implementation or definition. For associated types, these + /// must be satisfied for an implementation to be well-formed, + /// and for opaque types, these are required to be satisfied by + /// the hidden-type of the opaque. + /// + /// Syntactially, these are the bounds written on the trait's type + /// definition, or those after the `impl` keyword for an opaque: /// /// ```ignore (incomplete) /// type X: Bound + 'lt @@ -363,7 +367,16 @@ rustc_queries! { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern - feedable + } + + /// The set of item bounds (see [`TyCtxt::explicit_item_bounds`]) that + /// share the `Self` type of the item. These are a subset of the bounds + /// that may explicitly be used for things like closure signature + /// deduction. + query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> { + desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern } /// Elaborated version of the predicates from `explicit_item_bounds`. @@ -386,10 +399,18 @@ rustc_queries! { /// ``` /// /// Bounds from the parent (e.g. with nested impl trait) are not included. - query item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx ty::List>> { + query item_bounds(key: DefId) -> ty::EarlyBinder> { desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } + query item_super_predicates(key: DefId) -> ty::EarlyBinder> { + desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } + } + + query item_non_self_assumptions(key: DefId) -> ty::EarlyBinder> { + desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } + } + /// Look up all native libraries this crate depends on. /// These are assembled from the following places: /// - `extern` blocks (depending on their `link` attributes) @@ -454,20 +475,6 @@ rustc_queries! { desc { |tcx| "building THIR for `{}`", tcx.def_path_str(key) } } - /// Create a THIR tree for debugging. - query thir_tree(key: LocalDefId) -> &'tcx String { - no_hash - arena_cache - desc { |tcx| "constructing THIR tree for `{}`", tcx.def_path_str(key) } - } - - /// Create a list-like THIR representation for debugging. - query thir_flat(key: LocalDefId) -> &'tcx String { - no_hash - arena_cache - desc { |tcx| "constructing flat THIR representation for `{}`", tcx.def_path_str(key) } - } - /// Set of all the `DefId`s in this crate that have MIR associated with /// them. This includes all the body owners, but also things like struct /// constructors. @@ -485,21 +492,15 @@ rustc_queries! { separate_provide_extern } - /// Fetch the MIR for a given `DefId` right after it's built - this includes - /// unreachable code. + /// Build the MIR for a given `DefId` and prepare it for const qualification. + /// + /// See the [rustc dev guide] for more info. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/construction.html query mir_built(key: LocalDefId) -> &'tcx Steal> { desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key) } } - /// Fetch the MIR for a given `DefId` up till the point where it is - /// ready for const qualification. - /// - /// See the README for the `mir` module for details. - query mir_const(key: LocalDefId) -> &'tcx Steal> { - desc { |tcx| "preparing `{}` for borrow checking", tcx.def_path_str(key) } - no_hash - } - /// Try to build an abstract representation of the given constant. query thir_abstract_const( key: DefId @@ -703,8 +704,8 @@ rustc_queries! { separate_provide_extern } - query adt_sized_constraint(key: DefId) -> ty::EarlyBinder<&'tcx ty::List>> { - desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } + query adt_sized_constraint(key: DefId) -> Option>> { + desc { |tcx| "computing the `Sized` constraint for `{}`", tcx.def_path_str(key) } } query adt_dtorck_constraint( @@ -846,8 +847,10 @@ rustc_queries! { separate_provide_extern } - query issue33140_self_ty(key: DefId) -> Option>> { - desc { |tcx| "computing Self type wrt issue #33140 `{}`", tcx.def_path_str(key) } + query self_ty_of_trait_impl_enabling_order_dep_trait_object_hack( + key: DefId + ) -> Option>> { + desc { |tcx| "computing self type wrt issue #33140 `{}`", tcx.def_path_str(key) } } /// Maps a `DefId` of a type to a list of its inherent impls. @@ -863,12 +866,6 @@ rustc_queries! { desc { |tcx| "collecting all inherent impls for `{:?}`", key } } - /// The result of unsafety-checking this `LocalDefId` with the old checker. - query mir_unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } - } - /// Unsafety-check this `LocalDefId`. query check_unsafety(key: LocalDefId) { desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) } @@ -981,10 +978,6 @@ rustc_queries! { cache_on_disk_if { true } } - query has_typeck_results(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` has a body", tcx.def_path_str(def_id) } - } - query coherent_trait(def_id: DefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } ensure_forwards_result_if_red @@ -1042,6 +1035,13 @@ rustc_queries! { } } + /// Computes the tag (if any) for a given type and variant. + query tag_for_variant( + key: (Ty<'tcx>, abi::VariantIdx) + ) -> Option { + desc { "computing variant tag for enum" } + } + /// Evaluates a constant and returns the computed allocation. /// /// **Do not use this** directly, use the `eval_to_const_value` or `eval_to_valtree` instead. @@ -1343,6 +1343,14 @@ rustc_queries! { query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Unpin`", env.value } } + /// Query backing `Ty::has_surface_async_drop`. + query has_surface_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` has `AsyncDrop` implementation", env.value } + } + /// Query backing `Ty::has_surface_drop`. + query has_surface_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` has `Drop` implementation", env.value } + } /// Query backing `Ty::needs_drop`. query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } @@ -2141,7 +2149,7 @@ rustc_queries! { desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } } - query reveal_opaque_types_in_bounds(key: &'tcx ty::List>) -> &'tcx ty::List> { + query reveal_opaque_types_in_bounds(key: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> { desc { "revealing opaque types in `{:?}`", key } } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 9c7c46f2ad24b..2dcb58729ff96 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -6,6 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, StableCrateId, LOCAL_CRATE}; use rustc_hir::definitions::DefPathHash; use rustc_index::{Idx, IndexVec}; +use rustc_macros::{Decodable, Encodable}; use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use rustc_middle::mir::{self, interpret}; @@ -492,9 +493,7 @@ impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { // expansion, so we use `import_source_files` to ensure that the foreign // source files are actually imported before we call `source_file_by_stable_id`. if source_file_cnum != LOCAL_CRATE { - self.tcx - .cstore_untracked() - .import_source_files(self.tcx.sess, source_file_cnum); + self.tcx.import_source_files(source_file_cnum); } source_map @@ -634,12 +633,7 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { expn_id } else { let index_guess = self.foreign_expn_data[&hash]; - self.tcx.cstore_untracked().expn_hash_to_expn_id( - self.tcx.sess, - krate, - index_guess, - hash, - ) + self.tcx.expn_hash_to_expn_id(krate, index_guess, hash) }; debug_assert_eq!(expn_id.krate, krate); @@ -737,9 +731,10 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { // If we get to this point, then all of the query inputs were green, // which means that the definition with this hash is guaranteed to // still exist in the current compilation session. - self.tcx.def_path_hash_to_def_id(def_path_hash, &mut || { - panic!("Failed to convert DefPathHash {def_path_hash:?}") - }) + self.tcx.def_path_hash_to_def_id( + def_path_hash, + &("Failed to convert DefPathHash", def_path_hash), + ) } fn decode_attr_id(&mut self) -> rustc_span::AttrId { diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 8d88488e1675d..8a4e3ab0e619a 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -8,12 +8,11 @@ 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}; use rustc_hir::hir_id::OwnerId; +use rustc_macros::HashStable; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::dep_graph::SerializedDepNodeIndex; pub(crate) use rustc_query_system::query::QueryJobId; @@ -22,16 +21,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, -} - -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, @@ -278,13 +267,7 @@ macro_rules! separate_provide_extern_default { () }; ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { - |_, key| bug!( - "`tcx.{}({:?})` unsupported by its crate; \ - perhaps the `{}` query was never assigned a provider function", - stringify!($name), - key, - stringify!($name), - ) + |_, key| $crate::query::plumbing::default_extern_query(stringify!($name), &key) }; ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { separate_provide_extern_default!([$($modifiers)*][$($args)*]) @@ -336,14 +319,13 @@ macro_rules! define_callbacks { )) } - pub type Storage<'tcx> = < - <$($K)* as keys::Key>::CacheSelector as CacheSelector<'tcx, Erase<$V>> - >::Cache; + pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>; - // Ensure that keys grow no larger than 64 bytes - #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + // Ensure that keys grow no larger than 72 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] const _: () = { - if mem::size_of::>() > 64 { + if mem::size_of::>() > 72 { panic!("{}", concat!( "the query `", stringify!($name), @@ -354,8 +336,9 @@ macro_rules! define_callbacks { } }; - // Ensure that values grow no larger than 64 bytes - #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] + // Ensure that values grow no larger than 64 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] const _: () = { if mem::size_of::>() > 64 { panic!("{}", concat!( @@ -474,15 +457,7 @@ macro_rules! define_callbacks { impl Default for Providers { fn default() -> Self { Providers { - $($name: |_, key| bug!( - "`tcx.{}({:?})` is not supported for this key;\n\ - hint: Queries can be either made to the local crate, or the external crate. \ - This error means you tried to use it for one that's not supported.\n\ - If that's not the case, {} was likely never assigned to a provider function.\n", - stringify!($name), - key, - stringify!($name), - ),)* + $($name: |_, key| $crate::query::plumbing::default_query(stringify!($name), &key)),* } } } @@ -673,3 +648,21 @@ use super::erase::EraseType; #[derive(Copy, Clone, Debug, HashStable)] pub struct CyclePlaceholder(pub ErrorGuaranteed); + +#[cold] +pub(crate) fn default_query(name: &str, key: &dyn std::fmt::Debug) -> ! { + bug!( + "`tcx.{name}({key:?})` is not supported for this key;\n\ + hint: Queries can be either made to the local crate, or the external crate. \ + This error means you tried to use it for one that's not supported.\n\ + If that's not the case, {name} was likely never assigned to a provider function.\n", + ) +} + +#[cold] +pub(crate) fn default_extern_query(name: &str, key: &dyn std::fmt::Debug) -> ! { + bug!( + "`tcx.{name}({key:?})` unsupported by its crate; \ + perhaps the `{name}` query was never assigned a provider function", + ) +} diff --git a/compiler/rustc_middle/src/tests.rs b/compiler/rustc_middle/src/tests.rs index 49960cc0bb2a2..6c2e5f63418a7 100644 --- a/compiler/rustc_middle/src/tests.rs +++ b/compiler/rustc_middle/src/tests.rs @@ -1,13 +1,9 @@ -use super::*; - -// FIXME(#27438): right now the unit tests of rustc_middle don't refer to any actual -// functions generated in rustc_data_structures (all -// references are through generic functions), but statics are -// referenced from time to time. Due to this bug we won't -// actually correctly link in the statics unless we also -// reference a function, so be sure to reference a dummy -// function. +// FIXME(#27438): Right now, the unit tests of `rustc_middle` don't refer to any actual functions +// generated in `rustc_data_structures` (all references are through generic functions), +// but statics are referenced from time to time. Due to this Windows `dllimport` bug +// we won't actually correctly link in the statics unless we also reference a function, +// so be sure to reference a dummy function. #[test] fn noop() { - rustc_data_structures::__noop_fix_for_27438(); + rustc_data_structures::__noop_fix_for_windows_dllimport_issue(); } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 96a61883cc14f..a7d8ead567738 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -12,12 +12,13 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::RangeEnd; +use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd}; use rustc_index::newtype_index; use rustc_index::IndexVec; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeVisitable}; use rustc_middle::middle::region; -use rustc_middle::mir::interpret::{AllocId, Scalar}; -use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Mutability, UnOp}; +use rustc_middle::mir::interpret::AllocId; +use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ @@ -115,13 +116,13 @@ pub struct Param<'tcx> { /// Whether this param is `self`, and how it is bound. pub self_kind: Option, /// HirId for lints. - pub hir_id: Option, + pub hir_id: Option, } #[derive(Copy, Clone, Debug, HashStable)] pub enum LintLevel { Inherited, - Explicit(hir::HirId), + Explicit(HirId), } #[derive(Clone, Debug, HashStable)] @@ -167,7 +168,7 @@ pub struct ClosureExpr<'tcx> { pub args: UpvarArgs<'tcx>, pub upvars: Box<[ExprId]>, pub movability: Option, - pub fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>, + pub fake_reads: Vec<(ExprId, FakeReadCause, HirId)>, } #[derive(Clone, Debug, HashStable)] @@ -184,7 +185,7 @@ pub enum BlockSafety { /// A compiler-generated unsafe block BuiltinUnsafe, /// An `unsafe` block. The `HirId` is the ID of the block. - ExplicitUnsafe(hir::HirId), + ExplicitUnsafe(HirId), } #[derive(Clone, Debug, HashStable)] @@ -233,7 +234,7 @@ pub enum StmtKind<'tcx> { } #[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] -pub struct LocalVarId(pub hir::HirId); +pub struct LocalVarId(pub HirId); /// A THIR expression. #[derive(Clone, Debug, HashStable)] @@ -356,8 +357,9 @@ pub enum ExprKind<'tcx> { /// A `match` expression. Match { scrutinee: ExprId, - scrutinee_hir_id: hir::HirId, + scrutinee_hir_id: HirId, arms: Box<[ArmId]>, + match_source: MatchSource, }, /// A block. Block { @@ -581,12 +583,6 @@ pub enum InlineAsmOperand<'tcx> { }, } -#[derive(Copy, Clone, Debug, PartialEq, HashStable)] -pub enum BindingMode { - ByValue, - ByRef(BorrowKind), -} - #[derive(Clone, Debug, HashStable, TypeVisitable)] pub struct FieldPat<'tcx> { pub field: FieldIdx, @@ -607,19 +603,19 @@ impl<'tcx> Pat<'tcx> { pub fn simple_ident(&self) -> Option { match self.kind { - PatKind::Binding { name, mode: BindingMode::ByValue, subpattern: None, .. } => { - Some(name) - } + PatKind::Binding { + name, mode: BindingMode(ByRef::No, _), subpattern: None, .. + } => Some(name), _ => None, } } /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` - pub fn each_binding(&self, mut f: impl FnMut(Symbol, BindingMode, Ty<'tcx>, Span)) { + pub fn each_binding(&self, mut f: impl FnMut(Symbol, ByRef, Ty<'tcx>, Span)) { self.walk_always(|p| { if let PatKind::Binding { name, mode, ty, .. } = p.kind { - f(name, mode, ty, p.span); + f(name, mode.0, ty, p.span); } }); } @@ -647,6 +643,7 @@ impl<'tcx> Pat<'tcx> { AscribeUserType { subpattern, .. } | Binding { subpattern: Some(subpattern), .. } | Deref { subpattern } + | DerefPattern { subpattern, .. } | InlineConstant { subpattern, .. } => subpattern.walk_(it), Leaf { subpatterns } | Variant { subpatterns, .. } => { subpatterns.iter().for_each(|field| field.pattern.walk_(it)) @@ -729,7 +726,6 @@ pub enum PatKind<'tcx> { /// `x`, `ref x`, `x @ P`, etc. Binding { - mutability: Mutability, name: Symbol, #[type_visitable(ignore)] mode: BindingMode, @@ -762,6 +758,12 @@ pub enum PatKind<'tcx> { subpattern: Box>, }, + /// Deref pattern, written `box P` for now. + DerefPattern { + subpattern: Box>, + mutability: hir::Mutability, + }, + /// One of the following: /// * `&str` (represented as a valtree), which will be handled as a string pattern and thus /// exhaustiveness checking will detect if you use the same string twice in different @@ -1006,18 +1008,20 @@ impl<'tcx> PatRangeBoundary<'tcx> { // This code is hot when compiling matches with many ranges. So we // special-case extraction of evaluated scalars for speed, for types where - // raw data comparisons are appropriate. E.g. `unicode-normalization` has + // we can do scalar comparisons. E.g. `unicode-normalization` has // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared // in this way. - (Finite(mir::Const::Ty(a)), Finite(mir::Const::Ty(b))) - if matches!(ty.kind(), ty::Uint(_) | ty::Char) => - { - return Some(a.kind().cmp(&b.kind())); + (Finite(a), Finite(b)) if matches!(ty.kind(), ty::Int(_) | ty::Uint(_) | ty::Char) => { + if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) { + let sz = ty.primitive_size(tcx); + let cmp = match ty.kind() { + ty::Uint(_) | ty::Char => a.assert_uint(sz).cmp(&b.assert_uint(sz)), + ty::Int(_) => a.assert_int(sz).cmp(&b.assert_int(sz)), + _ => unreachable!(), + }; + return Some(cmp); + } } - ( - Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(a)), _)), - Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(b)), _)), - ) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => return Some(a.cmp(&b)), _ => {} } @@ -1067,17 +1071,8 @@ impl<'tcx> fmt::Display for Pat<'tcx> { PatKind::Wild => write!(f, "_"), PatKind::Never => write!(f, "!"), PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"), - PatKind::Binding { mutability, name, mode, ref subpattern, .. } => { - let is_mut = match mode { - BindingMode::ByValue => mutability == Mutability::Mut, - BindingMode::ByRef(bk) => { - write!(f, "ref ")?; - matches!(bk, BorrowKind::Mut { .. }) - } - }; - if is_mut { - write!(f, "mut ")?; - } + PatKind::Binding { name, mode, ref subpattern, .. } => { + f.write_str(mode.prefix_str())?; write!(f, "{name}")?; if let Some(ref subpattern) = *subpattern { write!(f, " @ {subpattern}")?; @@ -1127,7 +1122,8 @@ impl<'tcx> fmt::Display for Pat<'tcx> { printed += 1; } - if printed < variant.fields.len() { + let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union()); + if printed < variant.fields.len() && (!is_union || printed == 0) { write!(f, "{}..", start_or_comma())?; } @@ -1172,6 +1168,9 @@ impl<'tcx> fmt::Display for Pat<'tcx> { } write!(f, "{subpattern}") } + PatKind::DerefPattern { ref subpattern, .. } => { + write!(f, "deref!({subpattern})") + } PatKind::Constant { value } => write!(f, "{value}"), PatKind::InlineConstant { def: _, ref subpattern } => { write!(f, "{} (from inline const)", subpattern) @@ -1208,9 +1207,10 @@ impl<'tcx> fmt::Display for Pat<'tcx> { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; + use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(Block, 48); static_assert_size!(Expr<'_>, 64); diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 5952c296fb642..f198881043739 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -229,15 +229,8 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( match &pat.kind { AscribeUserType { subpattern, ascription: _ } | Deref { subpattern } - | Binding { - subpattern: Some(subpattern), - mutability: _, - mode: _, - var: _, - ty: _, - is_primary: _, - name: _, - } => visitor.visit_pat(subpattern), + | DerefPattern { subpattern, .. } + | Binding { subpattern: Some(subpattern), .. } => visitor.visit_pat(subpattern), Binding { .. } | Wild | Never | Error(_) => {} Variant { subpatterns, adt_def: _, args: _, variant_index: _ } | Leaf { subpatterns } => { for subpattern in subpatterns { diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index a04bd636622ea..1ae037e09a75e 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -12,17 +12,21 @@ pub mod util; use crate::infer::canonical::Canonical; use crate::mir::ConstraintCategory; use crate::ty::abstract_const::NotConstEvaluatable; +use crate::ty::GenericArgsRef; use crate::ty::{self, AdtKind, Ty}; -use crate::ty::{GenericArgsRef, TyCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::HirId; +use rustc_macros::{ + Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, +}; use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; use std::hash::{Hash, Hasher}; @@ -262,10 +266,10 @@ pub enum ObligationCauseCode<'tcx> { /// expression that caused the obligation, and the `usize` /// indicates exactly which predicate it is in the list of /// instantiated predicates. - ExprItemObligation(DefId, rustc_hir::HirId, usize), + ExprItemObligation(DefId, HirId, usize), /// Combines `ExprItemObligation` and `BindingObligation`. - ExprBindingObligation(DefId, Span, rustc_hir::HirId, usize), + ExprBindingObligation(DefId, Span, HirId, usize), /// A type like `&'a T` is WF only if `T: 'a`. ReferenceOutlivesReferent(Ty<'tcx>), @@ -287,9 +291,9 @@ pub enum ObligationCauseCode<'tcx> { /// `S { ... }` must be `Sized`. StructInitializerSized, /// Type of each variable must be `Sized`. - VariableType(hir::HirId), + VariableType(HirId), /// Argument type must be `Sized`. - SizedArgumentType(Option), + SizedArgumentType(Option), /// Return type must be `Sized`. SizedReturnType, /// Return type of a call expression must be `Sized`. @@ -327,17 +331,22 @@ pub enum ObligationCauseCode<'tcx> { /// `static` items must have `Sync` type. SharedStatic, + /// Derived obligation (i.e. theoretical `where` clause) on a built-in + /// implementation like `Copy` or `Sized`. BuiltinDerivedObligation(DerivedObligationCause<'tcx>), + /// Derived obligation (i.e. `where` clause) on an user-provided impl + /// or a trait alias. ImplDerivedObligation(Box>), - DerivedObligation(DerivedObligationCause<'tcx>), + /// Derived obligation for WF goals. + WellFormedDerivedObligation(DerivedObligationCause<'tcx>), FunctionArgumentObligation { /// The node of the relevant argument in the function call. - arg_hir_id: hir::HirId, + arg_hir_id: HirId, /// The node of the function call. - call_hir_id: hir::HirId, + call_hir_id: HirId, /// The obligation introduced by this argument. parent_code: InternedObligationCauseCode<'tcx>, }, @@ -402,18 +411,18 @@ pub enum ObligationCauseCode<'tcx> { ReturnNoExpression, /// `return` with an expression - ReturnValue(hir::HirId), + ReturnValue(HirId), /// Opaque return type of this function OpaqueReturnType(Option<(Ty<'tcx>, Span)>), /// Block implicit return - BlockTailExpression(hir::HirId, hir::MatchSource), + BlockTailExpression(HirId, hir::MatchSource), /// #[feature(trivial_bounds)] is not enabled TrivialBound, - AwaitableExpr(hir::HirId), + AwaitableExpr(HirId), ForLoopIterator, @@ -431,8 +440,8 @@ pub enum ObligationCauseCode<'tcx> { MatchImpl(ObligationCause<'tcx>, DefId), BinOp { - lhs_hir_id: hir::HirId, - rhs_hir_id: Option, + lhs_hir_id: HirId, + rhs_hir_id: Option, rhs_span: Option, rhs_is_lit: bool, output_ty: Option>, @@ -486,7 +495,7 @@ pub enum WellFormedLoc { /// The index of the parameter to use. /// Parameters are indexed from 0, with the return type /// being the last 'parameter' - param_idx: u16, + param_idx: usize, }, } @@ -533,7 +542,7 @@ impl<'tcx> ObligationCauseCode<'tcx> { match self { FunctionArgumentObligation { parent_code, .. } => Some((parent_code, None)), BuiltinDerivedObligation(derived) - | DerivedObligation(derived) + | WellFormedDerivedObligation(derived) | ImplDerivedObligation(box ImplDerivedObligationCause { derived, .. }) => { Some((&derived.parent_code, Some(derived.parent_trait_pred))) } @@ -550,8 +559,8 @@ impl<'tcx> ObligationCauseCode<'tcx> { } // `ObligationCauseCode` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(ObligationCauseCode<'_>, 48); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(ObligationCauseCode<'_>, 48); #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum StatementAsExpression { @@ -562,27 +571,29 @@ pub enum StatementAsExpression { #[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] #[derive(TypeVisitable, TypeFoldable)] pub struct MatchExpressionArmCause<'tcx> { - pub arm_block_id: Option, + pub arm_block_id: Option, pub arm_ty: Ty<'tcx>, pub arm_span: Span, - pub prior_arm_block_id: Option, + pub prior_arm_block_id: Option, pub prior_arm_ty: Ty<'tcx>, pub prior_arm_span: Span, pub scrut_span: Span, pub source: hir::MatchSource, pub prior_non_diverging_arms: Vec, - pub opt_suggest_box_span: Option, + // Is the expectation of this match expression an RPIT? + pub tail_defines_return_position_impl_trait: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] pub struct IfExpressionCause<'tcx> { - pub then_id: hir::HirId, - pub else_id: hir::HirId, + pub then_id: HirId, + pub else_id: HirId, pub then_ty: Ty<'tcx>, pub else_ty: Ty<'tcx>, pub outer_span: Option, - pub opt_suggest_box_span: Option, + // Is the expectation of this match expression an RPIT? + pub tail_defines_return_position_impl_trait: Option, } #[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] @@ -618,11 +629,10 @@ pub enum SelectionError<'tcx> { OpaqueTypeAutoTraitLeakageUnknown(DefId), } -// FIXME(@lcnr): The `Binder` here should be unnecessary. Just use `TraitRef` instead. #[derive(Clone, Debug, TypeVisitable)] pub struct SignatureMismatchData<'tcx> { - pub found_trait_ref: ty::PolyTraitRef<'tcx>, - pub expected_trait_ref: ty::PolyTraitRef<'tcx>, + pub found_trait_ref: ty::TraitRef<'tcx>, + pub expected_trait_ref: ty::TraitRef<'tcx>, pub terr: ty::error::TypeError<'tcx>, } @@ -995,33 +1005,3 @@ pub enum CodegenObligationError { Unimplemented, FulfillmentError, } - -/// Defines the treatment of opaque types in a given inference context. -/// -/// This affects both what opaques are allowed to be defined, but also whether -/// opaques are replaced with inference vars eagerly in the old solver (e.g. -/// in projection, and in the signature during function type-checking). -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] -pub enum DefiningAnchor<'tcx> { - /// Define opaques which are in-scope of the current item being analyzed. - /// Also, eagerly replace these opaque types in `replace_opaque_types_with_inference_vars`. - /// - /// If the list is empty, do not allow any opaques to be defined. This is used to catch type mismatch - /// errors when handling opaque types, and also should be used when we would - /// otherwise reveal opaques (such as [`Reveal::All`] reveal mode). - Bind(&'tcx ty::List), - /// In contexts where we don't currently know what opaques are allowed to be - /// defined, such as (old solver) canonical queries, we will simply allow - /// opaques to be defined, but "bubble" them up in the canonical response or - /// otherwise treat them to be handled later. - /// - /// We do not eagerly replace opaque types in `replace_opaque_types_with_inference_vars`, - /// which may affect what predicates pass and fail in the old trait solver. - Bubble, -} - -impl<'tcx> DefiningAnchor<'tcx> { - pub fn bind(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self { - Self::Bind(tcx.opaque_types_defined_by(item)) - } -} diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 12e4f70ba5751..70f3532e3ab15 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -10,11 +10,13 @@ use crate::infer::canonical::{Canonical, QueryResponse}; use crate::ty::error::TypeError; use crate::ty::GenericArg; use crate::ty::{self, Ty, TyCtxt}; +use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_span::Span; pub mod type_op { use crate::ty::fold::TypeFoldable; use crate::ty::{Predicate, Ty, TyCtxt, UserType}; + use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use std::fmt; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 8e9751f45294c..c8caf228ffb5b 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -10,6 +10,7 @@ use rustc_errors::ErrorGuaranteed; use crate::ty; use rustc_hir::def_id::DefId; +use rustc_macros::{HashStable, TypeVisitable}; use rustc_query_system::cache::Cache; pub type SelectionCache<'tcx> = Cache< @@ -193,7 +194,6 @@ pub enum SelectionCandidate<'tcx> { /// The evaluation results are ordered: /// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions` /// implies `EvaluatedToAmbig` implies `EvaluatedToAmbigStackDependent` -/// - `EvaluatedToErr` implies `EvaluatedToErrStackDependent` /// - the "union" of evaluation results is equal to their maximum - /// all the "potential success" candidates can potentially succeed, /// so they are noops when unioned with a definite error, and within @@ -219,52 +219,9 @@ pub enum EvaluationResult { /// variables. We are somewhat imprecise there, so we don't actually /// know the real result. /// - /// This can't be trivially cached for the same reason as `EvaluatedToErrStackDependent`. + /// This can't be trivially cached because the result depends on the + /// stack results. EvaluatedToAmbigStackDependent, - /// Evaluation failed because we encountered an obligation we are already - /// trying to prove on this branch. - /// - /// We know this branch can't be a part of a minimal proof-tree for - /// the "root" of our cycle, because then we could cut out the recursion - /// and maintain a valid proof tree. However, this does not mean - /// that all the obligations on this branch do not hold -- it's possible - /// that we entered this branch "speculatively", and that there - /// might be some other way to prove this obligation that does not - /// go through this cycle -- so we can't cache this as a failure. - /// - /// For example, suppose we have this: - /// - /// ```rust,ignore (pseudo-Rust) - /// pub trait Trait { fn xyz(); } - /// // This impl is "useless", but we can still have - /// // an `impl Trait for SomeUnsizedType` somewhere. - /// impl Trait for T { fn xyz() {} } - /// - /// pub fn foo() { - /// ::xyz(); - /// } - /// ``` - /// - /// When checking `foo`, we have to prove `T: Trait`. This basically - /// translates into this: - /// - /// ```plain,ignore - /// (T: Trait + Sized →_\impl T: Trait), T: Trait ⊢ T: Trait - /// ``` - /// - /// When we try to prove it, we first go the first option, which - /// recurses. This shows us that the impl is "useless" -- it won't - /// tell us that `T: Trait` unless it already implemented `Trait` - /// by some other means. However, that does not prevent `T: Trait` - /// does not hold, because of the bound (which can indeed be satisfied - /// by `SomeUnsizedType` from another crate). - // - // FIXME: when an `EvaluatedToErrStackDependent` goes past its parent root, we - // ought to convert it to an `EvaluatedToErr`, because we know - // there definitely isn't a proof tree for that obligation. Not - // doing so is still sound -- there isn't any proof tree, so the - // branch still can't be a part of a minimal one -- but does not re-enable caching. - EvaluatedToErrStackDependent, /// Evaluation failed. EvaluatedToErr, } @@ -290,13 +247,13 @@ impl EvaluationResult { | EvaluatedToAmbig | EvaluatedToAmbigStackDependent => true, - EvaluatedToErr | EvaluatedToErrStackDependent => false, + EvaluatedToErr => false, } } pub fn is_stack_dependent(self) -> bool { match self { - EvaluatedToAmbigStackDependent | EvaluatedToErrStackDependent => true, + EvaluatedToAmbigStackDependent => true, EvaluatedToOkModuloOpaqueTypes | EvaluatedToOk diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index dc4cd2034156e..4c34bf88c7f96 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -1,10 +1,11 @@ use rustc_ast_ir::try_visit; use rustc_data_structures::intern::Interned; +use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_span::def_id::DefId; use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; use crate::traits::query::NoSolution; -use crate::traits::{Canonical, DefiningAnchor}; +use crate::traits::Canonical; use crate::ty::{ self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, @@ -114,7 +115,6 @@ impl MaybeCause { #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct QueryInput<'tcx, T> { pub goal: Goal<'tcx, T>, - pub anchor: DefiningAnchor<'tcx>, pub predefined_opaques_in_body: PredefinedOpaques<'tcx>, } @@ -164,6 +164,19 @@ pub struct ExternalConstraintsData<'tcx> { // FIXME: implement this. pub region_constraints: QueryRegionConstraints<'tcx>, pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, + pub normalization_nested_goals: NestedNormalizationGoals<'tcx>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)] +pub struct NestedNormalizationGoals<'tcx>(pub Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>); +impl<'tcx> NestedNormalizationGoals<'tcx> { + pub fn empty() -> Self { + NestedNormalizationGoals(vec![]) + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } } // FIXME: Having to clone `region_constraints` for folding feels bad and @@ -183,6 +196,10 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { .iter() .map(|opaque| opaque.try_fold_with(folder)) .collect::>()?, + normalization_nested_goals: self + .normalization_nested_goals + .clone() + .try_fold_with(folder)?, })) } @@ -190,6 +207,7 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData { region_constraints: self.region_constraints.clone().fold_with(folder), opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), + normalization_nested_goals: self.normalization_nested_goals.clone().fold_with(folder), }) } } @@ -197,7 +215,8 @@ impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { impl<'tcx> TypeVisitable> for ExternalConstraints<'tcx> { fn visit_with>>(&self, visitor: &mut V) -> V::Result { try_visit!(self.region_constraints.visit_with(visitor)); - self.opaque_types.visit_with(visitor) + try_visit!(self.opaque_types.visit_with(visitor)); + self.normalization_nested_goals.visit_with(visitor) } } @@ -239,7 +258,7 @@ impl<'tcx> TypeVisitable> for PredefinedOpaques<'tcx> { /// /// This is necessary as we treat nested goals different depending on /// their source. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TypeVisitable, TypeFoldable)] pub enum GoalSource { Misc, /// We're proving a where-bound of an impl. @@ -256,12 +275,6 @@ pub enum GoalSource { ImplWhereBound, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] -pub enum IsNormalizesToHack { - Yes, - No, -} - /// Possible ways the given goal can be proven. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CandidateSource { @@ -320,4 +333,9 @@ pub enum CandidateSource { /// } /// ``` AliasBound, + /// A candidate that is registered only during coherence to represent some + /// yet-unknown impl that could be produced downstream without violating orphan + /// rules. + // FIXME: Merge this with the forced ambiguity candidates, so those don't use `Misc`. + CoherenceUnknowable, } diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 90180af3b167f..cddf9d5f874a3 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -19,11 +19,12 @@ //! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use super::{ - CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, - NoSolution, QueryInput, QueryResult, + CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, NoSolution, + QueryInput, QueryResult, }; use crate::{infer::canonical::CanonicalVarValues, ty}; use format::ProofTreeFormatter; +use rustc_macros::{TypeFoldable, TypeVisitable}; use std::fmt::{Debug, Write}; mod format; @@ -50,7 +51,7 @@ pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>; #[derive(Eq, PartialEq)] pub enum GoalEvaluationKind<'tcx> { Root { orig_values: Vec> }, - Nested { is_normalizes_to_hack: IsNormalizesToHack }, + Nested, } #[derive(Eq, PartialEq)] @@ -60,14 +61,14 @@ pub struct GoalEvaluation<'tcx> { pub evaluation: CanonicalGoalEvaluation<'tcx>, } -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, Debug)] pub struct CanonicalGoalEvaluation<'tcx> { pub goal: CanonicalInput<'tcx>, pub kind: CanonicalGoalEvaluationKind<'tcx>, pub result: QueryResult<'tcx>, } -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, Debug)] pub enum CanonicalGoalEvaluationKind<'tcx> { Overflow, CycleInStack, @@ -86,7 +87,7 @@ pub struct AddedGoalsEvaluation<'tcx> { pub result: Result, } -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, Debug)] pub struct GoalEvaluationStep<'tcx> { pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, @@ -102,6 +103,7 @@ pub struct Probe<'tcx> { /// What happened inside of this probe in chronological order. pub steps: Vec>, pub kind: ProbeKind<'tcx>, + pub final_state: CanonicalState<'tcx, ()>, } impl Debug for Probe<'_> { @@ -121,8 +123,12 @@ pub enum ProbeStep<'tcx> { /// used whenever there are multiple candidates to prove the /// current goalby . NestedProbe(Probe<'tcx>), - CommitIfOkStart, - CommitIfOkSuccess, + /// A call to `EvalCtxt::evaluate_added_goals_make_canonical_response` with + /// `Certainty` was made. This is the certainty passed in, so it's not unified + /// with the certainty of the `try_evaluate_added_goals` that is done within; + /// if it's `Certainty::Yes`, then we can trust that the candidate is "finished" + /// and we didn't force ambiguity for some reason. + MakeCanonicalResponse { shallow_certainty: Certainty }, } /// What kind of probe we're in. In case the probe represents a candidate, or @@ -132,22 +138,19 @@ pub enum ProbeStep<'tcx> { pub enum ProbeKind<'tcx> { /// The root inference context while proving a goal. Root { result: QueryResult<'tcx> }, + /// Trying to normalize an alias by at least one step in `NormalizesTo`. + TryNormalizeNonRigid { result: QueryResult<'tcx> }, /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, - /// Some candidate to prove the current goal. - /// - /// FIXME: Remove this in favor of always using more strongly typed variants. - MiscCandidate { name: &'static str, result: QueryResult<'tcx> }, /// A candidate for proving a trait or alias-relate goal. TraitCandidate { source: CandidateSource, result: QueryResult<'tcx> }, /// Used in the probe that wraps normalizing the non-self type for the unsize /// trait, which is also structurally matched on. UnsizeAssembly, - /// A call to `EvalCtxt::commit_if_ok` which failed, causing the work - /// to be discarded. - CommitIfOk, /// During upcasting from some source object to target object type, used to /// do a probe to find out what projection type(s) may be used to prove that /// the source type upholds all of the target type's object bounds. UpcastProjectionCompatibility, + /// Try to unify an opaque type with an existing key in the storage. + OpaqueTypeStorageLookup { result: QueryResult<'tcx> }, } diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 8d3109a7b284c..2d73be387fdcd 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -55,10 +55,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result { let goal_text = match eval.kind { GoalEvaluationKind::Root { orig_values: _ } => "ROOT GOAL", - GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack { - IsNormalizesToHack::No => "GOAL", - IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL", - }, + GoalEvaluationKind::Nested => "GOAL", }; write!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?; self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation)) @@ -103,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ProbeKind::Root { result } => { write!(self.f, "ROOT RESULT: {result:?}") } + ProbeKind::TryNormalizeNonRigid { result } => { + write!(self.f, "TRY NORMALIZE NON-RIGID: {result:?}") + } ProbeKind::NormalizedSelfTyAssembly => { write!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } @@ -112,11 +112,8 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { ProbeKind::UpcastProjectionCompatibility => { write!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:") } - ProbeKind::CommitIfOk => { - write!(self.f, "COMMIT_IF_OK:") - } - ProbeKind::MiscCandidate { name, result } => { - write!(self.f, "CANDIDATE {name}: {result:?}") + ProbeKind::OpaqueTypeStorageLookup { result } => { + write!(self.f, "PROBING FOR AN EXISTING OPAQUE: {result:?}") } ProbeKind::TraitCandidate { source, result } => { write!(self.f, "CANDIDATE {source:?}: {result:?}") @@ -135,8 +132,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { } ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?, ProbeStep::NestedProbe(probe) => this.format_probe(probe)?, - ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?, - ProbeStep::CommitIfOkSuccess => writeln!(this.f, "COMMIT_IF_OK SUCCESS")?, + ProbeStep::MakeCanonicalResponse { shallow_certainty } => { + writeln!(this.f, "EVALUATE GOALS AND MAKE RESPONSE: {shallow_certainty:?}")? + } } } Ok(()) diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index ba29d4040a1e7..ff5d51bcb66f5 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -5,6 +5,7 @@ use crate::ty::{self, TyCtxt}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::sym; /// A per-trait graph of impls in specialization order. At the moment, this diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 570f896ba299b..dc46b470b6f48 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -4,6 +4,7 @@ use crate::ty::{ TypeVisitableExt, }; use rustc_errors::ErrorGuaranteed; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeVisitable}; #[derive(Hash, Debug, Clone, Copy, Ord, PartialOrd, PartialEq, Eq)] #[derive(TyDecodable, TyEncodable, HashStable, TypeVisitable, TypeFoldable)] diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index c3e8991c63a20..9badf65115ed4 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -1,7 +1,7 @@ use crate::ty::{self, Ty, TyCtxt}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::Span; use rustc_target::abi::FieldIdx; diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 2e1c7df6454aa..77da3fbe1d799 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -12,13 +12,13 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_index::{IndexSlice, IndexVec}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; use rustc_session::DataTypeKind; use rustc_span::symbol::sym; use rustc_target::abi::{ReprOptions, VariantIdx, FIRST_VARIANT}; use std::cell::RefCell; -use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::ops::Range; use std::str; @@ -27,7 +27,7 @@ use super::{Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, Var #[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] pub struct AdtFlags(u16); -bitflags! { +bitflags::bitflags! { impl AdtFlags: u16 { const NO_ADT_FLAGS = 0; /// Indicates whether the ADT is an enum. @@ -102,20 +102,6 @@ pub struct AdtDefData { repr: ReprOptions, } -impl PartialOrd for AdtDefData { - fn partial_cmp(&self, other: &AdtDefData) -> Option { - Some(self.cmp(other)) - } -} - -/// There should be only one AdtDef for each `did`, therefore -/// it is fine to implement `Ord` only based on `did`. -impl Ord for AdtDefData { - fn cmp(&self, other: &AdtDefData) -> Ordering { - self.did.cmp(&other.did) - } -} - impl PartialEq for AdtDefData { #[inline] fn eq(&self, other: &Self) -> bool { @@ -180,7 +166,7 @@ impl<'a> HashStable> for AdtDefData { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] #[rustc_pass_by_value] pub struct AdtDef<'tcx>(pub Interned<'tcx, AdtDefData>); @@ -590,16 +576,15 @@ impl<'tcx> AdtDef<'tcx> { tcx.adt_destructor(self.did()) } - /// Returns a list of types such that `Self: Sized` if and only if that - /// type is `Sized`, or `ty::Error` if this type has a recursive layout. - pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx ty::List>> { - tcx.adt_sized_constraint(self.did()) + /// Returns a type such that `Self: Sized` if and only if that type is `Sized`, + /// or `None` if the type is always sized. + pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option>> { + if self.is_struct() { tcx.adt_sized_constraint(self.did()) } else { None } } } -#[derive(Clone, Copy, Debug)] -#[derive(HashStable)] +#[derive(Clone, Copy, Debug, HashStable)] pub enum Representability { Representable, - Infinite, + Infinite(ErrorGuaranteed), } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 94a5ff13158a2..1cdde3f057c67 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -3,6 +3,7 @@ use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::symbol::{Ident, Symbol}; use super::{TyCtxt, Visibility}; diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs deleted file mode 100644 index af594bc5f24c8..0000000000000 --- a/compiler/rustc_middle/src/ty/binding.rs +++ /dev/null @@ -1,18 +0,0 @@ -use rustc_hir::{BindingAnnotation, ByRef, Mutability}; - -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Debug, Copy, HashStable)] -pub enum BindingMode { - BindByReference(Mutability), - BindByValue(Mutability), -} - -TrivialTypeTraversalImpls! { BindingMode } - -impl BindingMode { - pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode { - match by_ref { - ByRef::No => BindingMode::BindByValue(mutbl), - ByRef::Yes => BindingMode::BindByReference(mutbl), - } - } -} diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index e65585955199f..26c5a865fdc22 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -4,7 +4,7 @@ use crate::ty::{self, Ty}; use rustc_middle::mir; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; /// Types that are represented as ints. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -69,7 +69,7 @@ impl<'tcx> CastTy<'tcx> { ty::Uint(u) => Some(CastTy::Int(IntTy::U(u))), ty::Float(_) => Some(CastTy::Float), ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::Int(IntTy::CEnum)), - ty::RawPtr(mt) => Some(CastTy::Ptr(mt)), + ty::RawPtr(ty, mutbl) => Some(CastTy::Ptr(ty::TypeAndMut { ty, mutbl })), ty::FnPtr(..) => Some(CastTy::FnPtr), ty::Dynamic(_, _, ty::DynStar) => Some(CastTy::DynStar), _ => None, @@ -83,9 +83,9 @@ pub fn mir_cast_kind<'tcx>(from_ty: Ty<'tcx>, cast_ty: Ty<'tcx>) -> mir::CastKin let cast = CastTy::from_ty(cast_ty); let cast_kind = match (from, cast) { (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { - mir::CastKind::PointerExposeAddress + mir::CastKind::PointerExposeProvenance } - (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerFromExposedAddress, + (Some(CastTy::Int(_)), Some(CastTy::Ptr(_))) => mir::CastKind::PointerWithExposedProvenance, (_, Some(CastTy::DynStar)) => mir::CastKind::DynStar, (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => mir::CastKind::IntToInt, (Some(CastTy::FnPtr), Some(CastTy::Ptr(_))) => mir::CastKind::FnPtrToPtr, diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 7db64504f85ea..bade0d564156f 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -6,9 +6,12 @@ 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; +use rustc_hir::HirId; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::def_id::LocalDefIdMap; use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol}; @@ -24,7 +27,7 @@ pub const CAPTURE_STRUCT_LOCAL: mir::Local = mir::Local::from_u32(1); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub struct UpvarPath { - pub hir_id: hir::HirId, + pub hir_id: HirId, } /// Upvars do not get their own `NodeId`. Instead, we use the pair of @@ -38,14 +41,14 @@ pub struct UpvarId { } impl UpvarId { - pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId { + pub fn new(var_hir_id: HirId, closure_def_id: LocalDefId) -> UpvarId { UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id } } } /// Information describing the capture of an upvar. This is computed /// during `typeck`, specifically by `regionck`. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(Eq, PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub enum UpvarCapture { /// Upvar is captured by value. This is always true when the @@ -67,13 +70,13 @@ pub type MinCaptureInformationMap<'tcx> = LocalDefIdMap = FxIndexMap>; +pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; /// Part of `MinCaptureInformationMap`; List of `CapturePlace`s. pub type MinCaptureList<'tcx> = Vec>; /// A composite describing a `Place` that is captured by a closure. -#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(Eq, PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub struct CapturedPlace<'tcx> { /// Name and span where the binding happens. @@ -134,7 +137,7 @@ impl<'tcx> CapturedPlace<'tcx> { /// Returns the hir-id of the root variable for the captured place. /// e.g., if `a.b.c` was captured, would return the hir-id for `a`. - pub fn get_root_variable(&self) -> hir::HirId { + pub fn get_root_variable(&self) -> HirId { match self.place.base { HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, base => bug!("Expected upvar, found={:?}", base), @@ -192,7 +195,7 @@ impl<'tcx> CapturedPlace<'tcx> { #[derive(Copy, Clone, Debug, HashStable)] pub struct ClosureTypeInfo<'tcx> { user_provided_sig: ty::CanonicalPolyFnSig<'tcx>, - captures: &'tcx [&'tcx ty::CapturedPlace<'tcx>], + captures: &'tcx ty::List<&'tcx ty::CapturedPlace<'tcx>>, kind_origin: Option<&'tcx (Span, HirPlace<'tcx>)>, } @@ -201,7 +204,7 @@ fn closure_typeinfo<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ClosureTypeInfo let typeck_results = tcx.typeck(def); let user_provided_sig = typeck_results.user_provided_sigs[&def]; let captures = typeck_results.closure_min_captures_flattened(def); - let captures = tcx.arena.alloc_from_iter(captures); + let captures = tcx.mk_captures_from_iter(captures); let hir_id = tcx.local_def_id_to_hir_id(def); let kind_origin = typeck_results.closure_kind_origins().get(hir_id); ClosureTypeInfo { user_provided_sig, captures, kind_origin } @@ -253,7 +256,7 @@ pub fn is_ancestor_or_same_capture( /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) /// for a particular capture as well as identifying the part of the source code /// that triggered this capture to occur. -#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +#[derive(Eq, PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub struct CaptureInfo { /// Expr Id pointing to use that resulted in selecting the current capture kind @@ -285,12 +288,12 @@ pub struct CaptureInfo { /// /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured, /// but we won't see it being used during capture analysis, since it's essentially a discard. - pub capture_kind_expr_id: Option, + pub capture_kind_expr_id: Option, /// Expr Id pointing to use that resulted the corresponding place being captured /// /// See `capture_kind_expr_id` for example. /// - pub path_expr_id: Option, + pub path_expr_id: Option, /// Capture mode that was selected pub capture_kind: UpvarCapture, @@ -332,7 +335,7 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc curr_string } -#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] +#[derive(Eq, Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable, Hash)] #[derive(TypeFoldable, TypeVisitable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. @@ -415,6 +418,76 @@ impl BorrowKind { } } +pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>( + parent_captures: impl IntoIterator>, + child_captures: impl IntoIterator>, + mut for_each: impl FnMut((usize, &'a CapturedPlace<'tcx>), (usize, &'a CapturedPlace<'tcx>)) -> T, +) -> impl Iterator + Captures<'a> + Captures<'tcx> { + std::iter::from_coroutine( + #[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/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index ddbc0bffaedde..c0effe9804cd7 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -148,6 +148,12 @@ impl<'tcx, E: TyEncoder>> Encodable for ty::Const<'tcx> { } } +impl<'tcx, E: TyEncoder>> Encodable for ty::Pattern<'tcx> { + fn encode(&self, e: &mut E) { + self.0.0.encode(e); + } +} + impl<'tcx, E: TyEncoder>> Encodable for ConstAllocation<'tcx> { fn encode(&self, e: &mut E) { self.inner().encode(e) @@ -364,6 +370,12 @@ impl<'tcx, D: TyDecoder>> Decodable for ty::Const<'tcx> { } } +impl<'tcx, D: TyDecoder>> Decodable for ty::Pattern<'tcx> { + fn decode(decoder: &mut D) -> Self { + decoder.interner().mk_pat(Decodable::decode(decoder)) + } +} + impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] { fn decode(decoder: &mut D) -> &'tcx Self { decoder @@ -414,7 +426,9 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List>> RefDecodable<'tcx, D> for ty::List> { +impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> + for ty::ListWithCachedTypeInfo> +{ fn decode(decoder: &mut D) -> &'tcx Self { let len = decoder.read_usize(); decoder.interner().mk_clauses_from_iter( @@ -441,6 +455,12 @@ impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List>> Decodable for &'tcx ty::List { + fn decode(d: &mut D) -> Self { + RefDecodable::decode(d) + } +} + impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for ty::List<(VariantIdx, FieldIdx)> { @@ -458,11 +478,10 @@ impl_decodable_via_ref! { &'tcx ty::List>, &'tcx traits::ImplSource<'tcx, ()>, &'tcx mir::Body<'tcx>, - &'tcx mir::UnsafetyCheckResult, &'tcx mir::BorrowCheckResult<'tcx>, &'tcx mir::coverage::CodeRegion, &'tcx ty::List, - &'tcx ty::List>, + &'tcx ty::ListWithCachedTypeInfo>, &'tcx ty::List, &'tcx ty::List<(VariantIdx, FieldIdx)>, } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 5b62c0bf931af..a65b3a41ade3b 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -6,7 +6,7 @@ use rustc_error_messages::MultiSpan; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_type_ir::ConstKind as IrConstKind; use rustc_type_ir::{ConstTy, IntoKind, TypeFlags, WithCachedTypeInfo}; @@ -22,8 +22,11 @@ pub use valtree::*; pub type ConstKind<'tcx> = IrConstKind>; +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(ConstKind<'_>, 32); + /// Use this rather than `ConstData`, whenever possible. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] #[rustc_pass_by_value] pub struct Const<'tcx>(pub(super) Interned<'tcx, WithCachedTypeInfo>>); @@ -52,15 +55,15 @@ impl<'tcx> ConstTy> for Const<'tcx> { } /// Typed constant value. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(HashStable, TyEncodable, TyDecodable)] pub struct ConstData<'tcx> { pub ty: Ty<'tcx>, pub kind: ConstKind<'tcx>, } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(ConstData<'_>, 40); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(ConstData<'_>, 40); impl<'tcx> Const<'tcx> { #[inline] @@ -335,7 +338,7 @@ impl<'tcx> Const<'tcx> { self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - span: Option, + span: Span, ) -> Result, ErrorHandled> { assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); match self.kind() { @@ -349,7 +352,7 @@ impl<'tcx> Const<'tcx> { else { // This can happen when we run on ill-typed code. let e = tcx.dcx().span_delayed_bug( - span.unwrap_or(DUMMY_SP), + span, "`ty::Const::eval` called on a non-valtree-compatible type", ); return Err(e.into()); @@ -362,14 +365,14 @@ impl<'tcx> Const<'tcx> { | ConstKind::Infer(_) | ConstKind::Bound(_, _) | ConstKind::Placeholder(_) - | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span.unwrap_or(DUMMY_SP))), + | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span)), } } /// Normalizes the constant to a value or an error if possible. #[inline] pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - match self.eval(tcx, param_env, None) { + match self.eval(tcx, param_env, DUMMY_SP) { Ok(val) => Self::new_value(tcx, val, self.ty()), Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into(), self.ty()), Err(ErrorHandled::TooGeneric(_span)) => self, @@ -382,7 +385,7 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Option { - self.eval(tcx, param_env, None).ok()?.try_to_scalar() + self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar() } #[inline] @@ -406,7 +409,7 @@ impl<'tcx> Const<'tcx> { let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size; // if `ty` does not depend on generic parameters, use an empty param_env - int.to_bits(size).ok() + int.try_to_bits(size).ok() } #[inline] diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 71d7dfd8b01ef..40ac87873a0a4 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -126,7 +126,7 @@ impl IntoDiagArg for ConstInt { /// /// This is a packed struct in order to allow this type to be optimally embedded in enums /// (like Scalar). -#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Clone, Copy, Eq, PartialEq, Hash)] #[repr(packed)] pub struct ScalarInt { /// The first `size` bytes of `data` are the value. @@ -167,9 +167,12 @@ impl Decodable for ScalarInt { impl ScalarInt { pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() }; - pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() }; + fn raw(data: u128, size: Size) -> Self { + Self { data, size: NonZero::new(size.bytes() as u8).unwrap() } + } + #[inline] pub fn size(self) -> Size { Size::from_bytes(self.size.get()) @@ -196,7 +199,7 @@ impl ScalarInt { #[inline] pub fn null(size: Size) -> Self { - Self { data: 0, size: NonZero::new(size.bytes() as u8).unwrap() } + Self::raw(0, size) } #[inline] @@ -207,11 +210,15 @@ impl ScalarInt { #[inline] pub fn try_from_uint(i: impl Into, size: Size) -> Option { let data = i.into(); - if size.truncate(data) == data { - Some(Self { data, size: NonZero::new(size.bytes() as u8).unwrap() }) - } else { - None - } + if size.truncate(data) == data { Some(Self::raw(data, size)) } else { None } + } + + /// Returns the truncated result, and whether truncation changed the value. + #[inline] + pub fn truncate_from_uint(i: impl Into, size: Size) -> (Self, bool) { + let data = i.into(); + let r = Self::raw(size.truncate(data), size); + (r, r.data != data) } #[inline] @@ -220,26 +227,27 @@ impl ScalarInt { // `into` performed sign extension, we have to truncate let truncated = size.truncate(i as u128); if size.sign_extend(truncated) as i128 == i { - Some(Self { data: truncated, size: NonZero::new(size.bytes() as u8).unwrap() }) + Some(Self::raw(truncated, size)) } else { None } } + /// Returns the truncated result, and whether truncation changed the value. #[inline] - pub fn try_from_target_usize(i: impl Into, tcx: TyCtxt<'_>) -> Option { - Self::try_from_uint(i, tcx.data_layout.pointer_size) + pub fn truncate_from_int(i: impl Into, size: Size) -> (Self, bool) { + let data = i.into(); + let r = Self::raw(size.truncate(data as u128), size); + (r, size.sign_extend(r.data) as i128 != data) } #[inline] - pub fn assert_bits(self, target_size: Size) -> u128 { - self.to_bits(target_size).unwrap_or_else(|size| { - bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes()) - }) + pub fn try_from_target_usize(i: impl Into, tcx: TyCtxt<'_>) -> Option { + Self::try_from_uint(i, tcx.data_layout.pointer_size) } #[inline] - pub fn to_bits(self, target_size: Size) -> Result { + pub fn try_to_bits(self, target_size: Size) -> Result { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); if target_size.bytes() == u64::from(self.size.get()) { self.check_data(); @@ -249,16 +257,28 @@ impl ScalarInt { } } + #[inline] + pub fn assert_bits(self, target_size: Size) -> u128 { + self.try_to_bits(target_size).unwrap_or_else(|size| { + bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes()) + }) + } + /// Tries to convert the `ScalarInt` to an unsigned integer of the given size. /// Fails if the size of the `ScalarInt` is not equal to `size` and returns the /// `ScalarInt`s size in that case. #[inline] pub fn try_to_uint(self, size: Size) -> Result { - self.to_bits(size) + self.try_to_bits(size) + } + + #[inline] + pub fn assert_uint(self, size: Size) -> u128 { + self.assert_bits(size) } // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt` - // in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in + // in not equal to 1 byte and returns the `size` value of the `ScalarInt` in // that case. #[inline] pub fn try_to_u8(self) -> Result { @@ -266,7 +286,7 @@ impl ScalarInt { } /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt` - /// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in + /// in not equal to 2 bytes and returns the `size` value of the `ScalarInt` in /// that case. #[inline] pub fn try_to_u16(self) -> Result { @@ -274,7 +294,7 @@ impl ScalarInt { } /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt` - /// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in + /// in not equal to 4 bytes and returns the `size` value of the `ScalarInt` in /// that case. #[inline] pub fn try_to_u32(self) -> Result { @@ -282,7 +302,7 @@ impl ScalarInt { } /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt` - /// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in + /// in not equal to 8 bytes and returns the `size` value of the `ScalarInt` in /// that case. #[inline] pub fn try_to_u64(self) -> Result { @@ -290,7 +310,7 @@ impl ScalarInt { } /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt` - /// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in + /// in not equal to 16 bytes and returns the `size` value of the `ScalarInt` in /// that case. #[inline] pub fn try_to_u128(self) -> Result { @@ -303,7 +323,7 @@ impl ScalarInt { } // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt` - // in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size` + // in not equal to 1 byte or if the value is not 0 or 1 and returns the `size` // value of the `ScalarInt` in that case. #[inline] pub fn try_to_bool(self) -> Result { @@ -319,40 +339,46 @@ impl ScalarInt { /// `ScalarInt`s size in that case. #[inline] pub fn try_to_int(self, size: Size) -> Result { - let b = self.to_bits(size)?; + let b = self.try_to_bits(size)?; Ok(size.sign_extend(b) as i128) } + #[inline] + pub fn assert_int(self, size: Size) -> i128 { + let b = self.assert_bits(size); + size.sign_extend(b) as i128 + } + /// Tries to convert the `ScalarInt` to i8. - /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 1 }` + /// Fails if the size of the `ScalarInt` is not equal to 1 byte /// and returns the `ScalarInt`s size in that case. pub fn try_to_i8(self) -> Result { self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap()) } /// Tries to convert the `ScalarInt` to i16. - /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 2 }` + /// Fails if the size of the `ScalarInt` is not equal to 2 bytes /// and returns the `ScalarInt`s size in that case. pub fn try_to_i16(self) -> Result { self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap()) } /// Tries to convert the `ScalarInt` to i32. - /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 4 }` + /// Fails if the size of the `ScalarInt` is not equal to 4 bytes /// and returns the `ScalarInt`s size in that case. pub fn try_to_i32(self) -> Result { self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap()) } /// Tries to convert the `ScalarInt` to i64. - /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 8 }` + /// Fails if the size of the `ScalarInt` is not equal to 8 bytes /// and returns the `ScalarInt`s size in that case. pub fn try_to_i64(self) -> Result { self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap()) } /// Tries to convert the `ScalarInt` to i128. - /// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 16 }` + /// Fails if the size of the `ScalarInt` is not equal to 16 bytes /// and returns the `ScalarInt`s size in that case. pub fn try_to_i128(self) -> Result { self.try_to_int(Size::from_bits(128)) @@ -366,7 +392,7 @@ impl ScalarInt { #[inline] pub fn try_to_float(self) -> Result { // Going through `to_uint` to check size and truncation. - Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?)) + Ok(F::from_bits(self.try_to_bits(Size::from_bits(F::BITS))?)) } #[inline] @@ -415,7 +441,7 @@ macro_rules! try_from { fn try_from(int: ScalarInt) -> Result { // The `unwrap` cannot fail because to_bits (if it succeeds) // is guaranteed to return a value that fits into the size. - int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>())) + int.try_to_bits(Size::from_bytes(std::mem::size_of::<$ty>())) .map(|u| u.try_into().unwrap()) } } @@ -450,7 +476,7 @@ impl TryFrom for char { #[inline] fn try_from(int: ScalarInt) -> Result { - let Ok(bits) = int.to_bits(Size::from_bytes(std::mem::size_of::())) else { + let Ok(bits) = int.try_to_bits(Size::from_bytes(std::mem::size_of::())) else { return Err(CharTryFromScalarInt); }; match char::from_u32(bits.try_into().unwrap()) { @@ -472,7 +498,7 @@ impl TryFrom for Half { type Error = Size; #[inline] fn try_from(int: ScalarInt) -> Result { - int.to_bits(Size::from_bytes(2)).map(Self::from_bits) + int.try_to_bits(Size::from_bytes(2)).map(Self::from_bits) } } @@ -488,7 +514,7 @@ impl TryFrom for Single { type Error = Size; #[inline] fn try_from(int: ScalarInt) -> Result { - int.to_bits(Size::from_bytes(4)).map(Self::from_bits) + int.try_to_bits(Size::from_bytes(4)).map(Self::from_bits) } } @@ -504,7 +530,7 @@ impl TryFrom for Double { type Error = Size; #[inline] fn try_from(int: ScalarInt) -> Result { - int.to_bits(Size::from_bytes(8)).map(Self::from_bits) + int.try_to_bits(Size::from_bytes(8)).map(Self::from_bits) } } @@ -520,7 +546,7 @@ impl TryFrom for Quad { type Error = Size; #[inline] fn try_from(int: ScalarInt) -> Result { - int.to_bits(Size::from_bytes(16)).map(Self::from_bits) + int.try_to_bits(Size::from_bytes(16)).map(Self::from_bits) } } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 705987d92fed4..7e49b0ac915c6 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -4,10 +4,10 @@ use crate::ty::abstract_const::CastKind; use crate::ty::GenericArgsRef; use crate::ty::{self, visit::TypeVisitableExt as _, List, Ty, TyCtxt}; use rustc_hir::def_id::DefId; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; /// An unevaluated (potentially generic) constant used in the type-system. -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, Eq, PartialEq, TyEncodable, TyDecodable)] #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct UnevaluatedConst<'tcx> { pub def: DefId, @@ -62,7 +62,7 @@ impl<'tcx> UnevaluatedConst<'tcx> { } } -#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Hash)] #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] pub enum Expr<'tcx> { Binop(mir::BinOp, Const<'tcx>, Const<'tcx>), @@ -71,8 +71,5 @@ pub enum Expr<'tcx> { Cast(CastKind, Const<'tcx>, Ty<'tcx>), } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(Expr<'_>, 24); - -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(super::ConstKind<'_>, 32); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(Expr<'_>, 24); diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index ffa0e89c47328..96bc5515a5695 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -3,7 +3,7 @@ use crate::mir::interpret::Scalar; use crate::ty::{self, Ty, TyCtxt}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq)] #[derive(HashStable)] /// This datastructure is used to represent the value of constants used in the type system. /// diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 17ba97c5fd3a7..d2eacdf762f1b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -25,13 +25,15 @@ use crate::traits::solve::{ ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, PredefinedOpaquesData, }; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind, - ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate, - PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, - TypeVisitable, Visibility, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, ConstData, + GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, Pattern, + PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, + Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, TypeVisitable, + Visibility, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; +use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::intern::Interned; @@ -39,11 +41,13 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal}; +use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, RwLock, WorkerLocal}; #[cfg(parallel_compiler)] use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan}; +use rustc_errors::{ + Applicability, Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; @@ -52,7 +56,7 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{HirId, Node, TraitCandidate}; use rustc_index::IndexVec; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; @@ -81,6 +85,7 @@ use std::ops::{Bound, Deref}; #[allow(rustc::usage_of_ty_tykind)] impl<'tcx> Interner for TyCtxt<'tcx> { type DefId = DefId; + type DefiningOpaqueTypes = &'tcx ty::List; type AdtDef = ty::AdtDef<'tcx>; type GenericArgs = ty::GenericArgsRef<'tcx>; type GenericArg = ty::GenericArg<'tcx>; @@ -92,6 +97,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type CanonicalVars = CanonicalVarInfos<'tcx>; type Ty = Ty<'tcx>; + type Pat = Pattern<'tcx>; type Tys = &'tcx List>; type AliasTy = ty::AliasTy<'tcx>; type ParamTy = ParamTy; @@ -127,6 +133,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type SubtypePredicate = ty::SubtypePredicate<'tcx>; type CoercePredicate = ty::CoercePredicate<'tcx>; type ClosureKind = ty::ClosureKind; + type Clauses = ty::Clauses<'tcx>; fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { self.mk_canonical_var_infos(infos) @@ -149,10 +156,11 @@ pub struct CtxtInterners<'tcx> { region: InternedSet<'tcx, RegionKind<'tcx>>, poly_existential_predicates: InternedSet<'tcx, List>>, predicate: InternedSet<'tcx, WithCachedTypeInfo>>>, - clauses: InternedSet<'tcx, List>>, + clauses: InternedSet<'tcx, ListWithCachedTypeInfo>>, projs: InternedSet<'tcx, List>, place_elems: InternedSet<'tcx, List>>, const_: InternedSet<'tcx, WithCachedTypeInfo>>, + pat: InternedSet<'tcx, PatternKind<'tcx>>, const_allocation: InternedSet<'tcx, Allocation>, bound_variable_kinds: InternedSet<'tcx, List>, layout: InternedSet<'tcx, LayoutS>, @@ -161,6 +169,7 @@ pub struct CtxtInterners<'tcx> { predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>, fields: InternedSet<'tcx, List>, local_def_ids: InternedSet<'tcx, List>, + captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>, } @@ -180,6 +189,7 @@ impl<'tcx> CtxtInterners<'tcx> { projs: Default::default(), place_elems: Default::default(), const_: Default::default(), + pat: Default::default(), const_allocation: Default::default(), bound_variable_kinds: Default::default(), layout: Default::default(), @@ -188,6 +198,7 @@ impl<'tcx> CtxtInterners<'tcx> { predefined_opaques_in_body: Default::default(), fields: Default::default(), local_def_ids: Default::default(), + captures: Default::default(), offset_of: Default::default(), } } @@ -283,6 +294,24 @@ impl<'tcx> CtxtInterners<'tcx> { .0, )) } + + fn intern_clauses(&self, clauses: &[Clause<'tcx>]) -> Clauses<'tcx> { + if clauses.is_empty() { + ListWithCachedTypeInfo::empty() + } else { + self.clauses + .intern_ref(clauses, || { + let flags = super::flags::FlagComputation::for_clauses(clauses); + + InternedInSet(ListWithCachedTypeInfo::from_arena( + &*self.arena, + flags.into(), + clauses, + )) + }) + .0 + } + } } // For these preinterned values, an alternative would be to have @@ -536,13 +565,6 @@ impl<'tcx> TyCtxt<'tcx> { TyCtxtFeed { tcx: self, key: () } } - /// Can only be fed before queries are run, and is thus exempt from any - /// incremental issues. Do not use except for the initial query feeding. - pub fn feed_local_crate(self) -> TyCtxtFeed<'tcx, CrateNum> { - self.dep_graph.assert_ignored(); - TyCtxtFeed { tcx: self, key: LOCAL_CRATE } - } - /// Only used in the resolver to register the `CRATE_DEF_ID` `DefId` and feed /// some queries for it. It will panic if used twice. pub fn create_local_crate_def_id(self, span: Span) -> TyCtxtFeed<'tcx, LocalDefId> { @@ -594,6 +616,27 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { pub fn feed_owner_id(&self) -> TyCtxtFeed<'tcx, hir::OwnerId> { TyCtxtFeed { tcx: self.tcx, key: hir::OwnerId { def_id: self.key } } } + + // Fills in all the important parts needed by HIR queries + pub fn feed_hir(&self) { + self.local_def_id_to_hir_id(HirId::make_owner(self.def_id())); + + let node = hir::OwnerNode::Synthetic; + let bodies = Default::default(); + let attrs = hir::AttributeMap::EMPTY; + + let (opt_hash_including_bodies, _) = self.tcx.hash_owner_nodes(node, &bodies, &attrs.map); + let node = node.into(); + self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes { + opt_hash_including_bodies, + nodes: IndexVec::from_elem_n( + hir::ParentedNode { parent: hir::ItemLocalId::INVALID, node }, + 1, + ), + bodies, + }))); + self.feed_owner_id().hir_attrs(attrs); + } } /// The central data structure of the compiler. It stores references @@ -700,16 +743,31 @@ pub struct GlobalCtxt<'tcx> { /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock>, + + current_gcx: CurrentGcx, } impl<'tcx> GlobalCtxt<'tcx> { /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of /// `f`. - pub fn enter<'a: 'tcx, F, R>(&'a self, f: F) -> R + pub fn enter(&'tcx self, f: F) -> R where F: FnOnce(TyCtxt<'tcx>) -> R, { let icx = tls::ImplicitCtxt::new(self); + + // Reset `current_gcx` to `None` when we exit. + let _on_drop = defer(move || { + *self.current_gcx.value.write() = None; + }); + + // Set this `GlobalCtxt` as the current one. + { + let mut guard = self.current_gcx.value.write(); + assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); + *guard = Some(self as *const _ as *const ()); + } + tls::enter_context(&icx, || f(icx.tcx)) } @@ -718,7 +776,51 @@ impl<'tcx> GlobalCtxt<'tcx> { } } +/// This is used to get a reference to a `GlobalCtxt` if one is available. +/// +/// This is needed to allow the deadlock handler access to `GlobalCtxt` to look for query cycles. +/// It cannot use the `TLV` global because that's only guaranteed to be defined on the thread +/// creating the `GlobalCtxt`. Other threads have access to the `TLV` only inside Rayon jobs, but +/// the deadlock handler is not called inside such a job. +#[derive(Clone)] +pub struct CurrentGcx { + /// This stores a pointer to a `GlobalCtxt`. This is set to `Some` inside `GlobalCtxt::enter` + /// and reset to `None` when that function returns or unwinds. + value: Lrc>>, +} + +#[cfg(parallel_compiler)] +unsafe impl DynSend for CurrentGcx {} +#[cfg(parallel_compiler)] +unsafe impl DynSync for CurrentGcx {} + +impl CurrentGcx { + pub fn new() -> Self { + Self { value: Lrc::new(RwLock::new(None)) } + } + + pub fn access(&self, f: impl for<'tcx> FnOnce(&'tcx GlobalCtxt<'tcx>) -> R) -> R { + let read_guard = self.value.read(); + let gcx: *const GlobalCtxt<'_> = read_guard.unwrap() as *const _; + // SAFETY: We hold the read lock for the `GlobalCtxt` pointer. That prevents + // `GlobalCtxt::enter` from returning as it would first acquire the write lock. + // This ensures the `GlobalCtxt` is live during `f`. + f(unsafe { &*gcx }) + } +} + impl<'tcx> TyCtxt<'tcx> { + pub fn has_typeck_results(self, def_id: LocalDefId) -> bool { + // Closures' typeck results come from their outermost function, + // as they are part of the same "inference environment". + let typeck_root_def_id = self.typeck_root_def_id(def_id.to_def_id()); + if typeck_root_def_id != def_id.to_def_id() { + return self.has_typeck_results(typeck_root_def_id.expect_local()); + } + + self.hir_node_by_def_id(def_id).body_id().is_some() + } + /// Expects a body and returns its codegen attributes. /// /// Unlike `codegen_fn_attrs`, this returns `CodegenFnAttrs::EMPTY` for @@ -836,6 +938,7 @@ impl<'tcx> TyCtxt<'tcx> { query_kinds: &'tcx [DepKindStruct<'tcx>], query_system: QuerySystem<'tcx>, hooks: crate::hooks::Providers, + current_gcx: CurrentGcx, ) -> GlobalCtxt<'tcx> { let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| { s.dcx().emit_fatal(err); @@ -870,6 +973,7 @@ impl<'tcx> TyCtxt<'tcx> { canonical_param_env_cache: Default::default(), data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), + current_gcx, } } @@ -882,6 +986,13 @@ impl<'tcx> TyCtxt<'tcx> { self.get_lang_items(()) } + /// Gets a `Ty` representing the [`LangItem::OrderingEnum`] + #[track_caller] + pub fn ty_ordering_enum(self, span: Option) -> Ty<'tcx> { + let ordering_enum = self.require_lang_item(hir::LangItem::OrderingEnum, span); + self.type_of(ordering_enum).no_bound_vars().unwrap() + } + /// Obtain the given diagnostic item's `DefId`. Use `is_diagnostic_item` if you just want to /// compare against another `DefId`, since `is_diagnostic_item` is cheaper. pub fn get_diagnostic_item(self, name: Symbol) -> Option { @@ -1033,14 +1144,23 @@ impl<'tcx> TyCtxt<'tcx> { if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) { LOCAL_CRATE } else { - self.cstore_untracked().stable_crate_id_to_crate_num(stable_crate_id) + *self + .untracked() + .stable_crate_ids + .read() + .get(&stable_crate_id) + .unwrap_or_else(|| bug!("uninterned StableCrateId: {stable_crate_id:?}")) } } /// Converts a `DefPathHash` to its corresponding `DefId` in the current compilation /// session, if it still exists. This is used during incremental compilation to /// turn a deserialized `DefPathHash` into its current `DefId`. - pub fn def_path_hash_to_def_id(self, hash: DefPathHash, err: &mut dyn FnMut() -> !) -> DefId { + pub fn def_path_hash_to_def_id( + self, + hash: DefPathHash, + err_msg: &dyn std::fmt::Debug, + ) -> DefId { debug!("def_path_hash_to_def_id({:?})", hash); let stable_crate_id = hash.stable_crate_id(); @@ -1048,13 +1168,13 @@ impl<'tcx> TyCtxt<'tcx> { // If this is a DefPathHash from the local crate, we can look up the // DefId in the tcx's `Definitions`. if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) { - self.untracked.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id() + self.untracked + .definitions + .read() + .local_def_path_hash_to_def_id(hash, err_msg) + .to_def_id() } else { - // If this is a DefPathHash from an upstream crate, let the CrateStore map - // it to a DefId. - let cstore = &*self.cstore_untracked(); - let cnum = cstore.stable_crate_id_to_crate_num(stable_crate_id); - cstore.def_path_hash_to_def_id(cnum, hash) + self.def_path_hash_to_def_id_extern(hash, stable_crate_id) } } @@ -1145,26 +1265,42 @@ impl<'tcx> TyCtxt<'tcx> { feed } + pub fn create_crate_num( + self, + stable_crate_id: StableCrateId, + ) -> Result, CrateNum> { + if let Some(&existing) = self.untracked().stable_crate_ids.read().get(&stable_crate_id) { + return Err(existing); + } + + let num = CrateNum::new(self.untracked().stable_crate_ids.read().len()); + self.untracked().stable_crate_ids.write().insert(stable_crate_id, num); + Ok(TyCtxtFeed { key: num, tcx: self }) + } + pub fn iter_local_def_id(self) -> impl Iterator + 'tcx { // Create a dependency to the red node to be sure we re-execute this when the amount of // definitions change. self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE); let definitions = &self.untracked.definitions; - std::iter::from_coroutine(|| { - let mut i = 0; - - // Recompute the number of definitions each time, because our caller may be creating - // new ones. - while i < { definitions.read().num_definitions() } { - let local_def_index = rustc_span::def_id::DefIndex::from_usize(i); - yield LocalDefId { local_def_index }; - i += 1; - } + std::iter::from_coroutine( + #[coroutine] + || { + let mut i = 0; + + // Recompute the number of definitions each time, because our caller may be creating + // new ones. + while i < { definitions.read().num_definitions() } { + let local_def_index = rustc_span::def_id::DefIndex::from_usize(i); + yield LocalDefId { local_def_index }; + i += 1; + } - // Freeze definitions once we finish iterating on them, to prevent adding new ones. - definitions.freeze(); - }) + // Freeze definitions once we finish iterating on them, to prevent adding new ones. + definitions.freeze(); + }, + ) } pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable { @@ -1470,6 +1606,7 @@ macro_rules! nop_list_lift { nop_lift! {type_; Ty<'a> => Ty<'tcx>} nop_lift! {region; Region<'a> => Region<'tcx>} nop_lift! {const_; Const<'a> => Const<'tcx>} +nop_lift! {pat; Pattern<'a> => Pattern<'tcx>} nop_lift! {const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx>} nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>} nop_lift! {predicate; Clause<'a> => Clause<'tcx>} @@ -1503,7 +1640,7 @@ macro_rules! nop_slice_lift { nop_slice_lift! {ty::ValTree<'a> => ty::ValTree<'tcx>} TrivialLiftImpls! { - ImplPolarity, Promoted + ImplPolarity, PredicatePolarity, Promoted } macro_rules! sty_debug_print { @@ -1607,6 +1744,7 @@ impl<'tcx> TyCtxt<'tcx> { Param, Infer, Alias, + Pat, Foreign )?; @@ -1694,6 +1832,29 @@ impl<'tcx, T: Hash> Hash for InternedInSet<'tcx, List> { } } +impl<'tcx, T> Borrow<[T]> for InternedInSet<'tcx, ListWithCachedTypeInfo> { + fn borrow(&self) -> &[T] { + &self.0[..] + } +} + +impl<'tcx, T: PartialEq> PartialEq for InternedInSet<'tcx, ListWithCachedTypeInfo> { + fn eq(&self, other: &InternedInSet<'tcx, ListWithCachedTypeInfo>) -> bool { + // The `Borrow` trait requires that `x.borrow() == y.borrow()` equals + // `x == y`. + self.0[..] == other.0[..] + } +} + +impl<'tcx, T: Eq> Eq for InternedInSet<'tcx, ListWithCachedTypeInfo> {} + +impl<'tcx, T: Hash> Hash for InternedInSet<'tcx, ListWithCachedTypeInfo> { + fn hash(&self, s: &mut H) { + // The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`. + self.0[..].hash(s) + } +} + macro_rules! direct_interners { ($($name:ident: $vis:vis $method:ident($ty:ty): $ret_ctor:ident -> $ret_ty:ty,)+) => { $(impl<'tcx> Borrow<$ty> for InternedInSet<'tcx, $ty> { @@ -1735,6 +1896,7 @@ macro_rules! direct_interners { // crate only, and have a corresponding `mk_` function. direct_interners! { region: pub(crate) intern_region(RegionKind<'tcx>): Region -> Region<'tcx>, + pat: pub mk_pat(PatternKind<'tcx>): Pattern -> Pattern<'tcx>, const_allocation: pub mk_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>, layout: pub mk_layout(LayoutS): Layout -> Layout<'tcx>, adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>, @@ -1752,7 +1914,7 @@ macro_rules! slice_interners { List::empty() } else { self.interners.$field.intern_ref(v, || { - InternedInSet(List::from_arena(&*self.arena, v)) + InternedInSet(List::from_arena(&*self.arena, (), v)) }).0 } })+ @@ -1769,12 +1931,12 @@ slice_interners!( type_lists: pub mk_type_list(Ty<'tcx>), canonical_var_infos: pub mk_canonical_var_infos(CanonicalVarInfo<'tcx>), poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>), - clauses: intern_clauses(Clause<'tcx>), projs: pub mk_projs(ProjectionKind), place_elems: pub mk_place_elems(PlaceElem<'tcx>), bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind), fields: pub mk_fields(FieldIdx), local_def_ids: intern_local_def_ids(LocalDefId), + captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>), offset_of: pub mk_offset_of((VariantIdx, FieldIdx)), ); @@ -1805,12 +1967,12 @@ impl<'tcx> TyCtxt<'tcx> { let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() else { return false }; let future_trait = self.require_lang_item(LangItem::Future, None); - self.explicit_item_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| { + self.explicit_item_super_predicates(def_id).skip_binder().iter().any(|&(predicate, _)| { let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else { return false; }; trait_predicate.trait_ref.def_id == future_trait - && trait_predicate.polarity == ImplPolarity::Positive + && trait_predicate.polarity == PredicatePolarity::Positive }) } @@ -1880,33 +2042,104 @@ impl<'tcx> TyCtxt<'tcx> { if pred.kind() != binder { self.mk_predicate(binder) } else { pred } } - #[inline(always)] - pub(crate) fn check_and_mk_args( + pub fn check_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) -> bool { + self.check_args_compatible_inner(def_id, args, false) + } + + fn check_args_compatible_inner( self, - _def_id: DefId, - args: impl IntoIterator>>, - ) -> GenericArgsRef<'tcx> { - let args = args.into_iter().map(Into::into); - #[cfg(debug_assertions)] + def_id: DefId, + args: &'tcx [ty::GenericArg<'tcx>], + nested: bool, + ) -> bool { + let generics = self.generics_of(def_id); + + // IATs themselves have a weird arg setup (self + own args), but nested items *in* IATs + // (namely: opaques, i.e. ATPITs) do not. + let own_args = if !nested + && let DefKind::AssocTy = self.def_kind(def_id) + && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - let generics = self.generics_of(_def_id); + if generics.params.len() + 1 != args.len() { + return false; + } + + if !matches!(args[0].unpack(), ty::GenericArgKind::Type(_)) { + return false; + } - let n = if let DefKind::AssocTy = self.def_kind(_def_id) - && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id)) + &args[1..] + } else { + if generics.count() != args.len() { + return false; + } + + let (parent_args, own_args) = args.split_at(generics.parent_count); + + if let Some(parent) = generics.parent + && !self.check_args_compatible_inner(parent, parent_args, true) { - // If this is an inherent projection. - generics.params.len() + 1 - } else { - generics.count() - }; - assert_eq!( - (n, Some(n)), - args.size_hint(), - "wrong number of generic parameters for {_def_id:?}: {:?}", - args.collect::>(), - ); + return false; + } + + own_args + }; + + for (param, arg) in std::iter::zip(&generics.params, own_args) { + match (¶m.kind, arg.unpack()) { + (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} + _ => return false, + } + } + + true + } + + /// With `cfg(debug_assertions)`, assert that args are compatible with their generics, + /// and print out the args if not. + pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) { + if cfg!(debug_assertions) { + if !self.check_args_compatible(def_id, args) { + if let DefKind::AssocTy = self.def_kind(def_id) + && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) + { + bug!( + "args not compatible with generics for {}: args={:#?}, generics={:#?}", + self.def_path_str(def_id), + args, + // Make `[Self, GAT_ARGS...]` (this could be simplified) + self.mk_args_from_iter( + [self.types.self_param.into()].into_iter().chain( + self.generics_of(def_id) + .own_args(ty::GenericArgs::identity_for_item(self, def_id)) + .iter() + .copied() + ) + ) + ); + } else { + bug!( + "args not compatible with generics for {}: args={:#?}, generics={:#?}", + self.def_path_str(def_id), + args, + ty::GenericArgs::identity_for_item(self, def_id) + ); + } + } } - self.mk_args_from_iter(args) + } + + #[inline(always)] + pub(crate) fn check_and_mk_args( + self, + def_id: DefId, + args: impl IntoIterator>>, + ) -> GenericArgsRef<'tcx> { + let args = self.mk_args_from_iter(args.into_iter().map(Into::into)); + self.debug_assert_args_compatible(def_id, args); + args } #[inline] @@ -2003,11 +2236,11 @@ impl<'tcx> TyCtxt<'tcx> { self.intern_poly_existential_predicates(eps) } - pub fn mk_clauses(self, clauses: &[Clause<'tcx>]) -> &'tcx List> { + pub fn mk_clauses(self, clauses: &[Clause<'tcx>]) -> Clauses<'tcx> { // FIXME consider asking the input slice to be sorted to avoid // re-interning permutations, in which case that would be asserted // here. - self.intern_clauses(clauses) + self.interners.intern_clauses(clauses) } pub fn mk_local_def_ids(self, clauses: &[LocalDefId]) -> &'tcx List { @@ -2025,6 +2258,17 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_local_def_ids(xs)) } + pub fn mk_captures_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply< + &'tcx ty::CapturedPlace<'tcx>, + &'tcx List<&'tcx ty::CapturedPlace<'tcx>>, + >, + { + T::collect_and_apply(iter, |xs| self.intern_captures(xs)) + } + pub fn mk_const_list_from_iter(self, iter: I) -> T::Output where I: Iterator, @@ -2071,7 +2315,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn mk_clauses_from_iter(self, iter: I) -> T::Output where I: Iterator, - T: CollectAndApply, &'tcx List>>, + T: CollectAndApply, Clauses<'tcx>>, { T::collect_and_apply(iter, |xs| self.mk_clauses(xs)) } @@ -2174,6 +2418,45 @@ impl<'tcx> TyCtxt<'tcx> { lint_level(self.sess, lint, level, src, Some(span.into()), msg, decorate); } + /// Find the crate root and the appropriate span where `use` and outer attributes can be + /// inserted at. + pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option { + for (_hir_id, node) in self.hir().parent_iter(hir_id) { + if let hir::Node::Crate(m) = node { + return Some(m.spans.inject_use_span.shrink_to_lo()); + } + } + None + } + + pub fn disabled_nightly_features( + self, + diag: &mut Diag<'_, E>, + hir_id: Option, + features: impl IntoIterator, + ) { + if !self.sess.is_nightly_build() { + return; + } + + let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id)); + for (desc, feature) in features { + // FIXME: make this string translatable + let msg = + format!("add `#![feature({feature})]` to the crate attributes to enable{desc}"); + if let Some(span) = span { + diag.span_suggestion_verbose( + span, + msg, + format!("#![feature({feature})]\n"), + Applicability::MachineApplicable, + ); + } else { + diag.help(msg); + } + } + } + /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically /// generated by `#[derive(LintDiagnostic)]`). #[track_caller] diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index 788ccd5dbbdc7..5e256dc8d26e2 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -85,6 +85,7 @@ where /// Allows access to the current `ImplicitCtxt` in a closure if one is available. #[inline] +#[track_caller] pub fn with_context_opt(f: F) -> R where F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R, @@ -147,9 +148,13 @@ where /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. /// The closure is passed None if there is no `ImplicitCtxt` available. #[inline] +#[track_caller] pub fn with_opt(f: F) -> R where F: for<'tcx> FnOnce(Option>) -> R, { - with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx))) + with_context_opt( + #[track_caller] + |opt_context| f(opt_context.map(|context| context.tcx)), + ) } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 05463b8554f19..8c3ee6955f5d2 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -5,13 +5,13 @@ use std::fmt::Write; use std::ops::ControlFlow; use crate::ty::{ - AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, PolyTraitPredicate, - Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitor, + self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, + PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg}; +use rustc_errors::{into_diag_arg_using_display, Applicability, Diag, DiagArgValue, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -19,10 +19,9 @@ use rustc_hir::{PredicateOrigin, WherePredicate}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; -impl<'tcx> IntoDiagArg for Ty<'tcx> { - fn into_diag_arg(self) -> DiagArgValue { - self.to_string().into_diag_arg() - } +into_diag_arg_using_display! { + Ty<'_>, + ty::Region<'_>, } impl<'tcx> Ty<'tcx> { @@ -91,7 +90,12 @@ pub trait IsSuggestable<'tcx>: Sized { /// inference variables to be suggestable. fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool; - fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option; + fn make_suggestable( + self, + tcx: TyCtxt<'tcx>, + infer_suggestable: bool, + placeholder: Option>, + ) -> Option; } impl<'tcx, T> IsSuggestable<'tcx> for T @@ -103,8 +107,13 @@ where self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue() } - fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option { - self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok() + fn make_suggestable( + self, + tcx: TyCtxt<'tcx>, + infer_suggestable: bool, + placeholder: Option>, + ) -> Option { + self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable, placeholder }).ok() } } @@ -270,24 +279,29 @@ pub fn suggest_constraining_type_params<'a>( constraint.sort(); constraint.dedup(); let constraint = constraint.join(" + "); - let mut suggest_restrict = |span, bound_list_non_empty| { - suggestions.push(( - span, - if span_to_replace.is_some() { - constraint.clone() - } else if constraint.starts_with('<') { - constraint.to_string() - } else if bound_list_non_empty { - format!(" + {constraint}") - } else { - format!(" {constraint}") - }, - SuggestChangingConstraintsMessage::RestrictBoundFurther, - )) + let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| { + let suggestion = if span_to_replace.is_some() { + constraint.clone() + } else if constraint.starts_with('<') { + constraint.to_string() + } else if bound_list_non_empty { + format!(" + {constraint}") + } else { + format!(" {constraint}") + }; + + use SuggestChangingConstraintsMessage::RestrictBoundFurther; + + if let Some(open_paren_sp) = open_paren_sp { + suggestions.push((open_paren_sp, "(".to_string(), RestrictBoundFurther)); + suggestions.push((span, format!("){suggestion}"), RestrictBoundFurther)); + } else { + suggestions.push((span, suggestion, RestrictBoundFurther)); + } }; if let Some(span) = span_to_replace { - suggest_restrict(span, true); + suggest_restrict(span, true, None); continue; } @@ -318,8 +332,8 @@ pub fn suggest_constraining_type_params<'a>( // -- // | // replace with: `T: Bar +` - if let Some(span) = generics.bounds_span_for_suggestions(param.def_id) { - suggest_restrict(span, true); + if let Some((span, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) { + suggest_restrict(span, true, open_paren_sp); continue; } @@ -559,6 +573,7 @@ impl<'tcx> TypeVisitor> for IsSuggestableVisitor<'tcx> { pub struct MakeSuggestableFolder<'tcx> { tcx: TyCtxt<'tcx>, infer_suggestable: bool, + placeholder: Option>, } impl<'tcx> FallibleTypeFolder> for MakeSuggestableFolder<'tcx> { @@ -572,19 +587,24 @@ impl<'tcx> FallibleTypeFolder> for MakeSuggestableFolder<'tcx> { let t = match *t.kind() { Infer(InferTy::TyVar(_)) if self.infer_suggestable => t, - FnDef(def_id, args) => { + FnDef(def_id, args) if self.placeholder.is_none() => { Ty::new_fn_ptr(self.tcx, self.tcx.fn_sig(def_id).instantiate(self.tcx, args)) } - // FIXME(compiler-errors): We could replace these with infer, I guess. Closure(..) + | FnDef(..) | Infer(..) | Coroutine(..) | CoroutineWitness(..) | Bound(_, _) | Placeholder(_) | Error(_) => { - return Err(()); + if let Some(placeholder) = self.placeholder { + // We replace these with infer (which is passed in from an infcx). + placeholder + } else { + return Err(()); + } } Alias(Opaque, AliasTy { def_id, .. }) => { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index e15f03788464e..71437ce1df9de 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -4,6 +4,7 @@ use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::def_id::DefId; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_span::symbol::Symbol; use rustc_target::spec::abi; use std::borrow::Cow; @@ -32,7 +33,7 @@ impl ExpectedFound { pub enum TypeError<'tcx> { Mismatch, ConstnessMismatch(ExpectedFound), - PolarityMismatch(ExpectedFound), + PolarityMismatch(ExpectedFound), UnsafetyMismatch(ExpectedFound), AbiMismatch(ExpectedFound), Mutability, @@ -285,8 +286,9 @@ impl<'tcx> Ty<'tcx> { ty::Adt(def, _) => def.descr().into(), ty::Foreign(_) => "extern type".into(), ty::Array(..) => "array".into(), + ty::Pat(..) => "pattern type".into(), ty::Slice(_) => "slice".into(), - ty::RawPtr(_) => "raw pointer".into(), + ty::RawPtr(_, _) => "raw pointer".into(), ty::Ref(.., mutbl) => match mutbl { hir::Mutability::Mut => "mutable reference", _ => "reference", diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index adc153c4dfd2d..15f048ab598c9 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -2,6 +2,7 @@ use crate::mir::Mutability; use crate::ty::GenericArgKind; use crate::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_hir::def_id::DefId; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use std::fmt::Debug; use std::hash::Hash; use std::iter; @@ -58,30 +59,6 @@ pub enum TreatParams { /// This also treats projections with inference variables as infer vars /// since they could be further normalized. ForLookup, - /// Treat parameters as placeholders in the given environment. This is the - /// correct mode for *lookup*, as during candidate selection. - /// - /// N.B. during deep rejection, this acts identically to `ForLookup`. - /// - /// FIXME(-Znext-solver): Remove this variant and cleanup - /// the code. - NextSolverLookup, -} - -/// During fast-rejection, we have the choice of treating projection types -/// as either simplifiable or not, depending on whether we expect the projection -/// to be normalized/rigid. -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub enum TreatProjections { - /// In the old solver we don't try to normalize projections - /// when looking up impls and only access them by using the - /// current self type. This means that if the self type is - /// a projection which could later be normalized, we must not - /// treat it as rigid. - ForLookup, - /// We can treat projections in the self type as opaque as - /// we separately look up impls for the normalized self type. - NextSolverLookup, } /// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists. @@ -120,7 +97,8 @@ pub fn simplify_type<'tcx>( ty::Str => Some(SimplifiedType::Str), ty::Array(..) => Some(SimplifiedType::Array), ty::Slice(..) => Some(SimplifiedType::Slice), - ty::RawPtr(ptr) => Some(SimplifiedType::Ptr(ptr.mutbl)), + ty::Pat(ty, ..) => simplify_type(tcx, ty, treat_params), + ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => { Some(SimplifiedType::Trait(principal_def_id)) @@ -138,21 +116,17 @@ pub fn simplify_type<'tcx>( ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())), ty::Placeholder(..) => Some(SimplifiedType::Placeholder), ty::Param(_) => match treat_params { - TreatParams::ForLookup | TreatParams::NextSolverLookup => { - Some(SimplifiedType::Placeholder) - } + TreatParams::ForLookup => Some(SimplifiedType::Placeholder), TreatParams::AsCandidateKey => None, }, ty::Alias(..) => match treat_params { // When treating `ty::Param` as a placeholder, projections also // don't unify with anything else as long as they are fully normalized. - // - // We will have to be careful with lazy normalization here. - // FIXME(lazy_normalization): This is probably not right... + // FIXME(-Znext-solver): Can remove this `if` and always simplify to `Placeholder` + // when the new solver is enabled by default. TreatParams::ForLookup if !ty.has_non_region_infer() => { Some(SimplifiedType::Placeholder) } - TreatParams::NextSolverLookup => Some(SimplifiedType::Placeholder), TreatParams::ForLookup | TreatParams::AsCandidateKey => None, }, ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)), @@ -231,6 +205,7 @@ impl DeepRejectCtxt { | ty::Slice(..) | ty::RawPtr(..) | ty::Dynamic(..) + | ty::Pat(..) | ty::Ref(..) | ty::Never | ty::Tuple(..) @@ -269,6 +244,10 @@ impl DeepRejectCtxt { } _ => false, }, + ty::Pat(obl_ty, _) => { + // FIXME(pattern_types): take pattern into account + matches!(k, &ty::Pat(impl_ty, _) if self.types_may_unify(obl_ty, impl_ty)) + } ty::Slice(obl_ty) => { matches!(k, &ty::Slice(impl_ty) if self.types_may_unify(obl_ty, impl_ty)) } @@ -286,8 +265,10 @@ impl DeepRejectCtxt { } _ => false, }, - ty::RawPtr(obl) => match k { - ty::RawPtr(imp) => obl.mutbl == imp.mutbl && self.types_may_unify(obl.ty, imp.ty), + ty::RawPtr(obl_ty, obl_mutbl) => match *k { + ty::RawPtr(imp_ty, imp_mutbl) => { + obl_mutbl == imp_mutbl && self.types_may_unify(obl_ty, imp_ty) + } _ => false, }, ty::Dynamic(obl_preds, ..) => { @@ -323,7 +304,7 @@ impl DeepRejectCtxt { // Depending on the value of `treat_obligation_params`, we either // treat generic parameters like placeholders or like inference variables. ty::Param(_) => match self.treat_obligation_params { - TreatParams::ForLookup | TreatParams::NextSolverLookup => false, + TreatParams::ForLookup => false, TreatParams::AsCandidateKey => true, }, @@ -365,7 +346,7 @@ impl DeepRejectCtxt { let k = impl_ct.kind(); match obligation_ct.kind() { ty::ConstKind::Param(_) => match self.treat_obligation_params { - TreatParams::ForLookup | TreatParams::NextSolverLookup => false, + TreatParams::ForLookup => false, TreatParams::AsCandidateKey => true, }, diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 18cf5445e5621..0dc835671d56b 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -35,6 +35,15 @@ impl FlagComputation { result } + pub fn for_clauses(clauses: &[ty::Clause<'_>]) -> FlagComputation { + let mut result = FlagComputation::new(); + for c in clauses { + result.add_flags(c.as_predicate().flags()); + result.add_exclusive_binder(c.as_predicate().outer_exclusive_binder()); + } + result + } + fn add_flags(&mut self, flags: TypeFlags) { self.flags = self.flags | flags; } @@ -209,10 +218,24 @@ impl FlagComputation { self.add_const(len); } + &ty::Pat(ty, pat) => { + self.add_ty(ty); + match *pat { + ty::PatternKind::Range { start, end, include_end: _ } => { + if let Some(start) = start { + self.add_const(start) + } + if let Some(end) = end { + self.add_const(end) + } + } + } + } + &ty::Slice(tt) => self.add_ty(tt), - ty::RawPtr(m) => { - self.add_ty(m.ty); + &ty::RawPtr(ty, _) => { + self.add_ty(ty); } &ty::Ref(r, ty, _) => { diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index b57d4f372a7f9..fd3bee16e2685 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -2,8 +2,6 @@ use crate::ty::{self, Binder, BoundTy, Ty, TyCtxt, TypeVisitableExt}; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; -use std::collections::BTreeMap; - pub use rustc_type_ir::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; /////////////////////////////////////////////////////////////////////////// @@ -254,12 +252,12 @@ impl<'tcx> TyCtxt<'tcx> { self, value: Binder<'tcx, T>, mut fld_r: F, - ) -> (T, BTreeMap>) + ) -> (T, FxIndexMap>) where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>, T: TypeFoldable>, { - let mut region_map = BTreeMap::new(); + let mut region_map = FxIndexMap::default(); let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br)); let value = self.instantiate_bound_regions_uncached(value, real_fld_r); (value, region_map) diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 02b58c035d4ed..de2c01c304612 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -11,13 +11,14 @@ use rustc_ast_ir::walk_visitable_list; use rustc_data_structures::intern::Interned; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir::def_id::DefId; -use rustc_macros::HashStable; +use rustc_macros::{ + Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, +}; use rustc_serialize::{Decodable, Encodable}; use rustc_type_ir::WithCachedTypeInfo; use smallvec::SmallVec; use core::intrinsics; -use std::cmp::Ordering; use std::marker::PhantomData; use std::mem; use std::num::NonZero; @@ -68,7 +69,7 @@ const TYPE_TAG: usize = 0b00; const REGION_TAG: usize = 0b01; const CONST_TAG: usize = 0b10; -#[derive(Debug, TyEncodable, TyDecodable, PartialEq, Eq, PartialOrd, Ord, HashStable)] +#[derive(Debug, TyEncodable, TyDecodable, PartialEq, Eq, HashStable)] pub enum GenericArgKind<'tcx> { Lifetime(ty::Region<'tcx>), Type(Ty<'tcx>), @@ -100,18 +101,6 @@ impl<'tcx> GenericArgKind<'tcx> { } } -impl<'tcx> Ord for GenericArg<'tcx> { - fn cmp(&self, other: &GenericArg<'tcx>) -> Ordering { - self.unpack().cmp(&other.unpack()) - } -} - -impl<'tcx> PartialOrd for GenericArg<'tcx> { - fn partial_cmp(&self, other: &GenericArg<'tcx>) -> Option { - Some(self.cmp(other)) - } -} - impl<'tcx> From> for GenericArg<'tcx> { #[inline] fn from(r: ty::Region<'tcx>) -> GenericArg<'tcx> { diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 2630b96869bae..6bf2051d67ccd 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -3,6 +3,7 @@ use crate::ty::{EarlyBinder, GenericArgsRef}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; @@ -263,7 +264,7 @@ impl<'tcx> Generics { /// Returns the `GenericParamDef` associated with this `EarlyParamRegion`. pub fn region_param( &'tcx self, - param: &ty::EarlyParamRegion, + param: ty::EarlyParamRegion, tcx: TyCtxt<'tcx>, ) -> &'tcx GenericParamDef { let param = self.param_at(param.index as usize, tcx); @@ -274,7 +275,7 @@ impl<'tcx> Generics { } /// Returns the `GenericParamDef` associated with this `ParamTy`. - pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { + pub fn type_param(&'tcx self, param: ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { let param = self.param_at(param.index as usize, tcx); match param.kind { GenericParamDefKind::Type { .. } => param, @@ -286,7 +287,7 @@ impl<'tcx> Generics { /// `Generics`. pub fn opt_type_param( &'tcx self, - param: &ParamTy, + param: ParamTy, tcx: TyCtxt<'tcx>, ) -> Option<&'tcx GenericParamDef> { let param = self.opt_param_at(param.index as usize, tcx)?; @@ -297,7 +298,7 @@ impl<'tcx> Generics { } /// Returns the `GenericParamDef` associated with this `ParamConst`. - pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { + pub fn const_param(&'tcx self, param: ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { let param = self.param_at(param.index as usize, tcx); match param.kind { GenericParamDefKind::Const { .. } => param, @@ -316,11 +317,11 @@ impl<'tcx> Generics { /// of this item, excluding `Self`. /// /// **This should only be used for diagnostics purposes.** - pub fn own_args_no_defaults( + pub fn own_args_no_defaults<'a>( &'tcx self, tcx: TyCtxt<'tcx>, - args: &'tcx [ty::GenericArg<'tcx>], - ) -> &'tcx [ty::GenericArg<'tcx>] { + args: &'a [ty::GenericArg<'tcx>], + ) -> &'a [ty::GenericArg<'tcx>] { let mut own_params = self.parent_count..self.count(); if self.has_self && self.parent.is_none() { own_params.start = 1; diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs index 129d947697e89..ef7a7a99ff7e9 100644 --- a/compiler/rustc_middle/src/ty/impls_ty.rs +++ b/compiler/rustc_middle/src/ty/impls_ty.rs @@ -11,19 +11,20 @@ use rustc_data_structures::stable_hasher::HashingControls; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_query_system::ich::StableHashingContext; use std::cell::RefCell; +use std::ptr; -impl<'a, 'tcx, T> HashStable> for &'tcx ty::List +impl<'a, 'tcx, H, T> HashStable> for &'tcx ty::list::RawList where T: HashStable>, { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { thread_local! { - static CACHE: RefCell> = + static CACHE: RefCell> = RefCell::new(Default::default()); } let hash = CACHE.with(|cache| { - let key = (self.as_ptr() as usize, self.len(), hcx.hashing_controls()); + let key = (ptr::from_ref(*self).cast::<()>(), hcx.hashing_controls()); if let Some(&hash) = cache.borrow().get(&key) { return hash; } @@ -40,7 +41,7 @@ where } } -impl<'a, 'tcx, T> ToStableHashKey> for &'tcx ty::List +impl<'a, 'tcx, H, T> ToStableHashKey> for &'tcx ty::list::RawList where T: HashStable>, { diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index c571ac507248d..e3eea83ba49ad 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -1,3 +1,4 @@ +use rustc_macros::HashStable; use smallvec::SmallVec; use crate::ty::context::TyCtxt; diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index bbcc244cb26de..da5d57db5bef9 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -61,7 +61,7 @@ pub(crate) fn provide(providers: &mut Providers) { /// requires calling [`InhabitedPredicate::instantiate`] fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> { if let Some(def_id) = def_id.as_local() { - if matches!(tcx.representability(def_id), ty::Representability::Infinite) { + if matches!(tcx.representability(def_id), ty::Representability::Infinite(_)) { return InhabitedPredicate::True; } } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 814c3629b08fa..a08fde976bc1a 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -8,8 +8,11 @@ use rustc_hir::def::Namespace; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::FiniteBitSet; -use rustc_macros::HashStable; +use rustc_macros::{ + Decodable, Encodable, HashStable, Lift, TyDecodable, TyEncodable, TypeVisitable, +}; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Symbol; use std::assert_matches::assert_matches; @@ -30,6 +33,28 @@ pub struct Instance<'tcx> { pub args: GenericArgsRef<'tcx>, } +/// Describes why a `ReifyShim` was created. This is needed to distingish a ReifyShim created to +/// adjust for things like `#[track_caller]` in a vtable from a `ReifyShim` created to produce a +/// function pointer from a vtable entry. +/// Currently, this is only used when KCFI is enabled, as only KCFI needs to treat those two +/// `ReifyShim`s differently. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(TyEncodable, TyDecodable, HashStable)] +pub enum ReifyReason { + /// The `ReifyShim` was created to produce a function pointer. This happens when: + /// * A vtable entry is directly converted to a function call (e.g. creating a fn ptr from a + /// method on a `dyn` object). + /// * A function with `#[track_caller]` is converted to a function pointer + /// * If KCFI is enabled, creating a function pointer from a method on an object-safe trait. + /// This includes the case of converting `::call`-like methods on closure-likes to function + /// pointers. + FnPtr, + /// This `ReifyShim` was created to populate a vtable. Currently, this happens when a + /// `#[track_caller]` mismatch occurs between the implementation of a method and the method. + /// This includes the case of `::call`-like methods in closure-likes' vtables. + Vtable, +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)] pub enum InstanceDef<'tcx> { @@ -66,7 +91,13 @@ pub enum InstanceDef<'tcx> { /// Because this is a required part of the function's ABI but can't be tracked /// as a property of the function pointer, we use a single "caller location" /// (the definition of the function itself). - ReifyShim(DefId), + /// + /// The second field encodes *why* this shim was created. This allows distinguishing between + /// a `ReifyShim` that appears in a vtable vs one that appears as a function pointer. + /// + /// This field will only be populated if we are compiling in a mode that needs these shims + /// to be separable, currently only when KCFI is enabled. + ReifyShim(DefId, Option), /// `::call_*` (generated `FnTrait` implementation for `fn()` pointers). /// @@ -90,15 +121,19 @@ pub enum InstanceDef<'tcx> { /// and dispatch to the `FnMut::call_mut` instance for the closure. ClosureOnceShim { call_once: DefId, track_caller: bool }, - /// `<[FnMut/Fn coroutine-closure] as FnOnce>::call_once` or - /// `<[Fn coroutine-closure] as FnMut>::call_mut`. + /// `<[FnMut/Fn coroutine-closure] as FnOnce>::call_once` /// /// The body generated here differs significantly from the `ClosureOnceShim`, /// since we need to generate a distinct coroutine type that will move the /// closure's upvars *out* of the closure. ConstructCoroutineInClosureShim { coroutine_closure_def_id: DefId, - target_kind: ty::ClosureKind, + // Whether the generated MIR body takes the coroutine by-ref. This is + // because the signature of `<{async fn} as FnMut>::call_mut` is: + // `fn(&mut self, args: A) -> ::Output`, that is to say + // that it returns the `FnOnce`-flavored coroutine but takes the closure + // by mut ref (and similarly for `Fn::call`). + receiver_by_ref: bool, }, /// `<[coroutine] as Future>::poll`, but for coroutines produced when `AsyncFnOnce` @@ -107,7 +142,7 @@ pub enum InstanceDef<'tcx> { /// /// This will select the body that is produced by the `ByMoveBody` transform, and thus /// take and use all of its upvars by-move rather than by-ref. - CoroutineKindShim { coroutine_def_id: DefId, target_kind: ty::ClosureKind }, + CoroutineKindShim { coroutine_def_id: DefId }, /// Compiler-generated accessor for thread locals which returns a reference to the thread local /// the `DefId` defines. This is used to export thread locals from dylibs on platforms lacking @@ -135,6 +170,12 @@ pub enum InstanceDef<'tcx> { /// /// The `DefId` is for `FnPtr::addr`, the `Ty` is the type `T`. FnPtrAddrShim(DefId, Ty<'tcx>), + + /// `core::future::async_drop::async_drop_in_place::<'_, T>`. + /// + /// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty` + /// is the type `T`. + AsyncDropGlueCtorShim(DefId, Option>), } impl<'tcx> Instance<'tcx> { @@ -168,11 +209,18 @@ impl<'tcx> Instance<'tcx> { // If this a non-generic instance, it cannot be a shared monomorphization. self.args.non_erasable_generics(tcx, self.def_id()).next()?; + // compiler_builtins cannot use upstream monomorphizations. + if tcx.is_compiler_builtins(LOCAL_CRATE) { + return None; + } + match self.def { InstanceDef::Item(def) => tcx .upstream_monomorphizations_for(def) .and_then(|monos| monos.get(&self.args).cloned()), - InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args), + InstanceDef::DropGlue(_, Some(_)) | InstanceDef::AsyncDropGlueCtorShim(_, _) => { + tcx.upstream_drop_glue_for(self.args) + } _ => None, } } @@ -184,7 +232,7 @@ impl<'tcx> InstanceDef<'tcx> { match self { InstanceDef::Item(def_id) | InstanceDef::VTableShim(def_id) - | InstanceDef::ReifyShim(def_id) + | InstanceDef::ReifyShim(def_id, _) | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) @@ -192,12 +240,13 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ } | ty::InstanceDef::ConstructCoroutineInClosureShim { coroutine_closure_def_id: def_id, - target_kind: _, + receiver_by_ref: _, } - | ty::InstanceDef::CoroutineKindShim { coroutine_def_id: def_id, target_kind: _ } + | ty::InstanceDef::CoroutineKindShim { coroutine_def_id: def_id } | InstanceDef::DropGlue(def_id, _) | InstanceDef::CloneShim(def_id, _) - | InstanceDef::FnPtrAddrShim(def_id, _) => def_id, + | InstanceDef::FnPtrAddrShim(def_id, _) + | InstanceDef::AsyncDropGlueCtorShim(def_id, _) => def_id, } } @@ -205,9 +254,9 @@ impl<'tcx> InstanceDef<'tcx> { pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option { match self { ty::InstanceDef::Item(def) => Some(def), - ty::InstanceDef::DropGlue(def_id, Some(_)) | InstanceDef::ThreadLocalShim(def_id) => { - Some(def_id) - } + ty::InstanceDef::DropGlue(def_id, Some(_)) + | InstanceDef::AsyncDropGlueCtorShim(def_id, _) + | InstanceDef::ThreadLocalShim(def_id) => Some(def_id), InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) @@ -241,6 +290,7 @@ impl<'tcx> InstanceDef<'tcx> { let def_id = match *self { ty::InstanceDef::Item(def) => def, ty::InstanceDef::DropGlue(_, Some(_)) => return false, + ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => return false, ty::InstanceDef::ThreadLocalShim(_) => return false, _ => return true, }; @@ -309,11 +359,13 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::ThreadLocalShim(..) | InstanceDef::FnPtrAddrShim(..) | InstanceDef::FnPtrShim(..) - | InstanceDef::DropGlue(_, Some(_)) => false, + | InstanceDef::DropGlue(_, Some(_)) + | InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => false, InstanceDef::ClosureOnceShim { .. } | InstanceDef::ConstructCoroutineInClosureShim { .. } | InstanceDef::CoroutineKindShim { .. } | InstanceDef::DropGlue(..) + | InstanceDef::AsyncDropGlueCtorShim(..) | InstanceDef::Item(_) | InstanceDef::Intrinsic(..) | InstanceDef::ReifyShim(..) @@ -325,7 +377,7 @@ impl<'tcx> InstanceDef<'tcx> { fn fmt_instance( f: &mut fmt::Formatter<'_>, - instance: &Instance<'_>, + instance: Instance<'_>, type_length: Option, ) -> fmt::Result { ty::tls::with(|tcx| { @@ -344,7 +396,9 @@ fn fmt_instance( match instance.def { InstanceDef::Item(_) => Ok(()), InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"), - InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), + InstanceDef::ReifyShim(_, None) => write!(f, " - shim(reify)"), + InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => write!(f, " - shim(reify-fnptr)"), + InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => write!(f, " - shim(reify-vtable)"), InstanceDef::ThreadLocalShim(_) => write!(f, " - shim(tls)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{num}"), @@ -356,12 +410,14 @@ fn fmt_instance( InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"), InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"), + InstanceDef::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"), + InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), } } -pub struct ShortInstance<'a, 'tcx>(pub &'a Instance<'tcx>, pub usize); +pub struct ShortInstance<'tcx>(pub Instance<'tcx>, pub usize); -impl<'a, 'tcx> fmt::Display for ShortInstance<'a, 'tcx> { +impl<'tcx> fmt::Display for ShortInstance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_instance(f, self.0, Some(rustc_session::Limit(self.1))) } @@ -369,7 +425,7 @@ impl<'a, 'tcx> fmt::Display for ShortInstance<'a, 'tcx> { impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_instance(f, self, None) + fmt_instance(f, *self, None) } } @@ -466,15 +522,37 @@ impl<'tcx> Instance<'tcx> { debug!("resolve(def_id={:?}, args={:?})", def_id, args); // Use either `resolve_closure` or `resolve_for_vtable` assert!(!tcx.is_closure_like(def_id), "Called `resolve_for_fn_ptr` on closure: {def_id:?}"); + let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::FnPtr); Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { InstanceDef::Item(def) if resolved.def.requires_caller_location(tcx) => { debug!(" => fn pointer created for function with #[track_caller]"); - resolved.def = InstanceDef::ReifyShim(def); + resolved.def = InstanceDef::ReifyShim(def, reason); } InstanceDef::Virtual(def_id, _) => { debug!(" => fn pointer created for virtual call"); - resolved.def = InstanceDef::ReifyShim(def_id); + resolved.def = InstanceDef::ReifyShim(def_id, reason); + } + // Reify `Trait::method` implementations if KCFI is enabled + // FIXME(maurer) only reify it if it is a vtable-safe function + _ if tcx.sess.is_sanitizer_kcfi_enabled() + && tcx + .opt_associated_item(def_id) + .and_then(|assoc| assoc.trait_item_def_id) + .is_some() => + { + // If this function could also go in a vtable, we need to `ReifyShim` it with + // KCFI because it can only attach one type per function. + resolved.def = InstanceDef::ReifyShim(resolved.def_id(), reason) + } + // Reify `::call`-like method implementations if KCFI is enabled + _ if tcx.sess.is_sanitizer_kcfi_enabled() + && tcx.is_closure_like(resolved.def_id()) => + { + // Reroute through a reify via the *unresolved* instance. The resolved one can't + // be directly reified because it's closure-like. The reify can handle the + // unresolved instance. + resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args } } _ => {} } @@ -498,6 +576,7 @@ impl<'tcx> Instance<'tcx> { debug!(" => associated item with unsizeable self: Self"); Some(Instance { def: InstanceDef::VTableShim(def_id), args }) } else { + let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable); Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { InstanceDef::Item(def) => { @@ -534,18 +613,18 @@ impl<'tcx> Instance<'tcx> { // Create a shim for the `FnOnce/FnMut/Fn` method we are calling // - unlike functions, invoking a closure always goes through a // trait. - resolved = Instance { def: InstanceDef::ReifyShim(def_id), args }; + resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args }; } else { debug!( " => vtable fn pointer created for function with #[track_caller]: {:?}", def ); - resolved.def = InstanceDef::ReifyShim(def); + resolved.def = InstanceDef::ReifyShim(def, reason); } } } InstanceDef::Virtual(def_id, _) => { debug!(" => vtable fn pointer created for virtual call"); - resolved.def = InstanceDef::ReifyShim(def_id); + resolved.def = InstanceDef::ReifyShim(def_id, reason) } _ => {} } @@ -575,6 +654,12 @@ impl<'tcx> Instance<'tcx> { Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args) } + pub fn resolve_async_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { + let def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, None); + let args = tcx.mk_args(&[ty.into()]); + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args) + } + #[instrument(level = "debug", skip(tcx), ret)] pub fn fn_once_adapter_instance( tcx: TyCtxt<'tcx>, @@ -651,10 +736,7 @@ impl<'tcx> Instance<'tcx> { Some(Instance { def: ty::InstanceDef::Item(coroutine_def_id), args }) } else { Some(Instance { - def: ty::InstanceDef::CoroutineKindShim { - coroutine_def_id, - target_kind: args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap(), - }, + def: ty::InstanceDef::CoroutineKindShim { coroutine_def_id }, args, }) } diff --git a/compiler/rustc_middle/src/ty/intrinsic.rs b/compiler/rustc_middle/src/ty/intrinsic.rs index 18d08ed23a5e5..68c1d8c17ec11 100644 --- a/compiler/rustc_middle/src/ty/intrinsic.rs +++ b/compiler/rustc_middle/src/ty/intrinsic.rs @@ -1,3 +1,4 @@ +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{def_id::DefId, Symbol}; use super::TyCtxt; diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 11fd73c909417..f8490e5e15fc0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2,7 +2,7 @@ use crate::error::UnsupportedFnAbi; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; use crate::ty::normalize_erasing_regions::NormalizationError; -use crate::ty::{self, ConstKind, Ty, TyCtxt, TypeVisitableExt}; +use crate::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_error_messages::DiagMessage; use rustc_errors::{ Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, @@ -10,12 +10,15 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; +use rustc_macros::{extension, HashStable, TyDecodable, TyEncodable}; use rustc_session::config::OptLevel; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_target::abi::call::FnAbi; use rustc_target::abi::*; -use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target}; +use rustc_target::spec::{ + abi::Abi as SpecAbi, HasTargetSpec, HasWasmCAbiOpt, PanicStrategy, Target, WasmCAbi, +}; use std::borrow::Cow; use std::cmp; @@ -122,7 +125,7 @@ impl Primitive { F64 => tcx.types.f64, F128 => tcx.types.f128, // FIXME(erikdesjardins): handle non-default addrspace ptr sizes - Pointer(_) => Ty::new_mut_ptr(tcx, Ty::new_unit(tcx)), + Pointer(_) => Ty::new_mut_ptr(tcx, tcx.types.unit), } } @@ -328,7 +331,7 @@ impl<'tcx> SizeSkeleton<'tcx> { }; match *ty.kind() { - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { let non_zero = !ty.is_unsafe_ptr(); let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); match tail.kind() { @@ -336,6 +339,10 @@ impl<'tcx> SizeSkeleton<'tcx> { debug_assert!(tail.has_non_region_param()); Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) }) } + ty::Error(guar) => { + // Fixes ICE #124031 + return Err(tcx.arena.alloc(LayoutError::ReferencesError(*guar))); + } _ => bug!( "SizeSkeleton::compute({ty}): layout errored ({err:?}), yet \ tail `{tail}` is not a type parameter or a projection", @@ -345,32 +352,26 @@ impl<'tcx> SizeSkeleton<'tcx> { ty::Array(inner, len) if len.ty() == tcx.types.usize && tcx.features().transmute_generic_consts => { + let len_eval = len.try_eval_target_usize(tcx, param_env); + if len_eval == Some(0) { + return Ok(SizeSkeleton::Known(Size::from_bytes(0))); + } + match SizeSkeleton::compute(inner, tcx, param_env)? { // This may succeed because the multiplication of two types may overflow // but a single size of a nested array will not. SizeSkeleton::Known(s) => { - if let Some(c) = len.try_eval_target_usize(tcx, param_env) { + if let Some(c) = len_eval { let size = s .bytes() .checked_mul(c) .ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?; return Ok(SizeSkeleton::Known(Size::from_bytes(size))); } - let len = tcx.expand_abstract_consts(len); - let prev = ty::Const::from_target_usize(tcx, s.bytes()); - let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, prev) else { - return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty))); - }; - Ok(SizeSkeleton::Generic(gen_size)) + Err(tcx.arena.alloc(LayoutError::Unknown(ty))) } SizeSkeleton::Pointer { .. } => Err(err), - SizeSkeleton::Generic(g) => { - let len = tcx.expand_abstract_consts(len); - let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, g) else { - return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty))); - }; - Ok(SizeSkeleton::Generic(gen_size)) - } + SizeSkeleton::Generic(_) => Err(tcx.arena.alloc(LayoutError::Unknown(ty))), } } @@ -468,56 +469,6 @@ impl<'tcx> SizeSkeleton<'tcx> { } } -/// When creating the layout for types with abstract consts in their size (i.e. [usize; 4 * N]), -/// to ensure that they have a canonical order and can be compared directly we combine all -/// constants, and sort the other terms. This allows comparison of expressions of sizes, -/// allowing for things like transmuting between types that depend on generic consts. -/// This returns `None` if multiplication of constants overflows. -fn mul_sorted_consts<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, -) -> Option> { - use crate::mir::BinOp::Mul; - - let mut work = vec![a, b]; - let mut done = vec![]; - while let Some(n) = work.pop() { - if let ConstKind::Expr(ty::Expr::Binop(Mul, l, r)) = n.kind() { - work.push(l); - work.push(r) - } else { - done.push(n); - } - } - let mut k = 1; - let mut overflow = false; - done.retain(|c| { - let Some(c) = c.try_eval_target_usize(tcx, param_env) else { - return true; - }; - let Some(next) = c.checked_mul(k) else { - overflow = true; - return false; - }; - k = next; - false - }); - if overflow { - return None; - } - if k != 1 { - done.push(ty::Const::from_target_usize(tcx, k)); - } else if k == 0 { - return Some(ty::Const::from_target_usize(tcx, 0)); - } - done.sort_unstable(); - - // create a single tree from the buffer - done.into_iter().reduce(|acc, n| ty::Const::new_expr(tcx, ty::Expr::Binop(Mul, n, acc), n.ty())) -} - pub trait HasTyCtxt<'tcx>: HasDataLayout { fn tcx(&self) -> TyCtxt<'tcx>; } @@ -539,6 +490,12 @@ impl<'tcx> HasTargetSpec for TyCtxt<'tcx> { } } +impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> { + fn wasm_c_abi_opt(&self) -> WasmCAbi { + self.sess.opts.unstable_opts.wasm_c_abi + } +} + impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { #[inline] fn tcx(&self) -> TyCtxt<'tcx> { @@ -584,6 +541,12 @@ impl<'tcx, T: HasTargetSpec> HasTargetSpec for LayoutCx<'tcx, T> { } } +impl<'tcx, T: HasWasmCAbiOpt> HasWasmCAbiOpt for LayoutCx<'tcx, T> { + fn wasm_c_abi_opt(&self) -> WasmCAbi { + self.tcx.wasm_c_abi_opt() + } +} + impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx.tcx() @@ -798,12 +761,13 @@ where | ty::FnDef(..) | ty::CoroutineWitness(..) | ty::Foreign(..) + | ty::Pat(_, _) | ty::Dynamic(_, _, ty::Dyn) => { bug!("TyAndLayout::field({:?}): not applicable", this) } // Potentially-fat pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { assert!(i < this.fields.count()); // Reuse the fat `*T` type as its own thin pointer data field. @@ -811,7 +775,7 @@ where // (which may have no non-DST form), and will work as long // as the `Abi` or `FieldsShape` is checked by users. if i == 0 { - let nil = Ty::new_unit(tcx); + let nil = tcx.types.unit; let unit_ptr_ty = if this.ty.is_unsafe_ptr() { Ty::new_mut_ptr(tcx, nil) } else { @@ -981,8 +945,8 @@ where let param_env = cx.param_env(); let pointee_info = match *this.ty.kind() { - ty::RawPtr(mt) if offset.bytes() == 0 => { - tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo { + ty::RawPtr(p_ty, _) if offset.bytes() == 0 => { + tcx.layout_of(param_env.and(p_ty)).ok().map(|layout| PointeeInfo { size: layout.size, align: layout.align.abi, safe: None, diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 336c2dce1141c..0db541899d20e 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -1,7 +1,8 @@ +use super::flags::FlagComputation; +use super::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, TyCtxt, TypeFlags, WithInfcx}; use crate::arena::Arena; use rustc_data_structures::aligned::{align_of, Aligned}; use rustc_serialize::{Encodable, Encoder}; -use rustc_type_ir::{InferCtxtLike, WithInfcx}; use std::alloc::Layout; use std::cmp::Ordering; use std::fmt; @@ -12,6 +13,9 @@ use std::ops::Deref; use std::ptr; use std::slice; +#[cfg(parallel_compiler)] +use rustc_data_structures::sync::DynSync; + /// `List` is a bit like `&[T]`, but with some critical differences. /// - IMPORTANT: Every `List` is *required* to have unique contents. The /// type's correctness relies on this, *but it does not enforce it*. @@ -28,15 +32,33 @@ use std::slice; /// - `T` must be `Copy`. This lets `List` be stored in a dropless arena and /// iterators return a `T` rather than a `&T`. /// - `T` must not be zero-sized. +pub type List = RawList<(), T>; + +/// A generic type that can be used to prepend a [`List`] with some header. +/// +/// The header will be ignored for value-based operations like [`PartialEq`], +/// [`Hash`] and [`Encodable`]. #[repr(C)] -pub struct List { - len: usize, +pub struct RawList { + skel: ListSkeleton, + opaque: OpaqueListContents, +} +/// A [`RawList`] without the unsized tail. This type is used for layout computation +/// and constructing empty lists. +#[repr(C)] +struct ListSkeleton { + header: H, + len: usize, /// Although this claims to be a zero-length array, in practice `len` /// elements are actually present. data: [T; 0], +} - opaque: OpaqueListContents, +impl Default for &List { + fn default() -> Self { + List::empty() + } } extern "C" { @@ -45,35 +67,17 @@ extern "C" { type OpaqueListContents; } -impl List { - /// Returns a reference to the (unique, static) empty list. +impl RawList { #[inline(always)] - pub fn empty<'a>() -> &'a List { - #[repr(align(64))] - struct MaxAlign; - - assert!(mem::align_of::() <= mem::align_of::()); - - #[repr(C)] - struct InOrder(T, U); - - // The empty slice is static and contains a single `0` usize (for the - // length) that is 64-byte aligned, thus featuring the necessary - // trailing padding for elements with up to 64-byte alignment. - static EMPTY_SLICE: InOrder = InOrder(0, MaxAlign); - unsafe { &*(std::ptr::addr_of!(EMPTY_SLICE) as *const List) } - } - pub fn len(&self) -> usize { - self.len + self.skel.len } + #[inline(always)] pub fn as_slice(&self) -> &[T] { self } -} -impl List { /// Allocates a list from `arena` and copies the contents of `slice` into it. /// /// WARNING: the contents *must be unique*, such that no list with these @@ -84,20 +88,31 @@ impl List { /// (because the empty list exists statically, and is available via /// `empty()`). #[inline] - pub(super) fn from_arena<'tcx>(arena: &'tcx Arena<'tcx>, slice: &[T]) -> &'tcx List { + pub(super) fn from_arena<'tcx>( + arena: &'tcx Arena<'tcx>, + header: H, + slice: &[T], + ) -> &'tcx RawList + where + T: Copy, + { assert!(!mem::needs_drop::()); assert!(mem::size_of::() != 0); assert!(!slice.is_empty()); let (layout, _offset) = - Layout::new::().extend(Layout::for_value::<[T]>(slice)).unwrap(); - let mem = arena.dropless.alloc_raw(layout) as *mut List; + Layout::new::>().extend(Layout::for_value::<[T]>(slice)).unwrap(); + + let mem = arena.dropless.alloc_raw(layout) as *mut RawList; unsafe { + // Write the header + ptr::addr_of_mut!((*mem).skel.header).write(header); + // Write the length - ptr::addr_of_mut!((*mem).len).write(slice.len()); + ptr::addr_of_mut!((*mem).skel.len).write(slice.len()); // Write the elements - ptr::addr_of_mut!((*mem).data) + ptr::addr_of_mut!((*mem).skel.data) .cast::() .copy_from_nonoverlapping(slice.as_ptr(), slice.len()); @@ -110,17 +125,44 @@ impl List { // // This would be weird, as `self.into_iter` iterates over `T` directly. #[inline(always)] - pub fn iter(&self) -> <&'_ List as IntoIterator>::IntoIter { + pub fn iter(&self) -> <&'_ RawList as IntoIterator>::IntoIter + where + T: Copy, + { self.into_iter() } } -impl fmt::Debug for List { +macro_rules! impl_list_empty { + ($header_ty:ty, $header_init:expr) => { + impl RawList<$header_ty, T> { + /// Returns a reference to the (per header unique, static) empty list. + #[inline(always)] + pub fn empty<'a>() -> &'a RawList<$header_ty, T> { + #[repr(align(64))] + struct MaxAlign; + + static EMPTY: ListSkeleton<$header_ty, MaxAlign> = + ListSkeleton { header: $header_init, len: 0, data: [] }; + + assert!(mem::align_of::() <= mem::align_of::()); + + // SAFETY: `EMPTY` is sufficiently aligned to be an empty list for all + // types with `align_of(T) <= align_of(MaxAlign)`, which we checked above. + unsafe { &*(std::ptr::addr_of!(EMPTY) as *const RawList<$header_ty, T>) } + } + } + }; +} + +impl_list_empty!((), ()); + +impl fmt::Debug for RawList { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { (**self).fmt(f) } } -impl<'tcx, T: super::DebugWithInfcx>> super::DebugWithInfcx> for List { +impl<'tcx, H, T: DebugWithInfcx>> DebugWithInfcx> for RawList { fn fmt>>( this: WithInfcx<'_, Infcx, &Self>, f: &mut core::fmt::Formatter<'_>, @@ -129,40 +171,40 @@ impl<'tcx, T: super::DebugWithInfcx>> super::DebugWithInfcx> Encodable for List { +impl> Encodable for RawList { #[inline] fn encode(&self, s: &mut S) { (**self).encode(s); } } -impl PartialEq for List { +impl PartialEq for RawList { #[inline] - fn eq(&self, other: &List) -> bool { + fn eq(&self, other: &RawList) -> bool { // Pointer equality implies list equality (due to the unique contents // assumption). ptr::eq(self, other) } } -impl Eq for List {} +impl Eq for RawList {} -impl Ord for List +impl Ord for RawList where T: Ord, { - fn cmp(&self, other: &List) -> Ordering { + fn cmp(&self, other: &RawList) -> Ordering { // Pointer equality implies list equality (due to the unique contents // assumption), but the contents must be compared otherwise. if self == other { Ordering::Equal } else { <[T] as Ord>::cmp(&**self, &**other) } } } -impl PartialOrd for List +impl PartialOrd for RawList where T: PartialOrd, { - fn partial_cmp(&self, other: &List) -> Option { + fn partial_cmp(&self, other: &RawList) -> Option { // Pointer equality implies list equality (due to the unique contents // assumption), but the contents must be compared otherwise. if self == other { @@ -173,16 +215,16 @@ where } } -impl Hash for List { +impl Hash for RawList { #[inline] fn hash(&self, s: &mut H) { // Pointer hashing is sufficient (due to the unique contents // assumption). - (self as *const List).hash(s) + ptr::from_ref(self).hash(s) } } -impl Deref for List { +impl Deref for RawList { type Target = [T]; #[inline(always)] fn deref(&self) -> &[T] { @@ -190,14 +232,19 @@ impl Deref for List { } } -impl AsRef<[T]> for List { +impl AsRef<[T]> for RawList { #[inline(always)] fn as_ref(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.data.as_ptr(), self.len) } + let data_ptr = ptr::addr_of!(self.skel.data).cast::(); + // SAFETY: `data_ptr` has the same provenance as `self` and can therefore + // access the `self.skel.len` elements stored at `self.skel.data`. + // Note that we specifically don't reborrow `&self.skel.data`, because that + // would give us a pointer with provenance over 0 bytes. + unsafe { slice::from_raw_parts(data_ptr, self.skel.len) } } } -impl<'a, T: Copy> IntoIterator for &'a List { +impl<'a, H, T: Copy> IntoIterator for &'a RawList { type Item = T; type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>; #[inline(always)] @@ -206,27 +253,56 @@ impl<'a, T: Copy> IntoIterator for &'a List { } } -unsafe impl Sync for List {} +unsafe impl Sync for RawList {} // We need this since `List` uses extern type `OpaqueListContents`. #[cfg(parallel_compiler)] -use rustc_data_structures::sync::DynSync; - -use super::TyCtxt; -#[cfg(parallel_compiler)] -unsafe impl DynSync for List {} +unsafe impl DynSync for RawList {} // Safety: -// Layouts of `Equivalent` and `List` are the same, modulo opaque tail, -// thus aligns of `Equivalent` and `List` must be the same. -unsafe impl Aligned for List { - const ALIGN: ptr::Alignment = { - #[repr(C)] - struct Equivalent { - _len: usize, - _data: [T; 0], - } +// Layouts of `ListSkeleton` and `RawList` are the same, modulo opaque tail, +// thus aligns of `ListSkeleton` and `RawList` must be the same. +unsafe impl Aligned for RawList { + const ALIGN: ptr::Alignment = align_of::>(); +} - align_of::>() - }; +/// A [`List`] that additionally stores type information inline to speed up +/// [`TypeVisitableExt`](super::TypeVisitableExt) operations. +pub type ListWithCachedTypeInfo = RawList; + +impl ListWithCachedTypeInfo { + #[inline(always)] + pub fn flags(&self) -> TypeFlags { + self.skel.header.flags + } + + #[inline(always)] + pub fn outer_exclusive_binder(&self) -> DebruijnIndex { + self.skel.header.outer_exclusive_binder + } +} + +impl_list_empty!(TypeInfo, TypeInfo::empty()); + +/// The additional info that is stored in [`ListWithCachedTypeInfo`]. +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TypeInfo { + flags: TypeFlags, + outer_exclusive_binder: DebruijnIndex, +} + +impl TypeInfo { + const fn empty() -> Self { + Self { flags: TypeFlags::empty(), outer_exclusive_binder: super::INNERMOST } + } +} + +impl From for TypeInfo { + fn from(computation: FlagComputation) -> TypeInfo { + TypeInfo { + flags: computation.flags, + outer_exclusive_binder: computation.outer_exclusive_binder, + } + } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8565f90957f23..73b20f0485b6e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -45,21 +45,24 @@ use rustc_data_structures::unord::UnordMap; use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_index::IndexVec; -use rustc_macros::HashStable; +use rustc_macros::{ + Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, +}; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::{Decodable, Encodable}; use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{hygiene, ExpnId, ExpnKind, Span}; +use rustc_span::{ExpnId, ExpnKind, Span}; use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx}; pub use rustc_target::abi::{ReprFlags, ReprOptions}; pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx}; pub use vtable::*; +use std::assert_matches::assert_matches; use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; @@ -75,23 +78,23 @@ pub use rustc_type_ir::ConstKind::{ }; pub use rustc_type_ir::*; -pub use self::binding::BindingMode; -pub use self::binding::BindingMode::*; 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, }; pub use self::context::{ - tls, CtxtInterners, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, - TyCtxtFeed, + tls, CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, + TyCtxt, TyCtxtFeed, }; -pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams}; -pub use self::list::List; +pub use self::instance::{Instance, InstanceDef, ReifyReason, ShortInstance, UnusedGenericParams}; +pub use self::list::{List, ListWithCachedTypeInfo}; pub use self::parameterized::ParameterizedOverTcx; +pub use self::pattern::{Pattern, PatternKind}; pub use self::predicate::{ Clause, ClauseKind, CoercePredicate, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, NormalizesTo, OutlivesPredicate, PolyCoercePredicate, @@ -122,7 +125,6 @@ pub use self::typeck_results::{ pub mod _match; pub mod abstract_const; pub mod adjustment; -pub mod binding; pub mod cast; pub mod codec; pub mod error; @@ -132,6 +134,7 @@ pub mod fold; pub mod inhabitedness; pub mod layout; pub mod normalize_erasing_regions; +pub mod pattern; pub mod print; pub mod relate; pub mod trait_def; @@ -214,7 +217,6 @@ pub struct ResolverAstLowering { pub next_node_id: ast::NodeId, pub node_id_to_def_id: NodeMap, - pub def_id_to_node_id: IndexVec, pub trait_map: NodeMap>, /// List functions and methods for which lifetime elision was successful. @@ -224,8 +226,15 @@ pub struct ResolverAstLowering { pub lint_buffer: Steal, /// Information about functions signatures for delegation items expansion - pub has_self: LocalDefIdSet, - pub fn_parameter_counts: LocalDefIdMap, + pub delegation_fn_sigs: LocalDefIdMap, +} + +#[derive(Debug)] +pub struct DelegationFnSig { + pub header: ast::FnHeader, + pub param_count: usize, + pub has_self: bool, + pub c_variadic: bool, } #[derive(Clone, Copy, Debug)] @@ -280,23 +289,43 @@ pub enum ImplPolarity { Reservation, } -impl ImplPolarity { +impl fmt::Display for ImplPolarity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Positive => f.write_str("positive"), + Self::Negative => f.write_str("negative"), + Self::Reservation => f.write_str("reservation"), + } + } +} + +/// Polarity for a trait predicate. May either be negative or positive. +/// Distinguished from [`ImplPolarity`] since we never compute goals with +/// "reservation" level. +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)] +#[derive(TypeFoldable, TypeVisitable)] +pub enum PredicatePolarity { + /// `Type: Trait` + Positive, + /// `Type: !Trait` + Negative, +} + +impl PredicatePolarity { /// Flips polarity by turning `Positive` into `Negative` and `Negative` into `Positive`. - pub fn flip(&self) -> Option { + pub fn flip(&self) -> PredicatePolarity { match self { - ImplPolarity::Positive => Some(ImplPolarity::Negative), - ImplPolarity::Negative => Some(ImplPolarity::Positive), - ImplPolarity::Reservation => None, + PredicatePolarity::Positive => PredicatePolarity::Negative, + PredicatePolarity::Negative => PredicatePolarity::Positive, } } } -impl fmt::Display for ImplPolarity { +impl fmt::Display for PredicatePolarity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Positive => f.write_str("positive"), Self::Negative => f.write_str("negative"), - Self::Reservation => f.write_str("reservation"), } } } @@ -496,7 +525,7 @@ pub struct CReaderCacheKey { } /// Use this rather than `TyKind`, whenever possible. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] #[rustc_diagnostic_item = "Ty"] #[rustc_pass_by_value] pub struct Ty<'tcx>(Interned<'tcx, WithCachedTypeInfo>>); @@ -681,7 +710,7 @@ const TAG_MASK: usize = 0b11; const TYPE_TAG: usize = 0b00; const CONST_TAG: usize = 0b01; -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable)] pub enum TermKind<'tcx> { Ty(Ty<'tcx>), @@ -812,6 +841,38 @@ pub struct OpaqueTypeKey<'tcx> { pub args: GenericArgsRef<'tcx>, } +impl<'tcx> OpaqueTypeKey<'tcx> { + pub fn iter_captured_args( + self, + tcx: TyCtxt<'tcx>, + ) -> impl Iterator)> { + std::iter::zip(self.args, tcx.variances_of(self.def_id)).enumerate().filter_map( + |(i, (arg, v))| match (arg.unpack(), v) { + (_, ty::Invariant) => Some((i, arg)), + (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => None, + _ => bug!("unexpected opaque type arg variance"), + }, + ) + } + + pub fn fold_captured_lifetime_args( + self, + tcx: TyCtxt<'tcx>, + mut f: impl FnMut(Region<'tcx>) -> Region<'tcx>, + ) -> Self { + let Self { def_id, args } = self; + let args = std::iter::zip(args, tcx.variances_of(def_id)).map(|(arg, v)| { + match (arg.unpack(), v) { + (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg, + (ty::GenericArgKind::Lifetime(lt), _) => f(lt).into(), + _ => arg, + } + }); + let args = tcx.mk_args_from_iter(args); + Self { def_id, args } + } +} + #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)] pub struct OpaqueHiddenType<'tcx> { /// The span of this particular definition of the opaque type. So @@ -959,7 +1020,7 @@ impl PlaceholderLike for PlaceholderType { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] -#[derive(TyEncodable, TyDecodable, PartialOrd, Ord)] +#[derive(TyEncodable, TyDecodable)] pub struct BoundConst<'tcx> { pub var: BoundVar, pub ty: Ty<'tcx>, @@ -985,9 +1046,23 @@ impl PlaceholderLike for PlaceholderConst { } } -/// When type checking, we use the `ParamEnv` to track -/// details about the set of where-clauses that are in scope at this -/// particular point. +pub type Clauses<'tcx> = &'tcx ListWithCachedTypeInfo>; + +impl<'tcx> rustc_type_ir::visit::Flags for Clauses<'tcx> { + fn flags(&self) -> TypeFlags { + (**self).flags() + } + + fn outer_exclusive_binder(&self) -> DebruijnIndex { + (**self).outer_exclusive_binder() + } +} + +/// When interacting with the type system we must provide information about the +/// environment. `ParamEnv` is the type that represents this information. See the +/// [dev guide chapter][param_env_guide] for more information. +/// +/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct ParamEnv<'tcx> { /// This packs both caller bounds and the reveal enum into one pointer. @@ -1002,7 +1077,7 @@ pub struct ParamEnv<'tcx> { /// want `Reveal::All`. /// /// Note: This is packed, use the reveal() method to access it. - packed: CopyTaggedPtr<&'tcx List>, ParamTag, true>, + packed: CopyTaggedPtr, ParamTag, true>, } #[derive(Copy, Clone)] @@ -1010,7 +1085,7 @@ struct ParamTag { reveal: traits::Reveal, } -impl_tag! { +rustc_data_structures::impl_tag! { impl Tag for ParamTag; ParamTag { reveal: traits::Reveal::UserFacing }, ParamTag { reveal: traits::Reveal::All }, @@ -1054,15 +1129,18 @@ impl<'tcx> TypeVisitable> for ParamEnv<'tcx> { impl<'tcx> ParamEnv<'tcx> { /// Construct a trait environment suitable for contexts where /// there are no where-clauses in scope. Hidden types (like `impl - /// Trait`) are left hidden, so this is suitable for ordinary - /// type-checking. + /// Trait`) are left hidden. In majority of cases it is incorrect + /// to use an empty environment. See the [dev guide section][param_env_guide] + /// for information on what a `ParamEnv` is and how to acquire one. + /// + /// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/param_env/param_env_summary.html #[inline] pub fn empty() -> Self { - Self::new(List::empty(), Reveal::UserFacing) + Self::new(ListWithCachedTypeInfo::empty(), Reveal::UserFacing) } #[inline] - pub fn caller_bounds(self) -> &'tcx List> { + pub fn caller_bounds(self) -> Clauses<'tcx> { self.packed.pointer() } @@ -1080,12 +1158,12 @@ impl<'tcx> ParamEnv<'tcx> { /// or invoke `param_env.with_reveal_all()`. #[inline] pub fn reveal_all() -> Self { - Self::new(List::empty(), Reveal::All) + Self::new(ListWithCachedTypeInfo::empty(), Reveal::All) } /// Construct a trait environment with the given set of predicates. #[inline] - pub fn new(caller_bounds: &'tcx List>, reveal: Reveal) -> Self { + pub fn new(caller_bounds: Clauses<'tcx>, reveal: Reveal) -> Self { ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, ParamTag { reveal }) } } @@ -1114,7 +1192,7 @@ impl<'tcx> ParamEnv<'tcx> { /// Returns this same environment but with no caller bounds. #[inline] pub fn without_caller_bounds(self) -> Self { - Self::new(List::empty(), self.reveal()) + Self::new(ListWithCachedTypeInfo::empty(), self.reveal()) } /// Creates a pair of param-env and value for use in queries. @@ -1146,7 +1224,7 @@ pub struct Destructor { #[derive(Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] pub struct VariantFlags(u8); -bitflags! { +bitflags::bitflags! { impl VariantFlags: u8 { const NO_VARIANT_FLAGS = 0; /// Indicates whether the field list of this variant is `#[non_exhaustive]`. @@ -1270,7 +1348,7 @@ impl VariantDef { pub fn single_field(&self) -> &FieldDef { assert!(self.fields.len() == 1); - &self.fields[FieldIdx::from_u32(0)] + &self.fields[FieldIdx::ZERO] } /// Returns the last field in this variant, if present. @@ -1425,14 +1503,14 @@ pub enum ImplOverlapKind { /// Whether or not the impl is permitted due to the trait being a `#[marker]` trait marker: bool, }, - /// These impls are allowed to overlap, but that raises - /// an issue #33140 future-compatibility warning. + /// These impls are allowed to overlap, but that raises an + /// issue #33140 future-compatibility warning (tracked in #56484). /// /// Some background: in Rust 1.0, the trait-object types `Send + Sync` (today's /// `dyn Send + Sync`) and `Sync + Send` (now `dyn Sync + Send`) were different. /// - /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied - /// that difference, making what reduces to the following set of impls: + /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied on + /// that difference, doing what reduces to the following set of impls: /// /// ```compile_fail,(E0119) /// trait Trait {} @@ -1457,7 +1535,7 @@ pub enum ImplOverlapKind { /// 4. Neither of the impls can have any where-clauses. /// /// Once `traitobject` 0.1.0 is no longer an active concern, this hack can be removed. - Issue33140, + FutureCompatOrderDepTraitObjects, } /// Useful source information about where a desugared associated type for an @@ -1503,7 +1581,6 @@ impl<'tcx> TyCtxt<'tcx> { attr::ReprRust => ReprFlags::empty(), attr::ReprC => ReprFlags::IS_C, attr::ReprPacked(pack) => { - let pack = Align::from_bytes(pack as u64).unwrap(); min_pack = Some(if let Some(min_pack) = min_pack { min_pack.min(pack) } else { @@ -1535,7 +1612,7 @@ impl<'tcx> TyCtxt<'tcx> { ReprFlags::empty() } attr::ReprAlign(align) => { - max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap())); + max_align = max_align.max(Some(align)); ReprFlags::empty() } }); @@ -1653,27 +1730,26 @@ impl<'tcx> TyCtxt<'tcx> { | (ImplPolarity::Negative, ImplPolarity::Negative) => {} }; - let is_marker_overlap = { - let is_marker_impl = - |trait_ref: TraitRef<'_>| -> bool { self.trait_def(trait_ref.def_id).is_marker }; - is_marker_impl(trait_ref1) && is_marker_impl(trait_ref2) - }; + let is_marker_impl = |trait_ref: TraitRef<'_>| self.trait_def(trait_ref.def_id).is_marker; + let is_marker_overlap = is_marker_impl(trait_ref1) && is_marker_impl(trait_ref2); if is_marker_overlap { - Some(ImplOverlapKind::Permitted { marker: true }) - } else { - if let Some(self_ty1) = self.issue33140_self_ty(def_id1) { - if let Some(self_ty2) = self.issue33140_self_ty(def_id2) { - if self_ty1 == self_ty2 { - return Some(ImplOverlapKind::Issue33140); - } else { - debug!("found {self_ty1:?} != {self_ty2:?}"); - } - } - } + return Some(ImplOverlapKind::Permitted { marker: true }); + } - None + if let Some(self_ty1) = + self.self_ty_of_trait_impl_enabling_order_dep_trait_object_hack(def_id1) + && let Some(self_ty2) = + self.self_ty_of_trait_impl_enabling_order_dep_trait_object_hack(def_id2) + { + if self_ty1 == self_ty2 { + return Some(ImplOverlapKind::FutureCompatOrderDepTraitObjects); + } else { + debug!("found {self_ty1:?} != {self_ty2:?}"); + } } + + None } /// Returns `ty::VariantDef` if `res` refers to a struct, @@ -1729,7 +1805,8 @@ impl<'tcx> TyCtxt<'tcx> { | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance), + | ty::InstanceDef::FnPtrAddrShim(..) + | ty::InstanceDef::AsyncDropGlueCtorShim(..) => self.mir_shims(instance), } } @@ -1752,9 +1829,8 @@ impl<'tcx> TyCtxt<'tcx> { let filter_fn = move |a: &&ast::Attribute| a.has_name(attr); if let Some(did) = did.as_local() { self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) - } else if cfg!(debug_assertions) && rustc_feature::is_builtin_only_local(attr) { - bug!("tried to access the `only_local` attribute `{}` from an extern crate", attr); } else { + debug_assert!(rustc_feature::encode_cross_crate(attr)); self.item_attrs(did).iter().filter(filter_fn) } } @@ -1786,12 +1862,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Determines whether an item is annotated with an attribute. pub fn has_attr(self, did: impl Into, attr: Symbol) -> bool { - let did: DefId = did.into(); - if cfg!(debug_assertions) && !did.is_local() && rustc_feature::is_builtin_only_local(attr) { - bug!("tried to access the `only_local` attribute `{}` from an extern crate", attr); - } else { - self.get_attrs(did, attr).next().is_some() - } + self.get_attrs(did, attr).next().is_some() } /// Returns `true` if this is an `auto trait`. @@ -1812,8 +1883,40 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns layout of a coroutine. Layout might be unavailable if the /// coroutine is tainted by errors. - pub fn coroutine_layout(self, def_id: DefId) -> Option<&'tcx CoroutineLayout<'tcx>> { - self.optimized_mir(def_id).coroutine_layout() + /// + /// Takes `coroutine_kind` which can be acquired from the `CoroutineArgs::kind_ty`, + /// e.g. `args.as_coroutine().kind_ty()`. + pub fn coroutine_layout( + self, + def_id: DefId, + coroutine_kind_ty: Ty<'tcx>, + ) -> Option<&'tcx CoroutineLayout<'tcx>> { + let mir = self.optimized_mir(def_id); + // Regular coroutine + if coroutine_kind_ty.is_unit() { + mir.coroutine_layout_raw() + } else { + // If we have a `Coroutine` that comes from an coroutine-closure, + // then it may be a by-move or by-ref body. + let ty::Coroutine(_, identity_args) = + *self.type_of(def_id).instantiate_identity().kind() + else { + unreachable!(); + }; + let identity_kind_ty = identity_args.as_coroutine().kind_ty(); + // If the types differ, then we must be getting the by-move body of + // a by-ref coroutine. + if identity_kind_ty == coroutine_kind_ty { + mir.coroutine_layout_raw() + } else { + assert_matches!(coroutine_kind_ty.to_opt_closure_kind(), Some(ClosureKind::FnOnce)); + assert_matches!( + identity_kind_ty.to_opt_closure_kind(), + Some(ClosureKind::Fn | ClosureKind::FnMut) + ); + mir.coroutine_by_move_body().unwrap().coroutine_layout_raw() + } + } } /// Given the `DefId` of an impl, returns the `DefId` of the trait it implements. @@ -1911,22 +2014,6 @@ impl<'tcx> TyCtxt<'tcx> { (ident, scope) } - /// Returns corrected span if the debuginfo for `span` should be collapsed to the outermost - /// expansion site (with collapse_debuginfo attribute if the corresponding feature enabled). - /// Only applies when `Span` is the result of macro expansion. - /// - /// - If the `collapse_debuginfo` feature is enabled then debuginfo is not collapsed by default - /// and only when a (some enclosing) macro definition is annotated with `#[collapse_debuginfo]`. - /// - If `collapse_debuginfo` is not enabled, then debuginfo is collapsed by default. - /// - /// When `-Zdebug-macros` is provided then debuginfo will never be collapsed. - pub fn collapsed_debuginfo(self, span: Span, upto: Span) -> Span { - if self.sess.opts.unstable_opts.debug_macros || !span.from_expansion() { - return span; - } - hygiene::walk_chain_collapsed(span, upto, self.features().collapse_debuginfo) - } - #[inline] pub fn is_const_fn_raw(self, def_id: DefId) -> bool { matches!( @@ -2089,7 +2176,7 @@ pub struct DestructuredConst<'tcx> { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index d4c8f5900f9ee..791f27a97896d 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -10,6 +10,7 @@ use crate::traits::query::NoSolution; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder}; use crate::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; #[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)] pub enum NormalizationError<'tcx> { @@ -49,7 +50,7 @@ impl<'tcx> TyCtxt<'tcx> { let value = self.erase_regions(value); debug!(?value); - if !value.has_projections() { + if !value.has_aliases() { value } else { value.fold_with(&mut NormalizeAfterErasingRegionsFolder { tcx: self, param_env }) @@ -81,7 +82,7 @@ impl<'tcx> TyCtxt<'tcx> { let value = self.erase_regions(value); debug!(?value); - if !value.has_projections() { + if !value.has_aliases() { Ok(value) } else { let mut folder = TryNormalizeAfterErasingRegionsFolder::new(self, param_env); diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs new file mode 100644 index 0000000000000..d1875fbaea376 --- /dev/null +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -0,0 +1,49 @@ +use std::fmt; + +use crate::ty; +use rustc_data_structures::intern::Interned; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] +#[rustc_pass_by_value] +pub struct Pattern<'tcx>(pub Interned<'tcx, PatternKind<'tcx>>); + +impl<'tcx> std::ops::Deref for Pattern<'tcx> { + type Target = PatternKind<'tcx>; + + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + +impl<'tcx> fmt::Debug for Pattern<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", **self) + } +} + +impl<'tcx> fmt::Debug for PatternKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + PatternKind::Range { start, end, include_end } => { + if let Some(start) = start { + write!(f, "{start}")?; + } + write!(f, "..")?; + if include_end { + write!(f, "=")?; + } + if let Some(end) = end { + write!(f, "{end}")?; + } + Ok(()) + } + } + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] +pub enum PatternKind<'tcx> { + Range { start: Option>, end: Option>, include_end: bool }, +} diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 62822505fa5d2..56dd52567fde0 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -3,6 +3,7 @@ use rustc_data_structures::intern::Interned; use rustc_errors::{DiagArgValue, IntoDiagArg}; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; +use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_span::Span; use rustc_type_ir::ClauseKind as IrClauseKind; use rustc_type_ir::PredicateKind as IrPredicateKind; @@ -11,7 +12,7 @@ use std::cmp::Ordering; use crate::ty::visit::TypeVisitableExt; use crate::ty::{ self, AliasTy, Binder, DebruijnIndex, DebugWithInfcx, EarlyBinder, GenericArg, GenericArgs, - GenericArgsRef, ImplPolarity, Term, Ty, TyCtxt, TypeFlags, WithCachedTypeInfo, + GenericArgsRef, PredicatePolarity, Term, Ty, TyCtxt, TypeFlags, WithCachedTypeInfo, }; pub type ClauseKind<'tcx> = IrClauseKind>; @@ -70,7 +71,7 @@ impl<'tcx> Predicate<'tcx> { polarity, })) => Some(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { trait_ref, - polarity: polarity.flip()?, + polarity: polarity.flip(), }))), _ => None, @@ -192,7 +193,7 @@ impl<'tcx> Clause<'tcx> { } } -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub enum ExistentialPredicate<'tcx> { /// E.g., `Iterator`. @@ -336,7 +337,7 @@ impl<'tcx> ty::List> { /// /// Trait references also appear in object types like `Foo`, but in /// that case the `Self` parameter is absent from the generic parameters. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct TraitRef<'tcx> { pub def_id: DefId, @@ -420,7 +421,7 @@ impl<'tcx> IntoDiagArg for TraitRef<'tcx> { /// ``` /// The generic parameters don't include the erased `Self`, only trait /// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct ExistentialTraitRef<'tcx> { pub def_id: DefId, @@ -476,7 +477,7 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> { } /// A `ProjectionPredicate` for an `ExistentialTraitRef`. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct ExistentialProjection<'tcx> { pub def_id: DefId, @@ -663,7 +664,7 @@ pub struct TraitPredicate<'tcx> { /// exist via a series of predicates.) /// /// If polarity is Reserved: that's a bug. - pub polarity: ImplPolarity, + pub polarity: PredicatePolarity, } pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; @@ -693,7 +694,7 @@ impl<'tcx> PolyTraitPredicate<'tcx> { } #[inline] - pub fn polarity(self) -> ImplPolarity { + pub fn polarity(self) -> PredicatePolarity { self.skip_binder().polarity } } @@ -907,7 +908,7 @@ impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> { impl<'tcx> ToPredicate<'tcx, TraitPredicate<'tcx>> for TraitRef<'tcx> { #[inline(always)] fn to_predicate(self, _tcx: TyCtxt<'tcx>) -> TraitPredicate<'tcx> { - TraitPredicate { trait_ref: self, polarity: ImplPolarity::Positive } + TraitPredicate { trait_ref: self, polarity: PredicatePolarity::Positive } } } @@ -940,7 +941,7 @@ impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for Binder<'tcx, TraitRef fn to_predicate(self, _: TyCtxt<'tcx>) -> PolyTraitPredicate<'tcx> { self.map_bound(|trait_ref| TraitPredicate { trait_ref, - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }) } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 520fc1dd7aa4d..9d0e1123e43ac 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -259,11 +259,11 @@ fn characteristic_def_id_of_type_cached<'a>( ty::Dynamic(data, ..) => data.principal_def_id(), - ty::Array(subty, _) | ty::Slice(subty) => { + ty::Pat(subty, _) | ty::Array(subty, _) | ty::Slice(subty) => { characteristic_def_id_of_type_cached(subty, visited) } - ty::RawPtr(mt) => characteristic_def_id_of_type_cached(mt.ty, visited), + ty::RawPtr(ty, _) => characteristic_def_id_of_type_cached(ty, visited), ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 802953867ac1b..5f47aef0f32cb 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -4,17 +4,19 @@ use crate::query::Providers; use crate::traits::util::{super_predicates_for_pretty_printing, supertraits_for_pretty_printing}; use crate::ty::GenericArgKind; use crate::ty::{ - ConstInt, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable, + ConstInt, Expr, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; use rustc_hir::def_id::{DefIdMap, DefIdSet, ModDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPathDataName}; use rustc_hir::LangItem; +use rustc_macros::Lift; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; use rustc_session::Limit; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -24,7 +26,6 @@ use rustc_target::spec::abi::Abi; use smallvec::SmallVec; use std::cell::Cell; -use std::collections::BTreeMap; use std::fmt::{self, Write as _}; use std::iter; use std::ops::{Deref, DerefMut}; @@ -270,6 +271,31 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { Ok(()) } + /// Prints `(...)` around what `f` prints. + fn parenthesized( + &mut self, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + ) -> Result<(), PrintError> { + self.write_str("(")?; + f(self)?; + self.write_str(")")?; + Ok(()) + } + + /// Prints `(...)` around what `f` prints if `parenthesized` is true, otherwise just prints `f`. + fn maybe_parenthesized( + &mut self, + f: impl FnOnce(&mut Self) -> Result<(), PrintError>, + parenthesized: bool, + ) -> Result<(), PrintError> { + if parenthesized { + self.parenthesized(f)?; + } else { + f(self)?; + } + Ok(()) + } + /// Prints `<...>` around what `f` prints. fn generic_delimiters( &mut self, @@ -642,15 +668,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Int(t) => p!(write("{}", t.name_str())), ty::Uint(t) => p!(write("{}", t.name_str())), ty::Float(t) => p!(write("{}", t.name_str())), - ty::RawPtr(ref tm) => { - p!(write( - "*{} ", - match tm.mutbl { - hir::Mutability::Mut => "mut", - hir::Mutability::Not => "const", - } - )); - p!(print(tm.ty)) + ty::Pat(ty, pat) => { + p!("(", print(ty), ") is ", write("{pat:?}")) + } + ty::RawPtr(ty, mutbl) => { + p!(write("*{} ", mutbl.ptr_str())); + p!(print(ty)) } ty::Ref(r, ty, mutbl) => { p!("&"); @@ -779,7 +802,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } ty::Str => p!("str"), ty::Coroutine(did, args) => { - p!(write("{{")); + p!("{{"); let coroutine_kind = self.tcx().coroutine_kind(did).unwrap(); let should_print_movability = self.should_print_verbose() || matches!(coroutine_kind, hir::CoroutineKind::Coroutine(_)); @@ -793,9 +816,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if !self.should_print_verbose() { p!(write("{}", coroutine_kind)); - // FIXME(eddyb) should use `def_span`. - if let Some(did) = did.as_local() { - let span = self.tcx().def_span(did); + if coroutine_kind.is_fn_like() { + // If we are printing an `async fn` coroutine type, then give the path + // of the fn, instead of its span, because that will in most cases be + // more helpful for the reader than just a source location. + // + // This will look like: + // {async fn body of some_fn()} + let did_of_the_fn_item = self.tcx().parent(did); + p!(" of ", print_def_path(did_of_the_fn_item, args), "()"); + } else if let Some(local_did) = did.as_local() { + let span = self.tcx().def_span(local_did); p!(write( "@{}", // This may end up in stderr diagnostics but it may also be emitted @@ -803,7 +834,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.tcx().sess.source_map().span_to_embeddable_string(span) )); } else { - p!(write("@"), print_def_path(did, args)); + p!("@", print_def_path(did, args)); } } else { p!(print_def_path(did, args)); @@ -970,11 +1001,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // Don't print `+ Sized`, but rather `+ ?Sized` if absent. if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() { match pred.polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => { + ty::PredicatePolarity::Positive => { has_sized_bound = true; continue; } - ty::ImplPolarity::Negative => has_negative_sized_bound = true, + ty::PredicatePolarity::Negative => has_negative_sized_bound = true, } } @@ -995,7 +1026,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.insert_trait_and_projection( trait_ref, - ty::ImplPolarity::Positive, + ty::PredicatePolarity::Positive, Some(proj_ty), &mut traits, &mut fn_traits, @@ -1060,7 +1091,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { _ => { if entry.has_fn_once { traits - .entry((fn_once_trait_ref, ty::ImplPolarity::Positive)) + .entry((fn_once_trait_ref, ty::PredicatePolarity::Positive)) .or_default() .extend( // Group the return ty with its def id, if we had one. @@ -1070,10 +1101,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ); } if let Some(trait_ref) = entry.fn_mut_trait_ref { - traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default(); + traits.entry((trait_ref, ty::PredicatePolarity::Positive)).or_default(); } if let Some(trait_ref) = entry.fn_trait_ref { - traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default(); + traits.entry((trait_ref, ty::PredicatePolarity::Positive)).or_default(); } } } @@ -1089,7 +1120,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.wrap_binder(&trait_ref, |trait_ref, cx| { define_scoped_cx!(cx); - if polarity == ty::ImplPolarity::Negative { + if polarity == ty::PredicatePolarity::Negative { p!("!"); } p!(print(trait_ref.print_only_trait_name())); @@ -1198,10 +1229,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { fn insert_trait_and_projection( &mut self, trait_ref: ty::PolyTraitRef<'tcx>, - polarity: ty::ImplPolarity, + polarity: ty::PredicatePolarity, proj_ty: Option<(DefId, ty::Binder<'tcx, Term<'tcx>>)>, traits: &mut FxIndexMap< - (ty::PolyTraitRef<'tcx>, ty::ImplPolarity), + (ty::PolyTraitRef<'tcx>, ty::PredicatePolarity), FxIndexMap>>, >, fn_traits: &mut FxIndexMap, OpaqueFnEntry<'tcx>>, @@ -1211,7 +1242,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce // super-trait ref and record it there. // We skip negative Fn* bounds since they can't use parenthetical notation anyway. - if polarity == ty::ImplPolarity::Positive + if polarity == ty::PredicatePolarity::Positive && let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() { // If we have a FnOnce, then insert it into @@ -1246,7 +1277,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { fn pretty_print_inherent_projection( &mut self, - alias_ty: &ty::AliasTy<'tcx>, + alias_ty: ty::AliasTy<'tcx>, ) -> Result<(), PrintError> { let def_key = self.tcx().def_key(alias_ty.def_id); self.path_generic_args( @@ -1490,12 +1521,137 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Placeholder(placeholder) => p!(write("{placeholder:?}")), // FIXME(generic_const_exprs): // write out some legible representation of an abstract const? - ty::ConstKind::Expr(_) => p!("{{const expr}}"), + ty::ConstKind::Expr(expr) => self.pretty_print_const_expr(expr, print_ty)?, ty::ConstKind::Error(_) => p!("{{const error}}"), }; Ok(()) } + fn pretty_print_const_expr( + &mut self, + expr: Expr<'tcx>, + print_ty: bool, + ) -> Result<(), PrintError> { + define_scoped_cx!(self); + match expr { + Expr::Binop(op, c1, c2) => { + let precedence = |binop: rustc_middle::mir::BinOp| { + use rustc_ast::util::parser::AssocOp; + AssocOp::from_ast_binop(binop.to_hir_binop().into()).precedence() + }; + let op_precedence = precedence(op); + let formatted_op = op.to_hir_binop().as_str(); + let (lhs_parenthesized, rhs_parenthesized) = match (c1.kind(), c2.kind()) { + ( + ty::ConstKind::Expr(Expr::Binop(lhs_op, _, _)), + ty::ConstKind::Expr(Expr::Binop(rhs_op, _, _)), + ) => (precedence(lhs_op) < op_precedence, precedence(rhs_op) < op_precedence), + (ty::ConstKind::Expr(Expr::Binop(lhs_op, ..)), ty::ConstKind::Expr(_)) => { + (precedence(lhs_op) < op_precedence, true) + } + (ty::ConstKind::Expr(_), ty::ConstKind::Expr(Expr::Binop(rhs_op, ..))) => { + (true, precedence(rhs_op) < op_precedence) + } + (ty::ConstKind::Expr(_), ty::ConstKind::Expr(_)) => (true, true), + (ty::ConstKind::Expr(Expr::Binop(lhs_op, ..)), _) => { + (precedence(lhs_op) < op_precedence, false) + } + (_, ty::ConstKind::Expr(Expr::Binop(rhs_op, ..))) => { + (false, precedence(rhs_op) < op_precedence) + } + (ty::ConstKind::Expr(_), _) => (true, false), + (_, ty::ConstKind::Expr(_)) => (false, true), + _ => (false, false), + }; + + self.maybe_parenthesized( + |this| this.pretty_print_const(c1, print_ty), + lhs_parenthesized, + )?; + p!(write(" {formatted_op} ")); + self.maybe_parenthesized( + |this| this.pretty_print_const(c2, print_ty), + rhs_parenthesized, + )?; + } + Expr::UnOp(op, ct) => { + use rustc_middle::mir::UnOp; + let formatted_op = match op { + UnOp::Not => "!", + UnOp::Neg => "-", + }; + let parenthesized = match ct.kind() { + ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op, + ty::ConstKind::Expr(_) => true, + _ => false, + }; + p!(write("{formatted_op}")); + self.maybe_parenthesized( + |this| this.pretty_print_const(ct, print_ty), + parenthesized, + )? + } + Expr::FunctionCall(fn_def, fn_args) => { + use ty::TyKind; + match fn_def.ty().kind() { + TyKind::FnDef(def_id, gen_args) => { + p!(print_value_path(*def_id, gen_args), "("); + if print_ty { + let tcx = self.tcx(); + let sig = tcx.fn_sig(def_id).instantiate(tcx, gen_args).skip_binder(); + + let mut args_with_ty = fn_args.iter().map(|ct| (ct, ct.ty())); + let output_ty = sig.output(); + + if let Some((ct, ty)) = args_with_ty.next() { + self.typed_value( + |this| this.pretty_print_const(ct, print_ty), + |this| this.pretty_print_type(ty), + ": ", + )?; + for (ct, ty) in args_with_ty { + p!(", "); + self.typed_value( + |this| this.pretty_print_const(ct, print_ty), + |this| this.pretty_print_type(ty), + ": ", + )?; + } + } + p!(write(") -> {output_ty}")); + } else { + p!(comma_sep(fn_args.iter()), ")"); + } + } + _ => bug!("unexpected type of fn def"), + } + } + Expr::Cast(kind, ct, ty) => { + use ty::abstract_const::CastKind; + if kind == CastKind::As || (kind == CastKind::Use && self.should_print_verbose()) { + let parenthesized = match ct.kind() { + ty::ConstKind::Expr(Expr::Cast(_, _, _)) => false, + ty::ConstKind::Expr(_) => true, + _ => false, + }; + self.maybe_parenthesized( + |this| { + this.typed_value( + |this| this.pretty_print_const(ct, print_ty), + |this| this.pretty_print_type(ty), + " as ", + ) + }, + parenthesized, + )?; + } else { + self.pretty_print_const(ct, print_ty)? + } + } + } + Ok(()) + } + fn pretty_print_const_scalar( &mut self, scalar: Scalar, @@ -1602,7 +1758,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(write("{:?}", char::try_from(int).unwrap())) } // Pointer types - ty::Ref(..) | ty::RawPtr(_) | ty::FnPtr(_) => { + ty::Ref(..) | ty::RawPtr(_, _) | ty::FnPtr(_) => { let data = int.assert_bits(self.tcx().data_layout.pointer_size); self.typed_value( |this| { @@ -2379,7 +2535,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { struct RegionFolder<'a, 'tcx> { tcx: TyCtxt<'tcx>, current_index: ty::DebruijnIndex, - region_map: BTreeMap>, + region_map: UnordMap>, name: &'a mut ( dyn FnMut( Option, // Debruijn index of the folded late-bound region @@ -2431,7 +2587,7 @@ impl<'a, 'tcx> ty::TypeFolder> for RegionFolder<'a, 'tcx> { ty::BrAnon | ty::BrEnv => r, _ => { // Index doesn't matter, since this is just for naming and these never get bound - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind }; + let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind }; *self .region_map .entry(br) @@ -2456,7 +2612,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { pub fn name_all_regions( &mut self, value: &ty::Binder<'tcx, T>, - ) -> Result<(T, BTreeMap>), fmt::Error> + ) -> Result<(T, UnordMap>), fmt::Error> where T: Print<'tcx, Self> + TypeFoldable>, { @@ -2533,7 +2689,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { write!(self, "{var:?}")?; } start_or_continue(self, "", "> "); - (value.clone().skip_binder(), BTreeMap::default()) + (value.clone().skip_binder(), UnordMap::default()) } else { let tcx = self.tcx; @@ -2605,7 +2761,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { tcx, current_index: ty::INNERMOST, name: &mut name, - region_map: BTreeMap::new(), + region_map: UnordMap::default(), }; let new_value = value.clone().skip_binder().fold_with(&mut folder); let region_map = folder.region_map; @@ -2989,7 +3145,7 @@ define_print_and_forward_display! { TraitPredPrintModifiersAndPath<'tcx> { p!(pretty_print_bound_constness(self.0.trait_ref)); - if let ty::ImplPolarity::Negative = self.0.polarity { + if let ty::PredicatePolarity::Negative = self.0.polarity { p!("!") } p!(print(self.0.trait_ref.print_only_trait_path())); @@ -3022,7 +3178,7 @@ define_print_and_forward_display! { ty::TraitPredicate<'tcx> { p!(print(self.trait_ref.self_ty()), ": "); p!(pretty_print_bound_constness(self.trait_ref)); - if let ty::ImplPolarity::Negative = self.polarity { + if let ty::PredicatePolarity::Negative = self.polarity { p!("!"); } p!(print(self.trait_ref.print_trait_sugared())) @@ -3049,7 +3205,7 @@ define_print_and_forward_display! { ty::AliasTy<'tcx> { if let DefKind::Impl { of_trait: false } = cx.tcx().def_kind(cx.tcx().parent(self.def_id)) { - p!(pretty_print_inherent_projection(self)) + p!(pretty_print_inherent_projection(*self)) } else { // If we're printing verbosely, or don't want to invoke queries // (`is_impl_trait_in_trait`), then fall back to printing the def path. diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 7abc3cd283888..3d9be15310fee 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -3,6 +3,7 @@ use rustc_data_structures::intern::Interned; use rustc_errors::MultiSpan; use rustc_hir::def_id::DefId; use rustc_index::Idx; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::sym; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{ErrorGuaranteed, DUMMY_SP}; @@ -14,7 +15,7 @@ use crate::ty::{self, BoundVar, TyCtxt, TypeFlags}; pub type RegionKind<'tcx> = IrRegionKind>; /// Use this rather than `RegionKind`, whenever possible. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] #[rustc_pass_by_value] pub struct Region<'tcx>(pub Interned<'tcx, RegionKind<'tcx>>); @@ -140,6 +141,10 @@ impl<'tcx> rustc_type_ir::new::Region> for Region<'tcx> { fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::BrAnon }) } + + fn new_static(tcx: TyCtxt<'tcx>) -> Self { + tcx.lifetimes.re_static + } } /// Region utilities @@ -251,6 +256,7 @@ impl<'tcx> Region<'tcx> { } ty::ReError(_) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_ERROR; } } @@ -326,7 +332,7 @@ impl<'tcx> Deref for Region<'tcx> { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, PartialOrd, Ord)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct EarlyParamRegion { pub def_id: DefId, @@ -336,7 +342,9 @@ pub struct EarlyParamRegion { impl std::fmt::Debug for EarlyParamRegion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}, {}, {}", self.def_id, self.index, self.name) + // FIXME(BoxyUwU): self.def_id goes first because of `erased-regions-in-hidden-ty.rs` being impossible to write + // error annotations for otherwise. :). Ideally this would be `self.name, self.index, self.def_id`. + write!(f, "{:?}_{}/#{}", self.def_id, self.name, self.index) } } @@ -355,7 +363,7 @@ impl Atom for RegionVid { } } -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)] +#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] /// The parameter representation of late-bound function parameters, "some region /// at least as big as the scope `fr.scope`". @@ -364,7 +372,7 @@ pub struct LateParamRegion { pub bound_region: BoundRegionKind, } -#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)] +#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] pub enum BoundRegionKind { /// An anonymous region parameter for a given fn (&T) @@ -381,13 +389,25 @@ pub enum BoundRegionKind { BrEnv, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, PartialOrd, Ord)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct BoundRegion { pub var: BoundVar, pub kind: BoundRegionKind, } +impl core::fmt::Debug for BoundRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.kind { + BoundRegionKind::BrAnon => write!(f, "{:?}", self.var), + BoundRegionKind::BrEnv => write!(f, "{:?}.Env", self.var), + BoundRegionKind::BrNamed(def, symbol) => { + write!(f, "{:?}.Named({:?}, {:?})", self.var, def, symbol) + } + } + } +} + impl BoundRegionKind { pub fn is_named(&self) -> bool { match *self { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 990e78aff8af3..7063ef07201bb 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -10,9 +10,12 @@ use crate::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; +use rustc_macros::TypeVisitable; use rustc_target::spec::abi; use std::iter; +use super::Pattern; + pub type RelateResult<'tcx, T> = Result>; pub trait TypeRelation<'tcx>: Sized { @@ -94,28 +97,6 @@ pub trait Relate<'tcx>: TypeFoldable> + PartialEq + Copy { /////////////////////////////////////////////////////////////////////////// // Relate impls -pub fn relate_type_and_mut<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - a: ty::TypeAndMut<'tcx>, - b: ty::TypeAndMut<'tcx>, - base_ty: Ty<'tcx>, -) -> RelateResult<'tcx, ty::TypeAndMut<'tcx>> { - debug!("{}.mts({:?}, {:?})", relation.tag(), a, b); - if a.mutbl != b.mutbl { - Err(TypeError::Mutability) - } else { - let mutbl = a.mutbl; - let (variance, info) = match mutbl { - hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), - hir::Mutability::Mut => { - (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: base_ty, param_index: 0 }) - } - }; - let ty = relation.relate_with_variance(variance, info, a.ty, b.ty)?; - Ok(ty::TypeAndMut { ty, mutbl }) - } -} - #[inline] pub fn relate_args_invariantly<'tcx, R: TypeRelation<'tcx>>( relation: &mut R, @@ -373,6 +354,36 @@ impl<'tcx> Relate<'tcx> for Ty<'tcx> { } } +impl<'tcx> Relate<'tcx> for Pattern<'tcx> { + #[inline] + fn relate>( + relation: &mut R, + a: Self, + b: Self, + ) -> RelateResult<'tcx, Self> { + match (&*a, &*b) { + ( + &ty::PatternKind::Range { start: start_a, end: end_a, include_end: inc_a }, + &ty::PatternKind::Range { start: start_b, end: end_b, include_end: inc_b }, + ) => { + // FIXME(pattern_types): make equal patterns equal (`0..=` is the same as `..=`). + let mut relate_opt_const = |a, b| match (a, b) { + (None, None) => Ok(None), + (Some(a), Some(b)) => relation.relate(a, b).map(Some), + // FIXME(pattern_types): report a better error + _ => Err(TypeError::Mismatch), + }; + let start = relate_opt_const(start_a, start_b)?; + let end = relate_opt_const(end_a, end_b)?; + if inc_a != inc_b { + todo!() + } + Ok(relation.tcx().mk_pat(ty::PatternKind::Range { start, end, include_end: inc_a })) + } + } + } +} + /// Relates `a` and `b` structurally, calling the relation for all nested values. /// Any semantic equality, e.g. of projections, and inference variables have to be /// handled by the caller. @@ -465,17 +476,39 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>( Ok(Ty::new_coroutine_closure(tcx, a_id, args)) } - (&ty::RawPtr(a_mt), &ty::RawPtr(b_mt)) => { - let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?; - Ok(Ty::new_ptr(tcx, mt)) + (&ty::RawPtr(a_ty, a_mutbl), &ty::RawPtr(b_ty, b_mutbl)) => { + if a_mutbl != b_mutbl { + return Err(TypeError::Mutability); + } + + let (variance, info) = match a_mutbl { + hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), + hir::Mutability::Mut => { + (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + } + }; + + let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; + + Ok(Ty::new_ptr(tcx, ty, a_mutbl)) } (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => { + if a_mutbl != b_mutbl { + return Err(TypeError::Mutability); + } + + let (variance, info) = match a_mutbl { + hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), + hir::Mutability::Mut => { + (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + } + }; + let r = relation.relate(a_r, b_r)?; - let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl }; - let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl }; - let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?; - Ok(Ty::new_ref(tcx, r, mt)) + let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; + + Ok(Ty::new_ref(tcx, r, ty, a_mutbl)) } (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => { @@ -533,6 +566,12 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>( Ok(Ty::new_alias(tcx, a_kind, alias_ty)) } + (&ty::Pat(a_ty, a_pat), &ty::Pat(b_ty, b_pat)) => { + let ty = relation.relate(a_ty, b_ty)?; + let pat = relation.relate(a_pat, b_pat)?; + Ok(Ty::new_pat(tcx, ty, pat)) + } + _ => Err(TypeError::Sorts(expected_found(a, b))), } } @@ -769,12 +808,12 @@ impl<'tcx> Relate<'tcx> for GenericArg<'tcx> { } } -impl<'tcx> Relate<'tcx> for ty::ImplPolarity { +impl<'tcx> Relate<'tcx> for ty::PredicatePolarity { fn relate>( _relation: &mut R, - a: ty::ImplPolarity, - b: ty::ImplPolarity, - ) -> RelateResult<'tcx, ty::ImplPolarity> { + a: ty::PredicatePolarity, + b: ty::PredicatePolarity, + ) -> RelateResult<'tcx, ty::PredicatePolarity> { if a != b { Err(TypeError::PolarityMismatch(expected_found(a, b))) } else { Ok(a) } } } diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index 17eabec257e93..cb2f7284eaa0d 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -1,6 +1,7 @@ use crate::middle::region::{Scope, ScopeData, ScopeTree}; use rustc_hir as hir; use rustc_hir::ItemLocalMap; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; /// `RvalueScopes` is a mapping from sub-expressions to _extended_ lifetime as determined by /// rules laid out in `rustc_hir_analysis::check::rvalue_scopes`. diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 4b015640f916e..14a77d4b37ec7 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -20,6 +20,8 @@ use std::fmt::{self, Debug}; use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Region}; +use super::Pattern; + impl fmt::Debug for ty::TraitDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { @@ -210,6 +212,22 @@ impl<'tcx> DebugWithInfcx> for AliasTy<'tcx> { } } +impl<'tcx> DebugWithInfcx> for Pattern<'tcx> { + fn fmt>>( + this: WithInfcx<'_, Infcx, &Self>, + f: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + match &**this.data { + ty::PatternKind::Range { start, end, include_end } => f + .debug_struct("Pattern::Range") + .field("start", start) + .field("end", end) + .field("include_end", include_end) + .finish(), + } + } +} + impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { WithInfcx::with_no_infcx(self).fmt(f) @@ -402,12 +420,14 @@ TrivialTypeTraversalImpls! { ::rustc_span::symbol::Symbol, ::rustc_hir::def::Res, ::rustc_hir::def_id::LocalDefId, + ::rustc_hir::ByRef, ::rustc_hir::HirId, ::rustc_hir::MatchSource, ::rustc_target::asm::InlineAsmRegOrRegClass, crate::mir::coverage::BlockMarkerId, crate::mir::coverage::CounterId, crate::mir::coverage::ExpressionId, + crate::mir::coverage::ConditionId, crate::mir::Local, crate::mir::Promoted, crate::traits::Reveal, @@ -448,6 +468,7 @@ TrivialTypeTraversalAndLiftImpls! { crate::ty::ClosureKind, crate::ty::ParamConst, crate::ty::ParamTy, + crate::ty::instance::ReifyReason, interpret::AllocId, interpret::CtfeProvenance, interpret::Scalar, @@ -539,6 +560,22 @@ impl<'tcx> TypeFoldable> for &'tcx ty::List> { } } +impl<'tcx> TypeFoldable> for Pattern<'tcx> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let pat = (*self).clone().try_fold_with(folder)?; + Ok(if pat == *self { self } else { folder.interner().mk_pat(pat) }) + } +} + +impl<'tcx> TypeVisitable> for Pattern<'tcx> { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + (**self).visit_with(visitor) + } +} + impl<'tcx> TypeFoldable> for Ty<'tcx> { fn try_fold_with>>( self, @@ -560,7 +597,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { folder: &mut F, ) -> Result { let kind = match *self.kind() { - ty::RawPtr(tm) => ty::RawPtr(tm.try_fold_with(folder)?), + ty::RawPtr(ty, mutbl) => ty::RawPtr(ty.try_fold_with(folder)?, mutbl), ty::Array(typ, sz) => ty::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?), ty::Slice(typ) => ty::Slice(typ.try_fold_with(folder)?), ty::Adt(tid, args) => ty::Adt(tid, args.try_fold_with(folder)?), @@ -584,6 +621,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::CoroutineClosure(did, args.try_fold_with(folder)?) } ty::Alias(kind, data) => ty::Alias(kind, data.try_fold_with(folder)?), + ty::Pat(ty, pat) => ty::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?), ty::Bool | ty::Char @@ -607,7 +645,7 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { match self.kind() { - ty::RawPtr(ref tm) => tm.visit_with(visitor), + ty::RawPtr(ty, _mutbl) => ty.visit_with(visitor), ty::Array(typ, sz) => { try_visit!(typ.visit_with(visitor)); sz.visit_with(visitor) @@ -631,6 +669,11 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { ty::CoroutineClosure(_did, ref args) => args.visit_with(visitor), ty::Alias(_, ref data) => data.visit_with(visitor), + ty::Pat(ty, pat) => { + try_visit!(ty.visit_with(visitor)); + pat.visit_with(visitor) + } + ty::Bool | ty::Char | ty::Str @@ -710,7 +753,19 @@ impl<'tcx> TypeSuperVisitable> for ty::Predicate<'tcx> { } } -impl<'tcx> TypeFoldable> for &'tcx ty::List> { +impl<'tcx> TypeVisitable> for ty::Clauses<'tcx> { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + visitor.visit_clauses(self) + } +} + +impl<'tcx> TypeSuperVisitable> for ty::Clauses<'tcx> { + fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { + self.as_slice().visit_with(visitor) + } +} + +impl<'tcx> TypeFoldable> for ty::Clauses<'tcx> { fn try_fold_with>>( self, folder: &mut F, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 11065b2a382be..de70c4f7b65a1 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -11,19 +11,20 @@ use crate::ty::{ }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use crate::ty::{List, ParamEnv}; -use hir::def::DefKind; +use hir::def::{CtorKind, DefKind}; use rustc_data_structures::captures::Captures; use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; -use rustc_macros::HashStable; +use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_target::spec::abi::{self, Abi}; use std::assert_matches::debug_assert_matches; use std::borrow::Cow; +use std::iter; use std::ops::{ControlFlow, Deref, Range}; use ty::util::IntTypeExt; @@ -483,7 +484,7 @@ impl<'tcx> CoroutineClosureSignature<'tcx> { self.to_coroutine( tcx, parent_args, - Ty::from_closure_kind(tcx, goal_kind), + Ty::from_coroutine_closure_kind(tcx, goal_kind), coroutine_def_id, tupled_upvars_ty, ) @@ -694,7 +695,8 @@ impl<'tcx> CoroutineArgs<'tcx> { #[inline] pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> Range { // FIXME requires optimized MIR - FIRST_VARIANT..tcx.coroutine_layout(def_id).unwrap().variant_fields.next_index() + FIRST_VARIANT + ..tcx.coroutine_layout(def_id, tcx.types.unit).unwrap().variant_fields.next_index() } /// The discriminant for the given variant. Panics if the `variant_index` is @@ -754,7 +756,7 @@ impl<'tcx> CoroutineArgs<'tcx> { def_id: DefId, tcx: TyCtxt<'tcx>, ) -> impl Iterator> + Captures<'tcx>> { - let layout = tcx.coroutine_layout(def_id).unwrap(); + let layout = tcx.coroutine_layout(def_id, self.kind_ty()).unwrap(); layout.variant_fields.iter().map(move |variant| { variant.iter().map(move |field| { ty::EarlyBinder::bind(layout.field_tys[*field].ty).instantiate(tcx, self.args) @@ -770,7 +772,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>), @@ -867,7 +869,7 @@ impl<'tcx> InlineConstArgs<'tcx> { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable)] pub enum BoundVariableKind { Ty(BoundTyKind), @@ -907,7 +909,7 @@ impl BoundVariableKind { /// e.g., `liberate_late_bound_regions`). /// /// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(HashStable, Lift)] pub struct Binder<'tcx, T> { value: T, @@ -1108,7 +1110,7 @@ where /// * For a projection, this would be `>::N<...>`. /// * For an inherent projection, this would be `Ty::N<...>`. /// * For an opaque type, there is no explicit syntax. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct AliasTy<'tcx> { /// The parameters of the associated or opaque item. @@ -1277,7 +1279,7 @@ pub struct GenSig<'tcx> { /// - `inputs`: is the list of arguments and their modes. /// - `output`: is the return type. /// - `c_variadic`: indicates whether this is a C-variadic function. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct FnSig<'tcx> { pub inputs_and_output: &'tcx List>, @@ -1378,7 +1380,7 @@ impl<'tcx> ParamTy { Ty::new_param(tcx, self.index, self.name) } - pub fn span_from_generics(&self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span { + pub fn span_from_generics(self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span { let generics = tcx.generics_of(item_with_generics); let type_param = generics.type_param(self, tcx); tcx.def_span(type_param.def_id) @@ -1402,14 +1404,14 @@ impl ParamConst { } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct BoundTy { pub var: BoundVar, pub kind: BoundTyKind, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable)] pub enum BoundTyKind { Anon, @@ -1521,6 +1523,11 @@ impl<'tcx> Ty<'tcx> { Ty::new(tcx, Alias(kind, alias_ty)) } + #[inline] + pub fn new_pat(tcx: TyCtxt<'tcx>, base: Ty<'tcx>, pat: ty::Pattern<'tcx>) -> Ty<'tcx> { + Ty::new(tcx, Pat(base, pat)) + } + #[inline] pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { Ty::new_alias(tcx, ty::Opaque, AliasTy::new(tcx, def_id, args)) @@ -1587,37 +1594,43 @@ impl<'tcx> Ty<'tcx> { } #[inline] - pub fn new_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { - Ty::new(tcx, Ref(r, tm.ty, tm.mutbl)) + pub fn new_ref( + tcx: TyCtxt<'tcx>, + r: Region<'tcx>, + ty: Ty<'tcx>, + mutbl: ty::Mutability, + ) -> Ty<'tcx> { + Ty::new(tcx, Ref(r, ty, mutbl)) } #[inline] pub fn new_mut_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - Ty::new_ref(tcx, r, TypeAndMut { ty, mutbl: hir::Mutability::Mut }) + Ty::new_ref(tcx, r, ty, hir::Mutability::Mut) } #[inline] pub fn new_imm_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - Ty::new_ref(tcx, r, TypeAndMut { ty, mutbl: hir::Mutability::Not }) + Ty::new_ref(tcx, r, ty, hir::Mutability::Not) } #[inline] - pub fn new_ptr(tcx: TyCtxt<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { - Ty::new(tcx, RawPtr(tm)) + pub fn new_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mutbl: ty::Mutability) -> Ty<'tcx> { + Ty::new(tcx, ty::RawPtr(ty, mutbl)) } #[inline] pub fn new_mut_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: hir::Mutability::Mut }) + Ty::new_ptr(tcx, ty, hir::Mutability::Mut) } #[inline] pub fn new_imm_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: hir::Mutability::Not }) + Ty::new_ptr(tcx, ty, hir::Mutability::Not) } #[inline] pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { + tcx.debug_assert_args_compatible(def.did(), args); Ty::new(tcx, Adt(def, args)) } @@ -1664,6 +1677,10 @@ impl<'tcx> Ty<'tcx> { def_id: DefId, args: impl IntoIterator>>, ) -> Ty<'tcx> { + debug_assert_matches!( + tcx.def_kind(def_id), + DefKind::AssocFn | DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) + ); let args = tcx.check_and_mk_args(def_id, args); Ty::new(tcx, FnDef(def_id, args)) } @@ -1698,11 +1715,7 @@ impl<'tcx> Ty<'tcx> { def_id: DefId, closure_args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - debug_assert_eq!( - closure_args.len(), - tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 3, - "closure constructed with incorrect generic parameters" - ); + tcx.debug_assert_args_compatible(def_id, closure_args); Ty::new(tcx, Closure(def_id, closure_args)) } @@ -1712,11 +1725,7 @@ impl<'tcx> Ty<'tcx> { def_id: DefId, closure_args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - debug_assert_eq!( - closure_args.len(), - tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 5, - "closure constructed with incorrect generic parameters" - ); + tcx.debug_assert_args_compatible(def_id, closure_args); Ty::new(tcx, CoroutineClosure(def_id, closure_args)) } @@ -1726,11 +1735,7 @@ impl<'tcx> Ty<'tcx> { def_id: DefId, coroutine_args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { - debug_assert_eq!( - coroutine_args.len(), - tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 6, - "coroutine constructed with incorrect number of generic parameters" - ); + tcx.debug_assert_args_compatible(def_id, coroutine_args); Ty::new(tcx, Coroutine(def_id, coroutine_args)) } @@ -1745,11 +1750,6 @@ impl<'tcx> Ty<'tcx> { // misc - #[inline] - pub fn new_unit(tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - tcx.types.unit - } - #[inline] pub fn new_static_str(tcx: TyCtxt<'tcx>) -> Ty<'tcx> { Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_) @@ -1910,7 +1910,7 @@ impl<'tcx> Ty<'tcx> { pub fn is_array_slice(self) -> bool { match self.kind() { Slice(_) => true, - RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_)), + ty::RawPtr(ty, _) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_)), _ => false, } } @@ -1941,7 +1941,7 @@ impl<'tcx> Ty<'tcx> { Adt(def, args) => { assert!(def.repr().simd(), "`simd_size_and_type` called on non-SIMD type"); let variant = def.non_enum_variant(); - let f0_ty = variant.fields[FieldIdx::from_u32(0)].ty(tcx, args); + let f0_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args); match f0_ty.kind() { // If the first field is an array, we assume it is the only field and its @@ -1964,11 +1964,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_mutable_ptr(self) -> bool { - matches!( - self.kind(), - RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. }) - | Ref(_, _, hir::Mutability::Mut) - ) + matches!(self.kind(), RawPtr(_, hir::Mutability::Mut) | Ref(_, _, hir::Mutability::Mut)) } /// Get the mutability of the reference or `None` when not a reference @@ -1982,7 +1978,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_unsafe_ptr(self) -> bool { - matches!(self.kind(), RawPtr(_)) + matches!(self.kind(), RawPtr(_, _)) } /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). @@ -2038,7 +2034,7 @@ impl<'tcx> Ty<'tcx> { | Uint(_) | FnDef(..) | FnPtr(_) - | RawPtr(_) + | RawPtr(_, _) | Infer(IntVar(_) | FloatVar(_)) ) } @@ -2174,7 +2170,7 @@ impl<'tcx> Ty<'tcx> { Some(TypeAndMut { ty: self.boxed_ty(), mutbl: hir::Mutability::Not }) } Ref(_, ty, mutbl) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }), - RawPtr(mt) if explicit => Some(*mt), + RawPtr(ty, mutbl) if explicit => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }), _ => None, } } @@ -2231,7 +2227,7 @@ impl<'tcx> Ty<'tcx> { pub fn tuple_fields(self) -> &'tcx List> { match self.kind() { Tuple(args) => args, - _ => bug!("tuple_fields called on non-tuple"), + _ => bug!("tuple_fields called on non-tuple: {self:?}"), } } @@ -2283,6 +2279,8 @@ impl<'tcx> Ty<'tcx> { Ty::new_projection(tcx, assoc_items[0], tcx.mk_args(&[self.into()])) } + ty::Pat(ty, _) => ty.discriminant_ty(tcx), + ty::Bool | ty::Char | ty::Int(_) @@ -2293,7 +2291,7 @@ impl<'tcx> Ty<'tcx> { | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) @@ -2314,6 +2312,133 @@ impl<'tcx> Ty<'tcx> { } } + /// Returns the type of the async destructor of this type. + pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> { + if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) { + return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop) + .instantiate_identity(); + } + match *self.kind() { + ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => { + let assoc_items = tcx + .associated_item_def_ids(tcx.require_lang_item(LangItem::AsyncDestruct, None)); + Ty::new_projection(tcx, assoc_items[0], [self]) + } + + ty::Array(elem_ty, _) | ty::Slice(elem_ty) => { + let dtor = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropSlice) + .instantiate(tcx, &[elem_ty.into()]); + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) + .instantiate(tcx, &[dtor.into()]) + } + + ty::Adt(adt_def, args) if adt_def.is_enum() || adt_def.is_struct() => self + .adt_async_destructor_ty( + tcx, + adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))), + param_env, + ), + ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env), + ty::Closure(_, args) => self.adt_async_destructor_ty( + tcx, + iter::once(args.as_closure().upvar_tys()), + param_env, + ), + ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty( + tcx, + iter::once(args.as_coroutine_closure().upvar_tys()), + param_env, + ), + + ty::Adt(adt_def, _) => { + assert!(adt_def.is_union()); + + let surface_drop = self.surface_async_dropper_ty(tcx, param_env).unwrap(); + + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) + .instantiate(tcx, &[surface_drop.into()]) + } + + ty::Bound(..) + | ty::Foreign(_) + | ty::Placeholder(_) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("`async_destructor_ty` applied to unexpected type: {self:?}") + } + + _ => bug!("`async_destructor_ty` is not yet implemented for type: {self:?}"), + } + } + + fn adt_async_destructor_ty( + self, + tcx: TyCtxt<'tcx>, + variants: I, + param_env: ParamEnv<'tcx>, + ) -> Ty<'tcx> + where + I: Iterator + ExactSizeIterator, + I::Item: IntoIterator>, + { + debug_assert!(!self.is_async_destructor_noop(tcx, param_env)); + + let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer); + let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain); + + let noop = + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity(); + let either = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropEither); + + let variants_dtor = variants + .into_iter() + .map(|variant| { + variant + .into_iter() + .map(|ty| defer.instantiate(tcx, &[ty.into()])) + .reduce(|acc, next| chain.instantiate(tcx, &[acc.into(), next.into()])) + .unwrap_or(noop) + }) + .reduce(|other, matched| { + either.instantiate(tcx, &[other.into(), matched.into(), self.into()]) + }) + .unwrap(); + + let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx, param_env) { + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain) + .instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()]) + } else { + variants_dtor + }; + + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) + .instantiate(tcx, &[dtor.into()]) + } + + fn surface_async_dropper_ty( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ) -> Option> { + if self.has_surface_async_drop(tcx, param_env) { + Some(LangItem::SurfaceAsyncDropInPlace) + } else if self.has_surface_drop(tcx, param_env) { + Some(LangItem::AsyncDropSurfaceDropInPlace) + } else { + None + } + .map(|dropper| { + Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()]) + }) + } + + fn async_destructor_combinator( + tcx: TyCtxt<'tcx>, + lang_item: LangItem, + ) -> ty::EarlyBinder> { + tcx.fn_sig(tcx.require_lang_item(lang_item, None)) + .map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap()) + } + /// Returns the type of metadata for (potentially fat) pointers to this type, /// or the struct tail if the metadata type cannot be determined. pub fn ptr_metadata_ty_or_tail( @@ -2364,6 +2489,7 @@ impl<'tcx> Ty<'tcx> { ty::Param(_) | ty::Alias(..) => Err(tail), ty::Infer(ty::TyVar(_)) + | ty::Pat(..) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!( @@ -2456,6 +2582,25 @@ impl<'tcx> Ty<'tcx> { } } + /// Like [`Ty::to_opt_closure_kind`], but it caps the "maximum" closure kind + /// to `FnMut`. This is because although we have three capability states, + /// `AsyncFn`/`AsyncFnMut`/`AsyncFnOnce`, we only need to distinguish two coroutine + /// bodies: by-ref and by-value. + /// + /// See the definition of `AsyncFn` and `AsyncFnMut` and the `CallRefFuture` + /// associated type for why we don't distinguish [`ty::ClosureKind::Fn`] and + /// [`ty::ClosureKind::FnMut`] for the purpose of the generated MIR bodies. + /// + /// This method should be used when constructing a `Coroutine` out of a + /// `CoroutineClosure`, when the `Coroutine`'s `kind` field is being populated + /// directly from the `CoroutineClosure`'s `kind`. + pub fn from_coroutine_closure_kind(tcx: TyCtxt<'tcx>, kind: ty::ClosureKind) -> Ty<'tcx> { + match kind { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => tcx.types.i16, + ty::ClosureKind::FnOnce => tcx.types.i32, + } + } + /// Fast path helper for testing if a type is `Sized`. /// /// Returning true means the type is known to be sized. Returning @@ -2481,16 +2626,20 @@ impl<'tcx> Ty<'tcx> { | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Array(..) + | ty::Pat(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never - | ty::Error(_) => true, + | ty::Error(_) + | ty::Dynamic(_, _, ty::DynStar) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => false, + ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, - ty::Tuple(tys) => tys.iter().all(|ty| ty.is_trivially_sized(tcx)), + ty::Tuple(tys) => tys.last().map_or(true, |ty| ty.is_trivially_sized(tcx)), - ty::Adt(def, _args) => def.sized_constraint(tcx).skip_binder().is_empty(), + ty::Adt(def, args) => def + .sized_constraint(tcx) + .map_or(true, |ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)), ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false, @@ -2532,6 +2681,8 @@ impl<'tcx> Ty<'tcx> { field_tys.len() <= 3 && field_tys.iter().all(Self::is_trivially_pure_clone_copy) } + ty::Pat(ty, _) => ty.is_trivially_pure_clone_copy(), + // Sometimes traits aren't implemented for every ABI or arity, // because we can't be generic over everything yet. ty::FnPtr(..) => false, @@ -2613,8 +2764,9 @@ impl<'tcx> Ty<'tcx> { | Foreign(_) | Str | Array(_, _) + | Pat(_, _) | Slice(_) - | RawPtr(_) + | RawPtr(_, _) | Ref(_, _, _) | FnDef(_, _) | FnPtr(_) @@ -2637,7 +2789,7 @@ impl<'tcx> Ty<'tcx> { /// a miscompilation or unsoundness. /// /// When in doubt, use `VarianceDiagInfo::default()` -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum VarianceDiagInfo<'tcx> { /// No additional information - this is the default. /// We will not add any additional information to error messages. @@ -2668,7 +2820,7 @@ impl<'tcx> VarianceDiagInfo<'tcx> { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index d85b541d36386..cf5decffea92b 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,5 +1,5 @@ use crate::traits::specialization_graph; -use crate::ty::fast_reject::{self, SimplifiedType, TreatParams, TreatProjections}; +use crate::ty::fast_reject::{self, SimplifiedType, TreatParams}; use crate::ty::{Ident, Ty, TyCtxt}; use hir::def_id::LOCAL_CRATE; use rustc_hir as hir; @@ -8,7 +8,7 @@ use std::iter; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; -use rustc_macros::HashStable; +use rustc_macros::{Decodable, Encodable, HashStable}; /// A trait's definition with type information. #[derive(HashStable, Encodable, Decodable)] @@ -135,21 +135,6 @@ impl<'tcx> TyCtxt<'tcx> { self, trait_def_id: DefId, self_ty: Ty<'tcx>, - f: impl FnMut(DefId), - ) { - self.for_each_relevant_impl_treating_projections( - trait_def_id, - self_ty, - TreatProjections::ForLookup, - f, - ) - } - - pub fn for_each_relevant_impl_treating_projections( - self, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - treat_projections: TreatProjections, mut f: impl FnMut(DefId), ) { // FIXME: This depends on the set of all impls for the trait. That is @@ -163,17 +148,13 @@ impl<'tcx> TyCtxt<'tcx> { f(impl_def_id); } - // Note that we're using `TreatParams::ForLookup` to query `non_blanket_impls` while using - // `TreatParams::AsCandidateKey` while actually adding them. - let treat_params = match treat_projections { - TreatProjections::NextSolverLookup => TreatParams::NextSolverLookup, - TreatProjections::ForLookup => TreatParams::ForLookup, - }; // This way, when searching for some impl for `T: Trait`, we do not look at any impls // whose outer level is not a parameter or projection. Especially for things like // `T: Clone` this is incredibly useful as we would otherwise look at all the impls // of `Clone` for `Option`, `Vec`, `ConcreteType` and so on. - if let Some(simp) = fast_reject::simplify_type(self, self_ty, treat_params) { + // Note that we're using `TreatParams::ForLookup` to query `non_blanket_impls` while using + // `TreatParams::AsCandidateKey` while actually adding them. + if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::ForLookup) { if let Some(impls) = impls.non_blanket_impls.get(&simp) { for &impl_def_id in impls { f(impl_def_id); diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 4287b382604ee..41f417dfd4b1a 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -3,24 +3,22 @@ use crate::{ infer::canonical::Canonical, traits::ObligationCause, ty::{ - self, tls, BindingMode, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, - GenericArgKind, GenericArgs, GenericArgsRef, Ty, UserArgs, + self, tls, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, GenericArgKind, + GenericArgs, GenericArgsRef, Ty, UserArgs, }, }; -use rustc_data_structures::{ - fx::FxIndexMap, - unord::{ExtendUnord, UnordItems, UnordSet}, -}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::unord::{ExtendUnord, UnordItems, UnordSet}; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::{ + self as hir, def::{DefKind, Res}, def_id::{DefId, LocalDefId, LocalDefIdMap}, hir_id::OwnerId, - HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, + BindingMode, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability, }; -use rustc_index::{Idx, IndexVec}; -use rustc_macros::HashStable; +use rustc_index::IndexVec; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_middle::mir::FakeReadCause; use rustc_session::Session; use rustc_span::Span; @@ -78,7 +76,7 @@ pub struct TypeckResults<'tcx> { adjustments: ItemLocalMap>>, - /// Stores the actual binding mode for all instances of hir::BindingAnnotation. + /// Stores the actual binding mode for all instances of [`BindingMode`]. pat_binding_modes: ItemLocalMap, /// Stores the types which were implicitly dereferenced in pattern binding modes @@ -96,6 +94,10 @@ pub struct TypeckResults<'tcx> { /// pat_adjustments: ItemLocalMap>>, + /// Set of reference patterns that match against a match-ergonomics inserted reference + /// (as opposed to against a reference in the scrutinee type). + skipped_ref_pats: ItemLocalSet, + /// Records the reasons that we picked the kind of each closure; /// not all closures are present in the map. closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>, @@ -188,7 +190,7 @@ pub struct TypeckResults<'tcx> { /// we never capture `t`. This becomes an issue when we build MIR as we require /// information on `t` in order to create place `t.0` and `t.1`. We can solve this /// issue by fake reading `t`. - pub closure_fake_reads: LocalDefIdMap, FakeReadCause, hir::HirId)>>, + pub closure_fake_reads: LocalDefIdMap, FakeReadCause, HirId)>>, /// Tracks the rvalue scoping rules which defines finer scoping for rvalue expressions /// by applying extended parameter rules. @@ -197,8 +199,7 @@ pub struct TypeckResults<'tcx> { /// Stores the predicates that apply on coroutine witness types. /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs - pub coroutine_interior_predicates: - LocalDefIdMap, ObligationCause<'tcx>)>>, + pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, /// We sometimes treat byte string literals (which are of type `&[u8; N]`) /// as `&[u8]`, depending on the pattern in which they are used. @@ -228,6 +229,7 @@ impl<'tcx> TypeckResults<'tcx> { adjustments: Default::default(), pat_binding_modes: Default::default(), pat_adjustments: Default::default(), + skipped_ref_pats: Default::default(), closure_kind_origins: Default::default(), liberated_fn_sigs: Default::default(), fru_field_types: Default::default(), @@ -238,7 +240,7 @@ impl<'tcx> TypeckResults<'tcx> { closure_min_captures: Default::default(), closure_fake_reads: Default::default(), rvalue_scopes: Default::default(), - coroutine_interior_predicates: Default::default(), + coroutine_stalled_predicates: Default::default(), treat_byte_string_as_slice: Default::default(), closure_size_eval: Default::default(), offset_of_data: Default::default(), @@ -246,7 +248,7 @@ impl<'tcx> TypeckResults<'tcx> { } /// Returns the final resolution of a `QPath` in an `Expr` or `Pat` node. - pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { + pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: HirId) -> Res { match *qpath { hir::QPath::Resolved(_, path) => path.res, hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self @@ -284,11 +286,11 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.field_indices } } - pub fn field_index(&self, id: hir::HirId) -> FieldIdx { + pub fn field_index(&self, id: HirId) -> FieldIdx { self.field_indices().get(id).cloned().expect("no index for a field") } - pub fn opt_field_index(&self, id: hir::HirId) -> Option { + pub fn opt_field_index(&self, id: HirId) -> Option { self.field_indices().get(id).cloned() } @@ -300,7 +302,7 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.nested_fields } } - pub fn nested_field_tys_and_indices(&self, id: hir::HirId) -> &[(Ty<'tcx>, FieldIdx)] { + pub fn nested_field_tys_and_indices(&self, id: HirId) -> &[(Ty<'tcx>, FieldIdx)] { self.nested_fields().get(id).map_or(&[], Vec::as_slice) } @@ -322,13 +324,13 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_types } } - pub fn node_type(&self, id: hir::HirId) -> Ty<'tcx> { + pub fn node_type(&self, id: HirId) -> Ty<'tcx> { self.node_type_opt(id).unwrap_or_else(|| { bug!("node_type: no type for node {}", tls::with(|tcx| tcx.hir().node_to_string(id))) }) } - pub fn node_type_opt(&self, id: hir::HirId) -> Option> { + pub fn node_type_opt(&self, id: HirId) -> Option> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.node_types.get(&id.local_id).cloned() } @@ -337,12 +339,12 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_args } } - pub fn node_args(&self, id: hir::HirId) -> GenericArgsRef<'tcx> { + pub fn node_args(&self, id: HirId) -> GenericArgsRef<'tcx> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.node_args.get(&id.local_id).cloned().unwrap_or_else(|| GenericArgs::empty()) } - pub fn node_args_opt(&self, id: hir::HirId) -> Option> { + pub fn node_args_opt(&self, id: HirId) -> Option> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.node_args.get(&id.local_id).cloned() } @@ -430,6 +432,39 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } + pub fn skipped_ref_pats(&self) -> LocalSetInContext<'_> { + LocalSetInContext { hir_owner: self.hir_owner, data: &self.skipped_ref_pats } + } + + pub fn skipped_ref_pats_mut(&mut self) -> LocalSetInContextMut<'_> { + LocalSetInContextMut { hir_owner: self.hir_owner, data: &mut self.skipped_ref_pats } + } + + /// Does the pattern recursively contain a `ref mut` binding in it? + /// + /// This is used to determined whether a `deref` pattern should emit a `Deref` + /// or `DerefMut` call for its pattern scrutinee. + /// + /// This is computed from the typeck results since we want to make + /// sure to apply any match-ergonomics adjustments, which we cannot + /// determine from the HIR alone. + pub fn pat_has_ref_mut_binding(&self, pat: &hir::Pat<'_>) -> bool { + let mut has_ref_mut = false; + pat.walk(|pat| { + if let hir::PatKind::Binding(_, id, _, _) = pat.kind + && let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) = + self.pat_binding_modes().get(id) + { + has_ref_mut = true; + // No need to continue recursing + false + } else { + true + } + }); + has_ref_mut + } + /// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured /// by the closure. pub fn closure_min_captures_flattened( @@ -469,7 +504,7 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.fru_field_types } } - pub fn is_coercion_cast(&self, hir_id: hir::HirId) -> bool { + pub fn is_coercion_cast(&self, hir_id: HirId) -> bool { validate_hir_id_for_typeck_results(self.hir_owner, hir_id); self.coercion_casts.contains(&hir_id.local_id) } @@ -503,7 +538,7 @@ impl<'tcx> TypeckResults<'tcx> { /// would result in lookup errors, or worse, in silently wrong data being /// stored/returned. #[inline] -fn validate_hir_id_for_typeck_results(hir_owner: OwnerId, hir_id: hir::HirId) { +fn validate_hir_id_for_typeck_results(hir_owner: OwnerId, hir_id: HirId) { if hir_id.owner != hir_owner { invalid_hir_id_for_typeck_results(hir_owner, hir_id); } @@ -511,7 +546,7 @@ fn validate_hir_id_for_typeck_results(hir_owner: OwnerId, hir_id: hir::HirId) { #[cold] #[inline(never)] -fn invalid_hir_id_for_typeck_results(hir_owner: OwnerId, hir_id: hir::HirId) { +fn invalid_hir_id_for_typeck_results(hir_owner: OwnerId, hir_id: HirId) { ty::tls::with(|tcx| { bug!( "node {} cannot be placed in TypeckResults with hir_owner {:?}", @@ -527,12 +562,12 @@ pub struct LocalTableInContext<'a, V> { } impl<'a, V> LocalTableInContext<'a, V> { - pub fn contains_key(&self, id: hir::HirId) -> bool { + pub fn contains_key(&self, id: HirId) -> bool { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.contains_key(&id.local_id) } - pub fn get(&self, id: hir::HirId) -> Option<&'a V> { + pub fn get(&self, id: HirId) -> Option<&'a V> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.get(&id.local_id) } @@ -549,10 +584,10 @@ impl<'a, V> LocalTableInContext<'a, V> { } } -impl<'a, V> ::std::ops::Index for LocalTableInContext<'a, V> { +impl<'a, V> ::std::ops::Index for LocalTableInContext<'a, V> { type Output = V; - fn index(&self, key: hir::HirId) -> &V { + fn index(&self, key: HirId) -> &V { self.get(key).expect("LocalTableInContext: key not found") } } @@ -563,30 +598,32 @@ pub struct LocalTableInContextMut<'a, V> { } impl<'a, V> LocalTableInContextMut<'a, V> { - pub fn get_mut(&mut self, id: hir::HirId) -> Option<&mut V> { + pub fn get_mut(&mut self, id: HirId) -> Option<&mut V> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.get_mut(&id.local_id) } - pub fn entry(&mut self, id: hir::HirId) -> Entry<'_, hir::ItemLocalId, V> { + pub fn get(&mut self, id: HirId) -> Option<&V> { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.get(&id.local_id) + } + + pub fn entry(&mut self, id: HirId) -> Entry<'_, hir::ItemLocalId, V> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.entry(id.local_id) } - pub fn insert(&mut self, id: hir::HirId, val: V) -> Option { + pub fn insert(&mut self, id: HirId, val: V) -> Option { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.insert(id.local_id, val) } - pub fn remove(&mut self, id: hir::HirId) -> Option { + pub fn remove(&mut self, id: HirId) -> Option { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.remove(&id.local_id) } - pub fn extend( - &mut self, - items: UnordItems<(hir::HirId, V), impl Iterator>, - ) { + pub fn extend(&mut self, items: UnordItems<(HirId, V), impl Iterator>) { self.data.extend_unord(items.map(|(id, value)| { validate_hir_id_for_typeck_results(self.hir_owner, id); (id.local_id, value) @@ -594,6 +631,49 @@ impl<'a, V> LocalTableInContextMut<'a, V> { } } +#[derive(Clone, Copy, Debug)] +pub struct LocalSetInContext<'a> { + hir_owner: OwnerId, + data: &'a ItemLocalSet, +} + +impl<'a> LocalSetInContext<'a> { + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn contains(&self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.contains(&id.local_id) + } +} + +#[derive(Debug)] +pub struct LocalSetInContextMut<'a> { + hir_owner: OwnerId, + data: &'a mut ItemLocalSet, +} + +impl<'a> LocalSetInContextMut<'a> { + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn contains(&self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.contains(&id.local_id) + } + pub fn insert(&mut self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.insert(id.local_id) + } + + pub fn remove(&mut self, id: hir::HirId) -> bool { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.data.remove(&id.local_id) + } +} + rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] @@ -645,7 +725,7 @@ impl<'tcx> IsIdentity for CanonicalUserType<'tcx> { return false; } - iter::zip(user_args.args, BoundVar::new(0)..).all(|(kind, cvar)| { + iter::zip(user_args.args, BoundVar::ZERO..).all(|(kind, cvar)| { match kind.unpack() { GenericArgKind::Type(ty) => match ty.kind() { ty::Bound(debruijn, b) => { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a6526b068517b..f5e973f85da12 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -17,12 +17,12 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_index::bit_set::GrowableBitSet; -use rustc_macros::HashStable; +use rustc_macros::{extension, HashStable, TyDecodable, TyEncodable}; use rustc_session::Limit; use rustc_span::sym; use rustc_target::abi::{Integer, IntegerType, Primitive, Size}; use rustc_target::spec::abi::Abi; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use std::{fmt, iter}; #[derive(Copy, Clone, Debug)] @@ -245,6 +245,11 @@ impl<'tcx> TyCtxt<'tcx> { ty::Tuple(_) => break, + ty::Pat(inner, _) => { + f(); + ty = inner; + } + ty::Alias(..) => { let normalized = normalize(ty); if ty == normalized { @@ -427,19 +432,19 @@ impl<'tcx> TyCtxt<'tcx> { .filter(|&(_, k)| { match k.unpack() { GenericArgKind::Lifetime(region) => match region.kind() { - ty::ReEarlyParam(ref ebr) => { + ty::ReEarlyParam(ebr) => { !impl_generics.region_param(ebr, self).pure_wrt_drop } // Error: not a region param _ => false, }, - GenericArgKind::Type(ty) => match ty.kind() { - ty::Param(ref pt) => !impl_generics.type_param(pt, self).pure_wrt_drop, + GenericArgKind::Type(ty) => match *ty.kind() { + ty::Param(pt) => !impl_generics.type_param(pt, self).pure_wrt_drop, // Error: not a type param _ => false, }, GenericArgKind::Const(ct) => match ct.kind() { - ty::ConstKind::Param(ref pc) => { + ty::ConstKind::Param(pc) => { !impl_generics.const_param(pc, self).pure_wrt_drop } // Error: not a const param @@ -682,6 +687,9 @@ impl<'tcx> TyCtxt<'tcx> { /// Return the set of types that should be taken into account when checking /// trait bounds on a coroutine's internal state. + // FIXME(compiler-errors): We should remove this when the old solver goes away; + // and all other usages of this function should go through `bound_coroutine_hidden_types` + // instead. pub fn coroutine_hidden_types( self, def_id: DefId, @@ -694,6 +702,33 @@ impl<'tcx> TyCtxt<'tcx> { .map(|decl| ty::EarlyBinder::bind(decl.ty)) } + /// Return the set of types that should be taken into account when checking + /// trait bounds on a coroutine's internal state. This properly replaces + /// `ReErased` with new existential bound lifetimes. + pub fn bound_coroutine_hidden_types( + self, + def_id: DefId, + ) -> impl Iterator>>> { + let coroutine_layout = self.mir_coroutine_witnesses(def_id); + coroutine_layout + .as_ref() + .map_or_else(|| [].iter(), |l| l.field_tys.iter()) + .filter(|decl| !decl.ignore_for_traits) + .map(move |decl| { + let mut vars = vec![]; + let ty = self.fold_regions(decl.ty, |re, debruijn| { + assert_eq!(re, self.lifetimes.re_erased); + let var = ty::BoundVar::from_usize(vars.len()); + vars.push(ty::BoundVariableKind::Region(ty::BrAnon)); + ty::Region::new_bound(self, debruijn, ty::BoundRegion { var, kind: ty::BrAnon }) + }); + ty::EarlyBinder::bind(ty::Binder::bind_with_vars( + ty, + self.mk_bound_variable_kinds(&vars), + )) + }) + } + /// Expands the given impl trait type, stopping if the type is recursive. #[instrument(skip(self), level = "debug", ret)] pub fn try_expand_impl_trait_type( @@ -998,8 +1033,10 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { Some(expanded_ty) => *expanded_ty, None => { if matches!(self.inspect_coroutine_fields, InspectCoroutineFields::Yes) { - for bty in self.tcx.coroutine_hidden_types(def_id) { - let hidden_ty = bty.instantiate(self.tcx, args); + for bty in self.tcx.bound_coroutine_hidden_types(def_id) { + let hidden_ty = self.tcx.instantiate_bound_regions_with_erased( + bty.instantiate(self.tcx, args), + ); self.fold_ty(hidden_ty); } } @@ -1205,12 +1242,12 @@ impl<'tcx> Ty<'tcx> { | ty::Str | ty::Never | ty::Ref(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::FnDef(..) | ty::Error(_) | ty::FnPtr(_) => true, ty::Tuple(fields) => fields.iter().all(Self::is_trivially_freeze), - ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_freeze(), + ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_freeze(), ty::Adt(..) | ty::Bound(..) | ty::Closure(..) @@ -1245,12 +1282,12 @@ impl<'tcx> Ty<'tcx> { | ty::Str | ty::Never | ty::Ref(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::FnDef(..) | ty::Error(_) | ty::FnPtr(_) => true, ty::Tuple(fields) => fields.iter().all(Self::is_trivially_unpin), - ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_unpin(), + ty::Pat(ty, _) | ty::Slice(ty) | ty::Array(ty, _) => ty.is_trivially_unpin(), ty::Adt(..) | ty::Bound(..) | ty::Closure(..) @@ -1266,6 +1303,98 @@ impl<'tcx> Ty<'tcx> { } } + /// Checks whether values of this type `T` implements the `AsyncDrop` + /// trait. + pub fn has_surface_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + self.could_have_surface_async_drop() && tcx.has_surface_async_drop_raw(param_env.and(self)) + } + + /// Fast path helper for testing if a type has `AsyncDrop` + /// implementation. + /// + /// Returning `false` means the type is known to not have `AsyncDrop` + /// implementation. Returning `true` means nothing -- could be + /// `AsyncDrop`, might not be. + fn could_have_surface_async_drop(self) -> bool { + !self.is_async_destructor_trivially_noop() + && !matches!( + self.kind(), + ty::Tuple(_) + | ty::Slice(_) + | ty::Array(_, _) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + ) + } + + /// Checks whether values of this type `T` implements the `Drop` + /// trait. + pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + self.could_have_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self)) + } + + /// Fast path helper for testing if a type has `Drop` implementation. + /// + /// Returning `false` means the type is known to not have `Drop` + /// implementation. Returning `true` means nothing -- could be + /// `Drop`, might not be. + fn could_have_surface_drop(self) -> bool { + !self.is_async_destructor_trivially_noop() + && !matches!( + self.kind(), + ty::Tuple(_) + | ty::Slice(_) + | ty::Array(_, _) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + ) + } + + /// Checks whether values of this type `T` implement has noop async destructor. + // + // FIXME: implement optimization to make ADTs, which do not need drop, + // to skip fields or to have noop async destructor. + pub fn is_async_destructor_noop( + self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + self.is_async_destructor_trivially_noop() + || if let ty::Adt(adt_def, _) = self.kind() { + (adt_def.is_union() || adt_def.is_payloadfree()) + && !self.has_surface_async_drop(tcx, param_env) + && !self.has_surface_drop(tcx, param_env) + } else { + false + } + } + + /// Fast path helper for testing if a type has noop async destructor. + /// + /// Returning `true` means the type is known to have noop async destructor + /// implementation. Returning `true` means nothing -- could be + /// `Drop`, might not be. + fn is_async_destructor_trivially_noop(self) -> bool { + match self.kind() { + ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Never + | ty::Ref(..) + | ty::RawPtr(..) + | ty::FnDef(..) + | ty::FnPtr(_) => true, + ty::Tuple(tys) => tys.is_empty(), + ty::Adt(adt_def, _) => adt_def.is_manually_drop(), + _ => false, + } + } + /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely /// non-copy and *might* have a destructor attached; if it returns /// `false`, then `ty` definitely has no destructor (i.e., no drop glue). @@ -1366,10 +1495,10 @@ impl<'tcx> Ty<'tcx> { // // Because this function is "shallow", we return `true` for these composites regardless // of the type(s) contained within. - ty::Ref(..) | ty::Array(..) | ty::Slice(_) | ty::Tuple(..) => true, + ty::Pat(..) | ty::Ref(..) | ty::Array(..) | ty::Slice(_) | ty::Tuple(..) => true, // Raw pointers use bitwise comparison. - ty::RawPtr(_) | ty::FnPtr(_) => true, + ty::RawPtr(_, _) | ty::FnPtr(_) => true, // Floating point numbers are not `Eq`. ty::Float(_) => false, @@ -1462,7 +1591,7 @@ impl<'tcx> ExplicitSelf<'tcx> { match *self_arg_ty.kind() { _ if is_self_ty(self_arg_ty) => ByValue, ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => ByRawPointer(mutbl), + ty::RawPtr(ty, mutbl) if is_self_ty(ty) => ByRawPointer(mutbl), ty::Adt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox, _ => Other, } @@ -1487,7 +1616,7 @@ pub fn needs_drop_components<'tcx>( | ty::FnDef(..) | ty::FnPtr(_) | ty::Char - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Str => Ok(SmallVec::new()), @@ -1496,7 +1625,7 @@ pub fn needs_drop_components<'tcx>( ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop), - ty::Slice(ty) => needs_drop_components(tcx, ty), + ty::Pat(ty, _) | ty::Slice(ty) => needs_drop_components(tcx, ty), ty::Array(elem_ty, size) => { match needs_drop_components(tcx, elem_ty) { Ok(v) if v.is_empty() => Ok(v), @@ -1542,7 +1671,7 @@ pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool { | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Str - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) @@ -1565,7 +1694,7 @@ pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool { | ty::CoroutineWitness(..) | ty::Adt(..) => false, - ty::Array(ty, _) | ty::Slice(ty) => is_trivially_const_drop(ty), + ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => is_trivially_const_drop(ty), ty::Tuple(tys) => tys.iter().all(|ty| is_trivially_const_drop(ty)), } @@ -1576,16 +1705,18 @@ pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool { /// let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); /// folder.tcx().intern_*(&v) /// ``` -pub fn fold_list<'tcx, F, T>( - list: &'tcx ty::List, +pub fn fold_list<'tcx, F, L, T>( + list: L, folder: &mut F, - intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> &'tcx ty::List, -) -> Result<&'tcx ty::List, F::Error> + intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> L, +) -> Result where F: FallibleTypeFolder>, + L: AsRef<[T]>, T: TypeFoldable> + PartialEq + Copy, { - let mut iter = list.iter(); + let slice = list.as_ref(); + let mut iter = slice.iter().copied(); // Look for the first element that changed match iter.by_ref().enumerate().find_map(|(i, t)| match t.try_fold_with(folder) { Ok(new_t) if new_t == t => None, @@ -1593,8 +1724,8 @@ where }) { Some((i, Ok(new_t))) => { // An element changed, prepare to intern the resulting list - let mut new_list = SmallVec::<[_; 8]>::with_capacity(list.len()); - new_list.extend_from_slice(&list[..i]); + let mut new_list = SmallVec::<[_; 8]>::with_capacity(slice.len()); + new_list.extend_from_slice(&slice[..i]); new_list.push(new_t); for t in iter { new_list.push(t.try_fold_with(folder)?) @@ -1615,8 +1746,8 @@ pub struct AlwaysRequiresDrop; /// with their underlying types. pub fn reveal_opaque_types_in_bounds<'tcx>( tcx: TyCtxt<'tcx>, - val: &'tcx ty::List>, -) -> &'tcx ty::List> { + val: ty::Clauses<'tcx>, +) -> ty::Clauses<'tcx> { let mut visitor = OpaqueTypeExpander { seen_opaque_tys: FxHashSet::default(), expanded_cache: FxHashMap::default(), @@ -1645,10 +1776,15 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { .any(|items| items.iter().any(|item| item.has_name(sym::notable_trait))) } -/// Determines whether an item is an intrinsic (which may be via Abi or via the `rustc_intrinsic` attribute) +/// Determines whether an item is an intrinsic (which may be via Abi or via the `rustc_intrinsic` attribute). +/// +/// We double check the feature gate here because whether a function may be defined as an intrinsic causes +/// the compiler to make some assumptions about its shape; if the user doesn't use a feature gate, they may +/// cause an ICE that we otherwise may want to prevent. pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { - if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic) - || tcx.has_attr(def_id, sym::rustc_intrinsic) + if (matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic) + && tcx.features().intrinsics) + || (tcx.has_attr(def_id, sym::rustc_intrinsic) && tcx.features().rustc_attrs) { Some(ty::IntrinsicDef { name: tcx.item_name(def_id.into()), diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 62f41921d888a..2ad194313100a 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -3,6 +3,7 @@ use std::fmt; use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar}; use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; use rustc_ast::Mutability; +use rustc_macros::HashStable; #[derive(Clone, Copy, PartialEq, HashStable)] pub enum VtblEntry<'tcx> { diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 46c26241c3e78..089e61749c3e8 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -4,7 +4,7 @@ use crate::ty::{self, Ty}; use crate::ty::{GenericArg, GenericArgKind}; use rustc_data_structures::sso::SsoHashSet; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; // The TypeWalker's stack is hot enough that it's worth going to some effort to // avoid heap allocations. @@ -151,6 +151,15 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) | ty::Bound(..) | ty::Foreign(..) => {} + ty::Pat(ty, pat) => { + match *pat { + ty::PatternKind::Range { start, end, include_end: _ } => { + stack.extend(end.map(Into::into)); + stack.extend(start.map(Into::into)); + } + } + stack.push(ty.into()); + } ty::Array(ty, len) => { stack.push(len.into()); stack.push(ty.into()); @@ -158,8 +167,8 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) ty::Slice(ty) => { stack.push(ty.into()); } - ty::RawPtr(mt) => { - stack.push(mt.ty.into()); + ty::RawPtr(ty, _) => { + stack.push(ty.into()); } ty::Ref(lt, ty, _) => { stack.push(ty.into()); diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs index a67ec99158210..43853a108960f 100644 --- a/compiler/rustc_middle/src/util/bug.rs +++ b/compiler/rustc_middle/src/util/bug.rs @@ -28,14 +28,17 @@ fn opt_span_bug_fmt>( args: fmt::Arguments<'_>, location: &Location<'_>, ) -> ! { - tls::with_opt(move |tcx| { - let msg = format!("{location}: {args}"); - match (tcx, span) { - (Some(tcx), Some(span)) => tcx.dcx().span_bug(span, msg), - (Some(tcx), None) => tcx.dcx().bug(msg), - (None, _) => panic_any(msg), - } - }) + tls::with_opt( + #[track_caller] + move |tcx| { + let msg = format!("{location}: {args}"); + match (tcx, span) { + (Some(tcx), Some(span)) => tcx.dcx().span_bug(span, msg), + (Some(tcx), None) => tcx.dcx().bug(msg), + (None, _) => panic_any(msg), + } + }, + ) } /// A query to trigger a delayed bug. Clearly, if one has a `tcx` one can already trigger a diff --git a/compiler/rustc_middle/src/util/common.rs b/compiler/rustc_middle/src/util/common.rs index dd3a36c7bf853..2038d3f84484e 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(accu: &Lock, 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(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_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index f7a3879a7d40c..5c17c0b3088fe 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -106,8 +106,8 @@ impl<'tcx> Value> for Representability { representable_ids.insert(def_id); } } - recursive_type_error(tcx, item_and_field_ids, &representable_ids); - Representability::Infinite + let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids); + Representability::Infinite(guar) } } @@ -268,7 +268,7 @@ pub fn recursive_type_error( tcx: TyCtxt<'_>, mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>, representable_ids: &FxHashSet, -) { +) -> ErrorGuaranteed { const ITEM_LIMIT: usize = 5; // Rotate the cycle so that the item with the lowest span is first @@ -344,7 +344,7 @@ pub fn recursive_type_error( suggestion, Applicability::HasPlaceholders, ) - .emit(); + .emit() } fn find_item_ty_spans( diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 1de691f32a70c..34440c60cf378 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -97,6 +97,8 @@ mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior .label = dereference of raw pointer +mir_build_exceeds_mcdc_condition_num_limit = Conditions number of the decision ({$conditions_num}) exceeds limit ({$max_conditions_num}). MCDC analysis will not count this expression. + mir_build_extern_static_requires_unsafe = use of extern static is unsafe and requires unsafe block .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index 4046122b6fe67..00e99f330f727 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -13,31 +13,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ast_block: BlockId, source_info: SourceInfo, ) -> BlockAnd<()> { - let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode } = + let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode: _ } = self.thir[ast_block]; self.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| { if targeted_by_break { this.in_breakable_scope(None, destination, span, |this| { - Some(this.ast_block_stmts( - destination, - block, - span, - stmts, - expr, - safety_mode, - region_scope, - )) + Some(this.ast_block_stmts(destination, block, span, stmts, expr, region_scope)) }) } else { - this.ast_block_stmts( - destination, - block, - span, - stmts, - expr, - safety_mode, - region_scope, - ) + this.ast_block_stmts(destination, block, span, stmts, expr, region_scope) } }) } @@ -49,7 +33,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, stmts: &[StmtId], expr: Option, - safety_mode: BlockSafety, region_scope: Scope, ) -> BlockAnd<()> { let this = self; @@ -72,13 +55,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // First we build all the statements in the block. let mut let_scope_stack = Vec::with_capacity(8); let outer_source_scope = this.source_scope; - let outer_in_scope_unsafe = this.in_scope_unsafe; // This scope information is kept for breaking out of the parent remainder scope in case // one let-else pattern matching fails. // By doing so, we can be sure that even temporaries that receive extended lifetime // assignments are dropped, too. let mut last_remainder_scope = region_scope; - this.update_source_scope_for_safety_mode(span, safety_mode); let source_info = this.source_info(span); for stmt in stmts { @@ -202,7 +183,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let_scope_stack.push(remainder_scope); let visibility_scope = - Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); + Some(this.new_source_scope(remainder_span, LintLevel::Inherited)); let initializer_span = this.thir[*initializer].span; let scope = (*init_scope, source_info); @@ -218,7 +199,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.visit_primary_bindings( pattern, UserTypeProjections::none(), - &mut |this, _, _, _, node, span, _, _| { + &mut |this, _, _, node, span, _, _| { this.storage_live_binding( block, node, @@ -271,7 +252,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree); let visibility_scope = - Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); + Some(this.new_source_scope(remainder_span, LintLevel::Inherited)); // Evaluate the initializer, if present. if let Some(init) = *initializer { @@ -308,7 +289,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.visit_primary_bindings( pattern, UserTypeProjections::none(), - &mut |this, _, _, _, node, span, _, _| { + &mut |this, _, _, node, span, _, _| { this.storage_live_binding(block, node, span, OutsideGuard, true); this.schedule_drop_for_binding(node, span, OutsideGuard); }, @@ -364,22 +345,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // Restore the original source scope. this.source_scope = outer_source_scope; - this.in_scope_unsafe = outer_in_scope_unsafe; block.unit() } - - /// If we are entering an unsafe block, create a new source scope - fn update_source_scope_for_safety_mode(&mut self, span: Span, safety_mode: BlockSafety) { - debug!("update_source_scope_for({:?}, {:?})", span, safety_mode); - let new_unsafety = match safety_mode { - BlockSafety::Safe => return, - BlockSafety::BuiltinUnsafe => Safety::BuiltinUnsafe, - BlockSafety::ExplicitUnsafe(hir_id) => { - self.in_scope_unsafe = Safety::ExplicitUnsafe(hir_id); - Safety::ExplicitUnsafe(hir_id) - } - }; - - self.source_scope = self.new_source_scope(span, LintLevel::Inherited, Some(new_unsafety)); - } } diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs index 2bd0e28973101..18e45291e9a86 100644 --- a/compiler/rustc_mir_build/src/build/cfg.rs +++ b/compiler/rustc_mir_build/src/build/cfg.rs @@ -107,9 +107,7 @@ impl<'tcx> CFG<'tcx> { /// This results in more accurate coverage reports for certain kinds of /// syntax (e.g. `continue` or `if !`) that would otherwise not appear in MIR. pub(crate) fn push_coverage_span_marker(&mut self, block: BasicBlock, source_info: SourceInfo) { - let kind = StatementKind::Coverage(Box::new(Coverage { - kind: coverage::CoverageKind::SpanMarker, - })); + let kind = StatementKind::Coverage(coverage::CoverageKind::SpanMarker); let stmt = Statement { source_info, kind }; self.push(block, stmt); } diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 0b8ec234dda7a..e2a5f97a84744 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -1,21 +1,25 @@ +mod mcdc; use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; -use rustc_middle::mir::{self, BasicBlock, UnOp}; +use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp}; use rustc_middle::thir::{ExprId, ExprKind, Thir}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; -use crate::build::Builder; +use crate::build::coverageinfo::mcdc::MCDCInfoBuilder; +use crate::build::{Builder, CFG}; pub(crate) struct BranchInfoBuilder { /// Maps condition expressions to their enclosing `!`, for better instrumentation. nots: FxHashMap, - num_block_markers: usize, + markers: BlockMarkerGen, branch_spans: Vec, + + mcdc_info: Option, } #[derive(Clone, Copy)] @@ -28,12 +32,46 @@ struct NotInfo { is_flipped: bool, } +#[derive(Default)] +struct BlockMarkerGen { + num_block_markers: usize, +} + +impl BlockMarkerGen { + fn next_block_marker_id(&mut self) -> BlockMarkerId { + let id = BlockMarkerId::from_usize(self.num_block_markers); + self.num_block_markers += 1; + id + } + + fn inject_block_marker( + &mut self, + cfg: &mut CFG<'_>, + source_info: SourceInfo, + block: BasicBlock, + ) -> BlockMarkerId { + let id = self.next_block_marker_id(); + let marker_statement = mir::Statement { + source_info, + kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), + }; + cfg.push(block, marker_statement); + + id + } +} + impl BranchInfoBuilder { /// Creates a new branch info builder, but only if branch coverage instrumentation /// is enabled and `def_id` represents a function that is eligible for coverage. pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) { - Some(Self { nots: FxHashMap::default(), num_block_markers: 0, branch_spans: vec![] }) + Some(Self { + nots: FxHashMap::default(), + markers: BlockMarkerGen::default(), + branch_spans: vec![], + mcdc_info: tcx.sess.instrument_coverage_mcdc().then(MCDCInfoBuilder::new), + }) } else { None } @@ -79,21 +117,41 @@ impl BranchInfoBuilder { } } - fn next_block_marker_id(&mut self) -> BlockMarkerId { - let id = BlockMarkerId::from_usize(self.num_block_markers); - self.num_block_markers += 1; - id + fn add_two_way_branch<'tcx>( + &mut self, + cfg: &mut CFG<'tcx>, + source_info: SourceInfo, + true_block: BasicBlock, + false_block: BasicBlock, + ) { + let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block); + let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block); + + self.branch_spans.push(BranchSpan { span: source_info.span, true_marker, false_marker }); } pub(crate) fn into_done(self) -> Option> { - let Self { nots: _, num_block_markers, branch_spans } = self; + let Self { + nots: _, + markers: BlockMarkerGen { num_block_markers }, + branch_spans, + mcdc_info, + } = self; if num_block_markers == 0 { assert!(branch_spans.is_empty()); return None; } - Some(Box::new(mir::coverage::BranchInfo { num_block_markers, branch_spans })) + let (mcdc_decision_spans, mcdc_branch_spans) = + mcdc_info.map(MCDCInfoBuilder::into_done).unwrap_or_default(); + + Some(Box::new(mir::coverage::BranchInfo { + num_block_markers, + branch_spans, + mcdc_branch_spans, + mcdc_decision_spans, + })) } } @@ -107,7 +165,7 @@ impl Builder<'_, '_> { mut else_block: BasicBlock, ) { // Bail out if branch coverage is not enabled for this function. - let Some(branch_info) = self.coverage_branch_info.as_ref() else { return }; + let Some(branch_info) = self.coverage_branch_info.as_mut() else { return }; // If this condition expression is nested within one or more `!` expressions, // replace it with the enclosing `!` collected by `visit_unary_not`. @@ -117,32 +175,24 @@ impl Builder<'_, '_> { std::mem::swap(&mut then_block, &mut else_block); } } - let source_info = self.source_info(self.thir[expr_id].span); - - // Now that we have `source_info`, we can upgrade to a &mut reference. - let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut"); - let mut inject_branch_marker = |block: BasicBlock| { - let id = branch_info.next_block_marker_id(); + let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; - let marker_statement = mir::Statement { - source_info, - kind: mir::StatementKind::Coverage(Box::new(mir::Coverage { - kind: CoverageKind::BlockMarker { id }, - })), + // Separate path for handling branches when MC/DC is enabled. + if let Some(mcdc_info) = branch_info.mcdc_info.as_mut() { + let inject_block_marker = |source_info, block| { + branch_info.markers.inject_block_marker(&mut self.cfg, source_info, block) }; - self.cfg.push(block, marker_statement); - - id - }; - - let true_marker = inject_branch_marker(then_block); - let false_marker = inject_branch_marker(else_block); + mcdc_info.visit_evaluated_condition( + self.tcx, + source_info, + then_block, + else_block, + inject_block_marker, + ); + return; + } - branch_info.branch_spans.push(BranchSpan { - span: source_info.span, - true_marker, - false_marker, - }); + branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block); } } diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs new file mode 100644 index 0000000000000..566dba460d43b --- /dev/null +++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs @@ -0,0 +1,275 @@ +use std::collections::VecDeque; + +use rustc_middle::mir::coverage::{ + BlockMarkerId, ConditionId, ConditionInfo, MCDCBranchSpan, MCDCDecisionSpan, +}; +use rustc_middle::mir::{BasicBlock, SourceInfo}; +use rustc_middle::thir::LogicalOp; +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; + +use crate::build::Builder; +use crate::errors::MCDCExceedsConditionNumLimit; + +/// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen, +/// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge. +/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged. +const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6; + +#[derive(Default)] +struct MCDCDecisionCtx { + /// To construct condition evaluation tree. + decision_stack: VecDeque, + processing_decision: Option, +} + +struct MCDCState { + decision_ctx_stack: Vec, +} + +impl MCDCState { + fn new() -> Self { + Self { decision_ctx_stack: vec![MCDCDecisionCtx::default()] } + } + + /// Decision depth is given as a u16 to reduce the size of the `CoverageKind`, + /// as it is very unlikely that the depth ever reaches 2^16. + #[inline] + fn decision_depth(&self) -> u16 { + match u16::try_from(self.decision_ctx_stack.len()) + .expect( + "decision depth did not fit in u16, this is likely to be an instrumentation error", + ) + .checked_sub(1) + { + Some(d) => d, + None => bug!("Unexpected empty decision stack"), + } + } + + // At first we assign ConditionIds for each sub expression. + // If the sub expression is composite, re-assign its ConditionId to its LHS and generate a new ConditionId for its RHS. + // + // Example: "x = (A && B) || (C && D) || (D && F)" + // + // Visit Depth1: + // (A && B) || (C && D) || (D && F) + // ^-------LHS--------^ ^-RHS--^ + // ID=1 ID=2 + // + // Visit LHS-Depth2: + // (A && B) || (C && D) + // ^-LHS--^ ^-RHS--^ + // ID=1 ID=3 + // + // Visit LHS-Depth3: + // (A && B) + // LHS RHS + // ID=1 ID=4 + // + // Visit RHS-Depth3: + // (C && D) + // LHS RHS + // ID=3 ID=5 + // + // Visit RHS-Depth2: (D && F) + // LHS RHS + // ID=2 ID=6 + // + // Visit Depth1: + // (A && B) || (C && D) || (D && F) + // ID=1 ID=4 ID=3 ID=5 ID=2 ID=6 + // + // A node ID of '0' always means MC/DC isn't being tracked. + // + // If a "next" node ID is '0', it means it's the end of the test vector. + // + // As the compiler tracks expression in pre-order, we can ensure that condition info of parents are always properly assigned when their children are visited. + // - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next". + // - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next". + fn record_conditions(&mut self, op: LogicalOp, span: Span) { + let decision_depth = self.decision_depth(); + let Some(decision_ctx) = self.decision_ctx_stack.last_mut() else { + bug!("Unexpected empty decision_ctx_stack") + }; + let decision = match decision_ctx.processing_decision.as_mut() { + Some(decision) => { + decision.span = decision.span.to(span); + decision + } + None => decision_ctx.processing_decision.insert(MCDCDecisionSpan { + span, + conditions_num: 0, + end_markers: vec![], + decision_depth, + }), + }; + + let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default(); + let lhs_id = if parent_condition.condition_id == ConditionId::NONE { + decision.conditions_num += 1; + ConditionId::from(decision.conditions_num) + } else { + parent_condition.condition_id + }; + + decision.conditions_num += 1; + let rhs_condition_id = ConditionId::from(decision.conditions_num); + + let (lhs, rhs) = match op { + LogicalOp::And => { + let lhs = ConditionInfo { + condition_id: lhs_id, + true_next_id: rhs_condition_id, + false_next_id: parent_condition.false_next_id, + }; + let rhs = ConditionInfo { + condition_id: rhs_condition_id, + true_next_id: parent_condition.true_next_id, + false_next_id: parent_condition.false_next_id, + }; + (lhs, rhs) + } + LogicalOp::Or => { + let lhs = ConditionInfo { + condition_id: lhs_id, + true_next_id: parent_condition.true_next_id, + false_next_id: rhs_condition_id, + }; + let rhs = ConditionInfo { + condition_id: rhs_condition_id, + true_next_id: parent_condition.true_next_id, + false_next_id: parent_condition.false_next_id, + }; + (lhs, rhs) + } + }; + // We visit expressions tree in pre-order, so place the left-hand side on the top. + decision_ctx.decision_stack.push_back(rhs); + decision_ctx.decision_stack.push_back(lhs); + } + + fn take_condition( + &mut self, + true_marker: BlockMarkerId, + false_marker: BlockMarkerId, + ) -> (Option, Option) { + let Some(decision_ctx) = self.decision_ctx_stack.last_mut() else { + bug!("Unexpected empty decision_ctx_stack") + }; + let Some(condition_info) = decision_ctx.decision_stack.pop_back() else { + return (None, None); + }; + let Some(decision) = decision_ctx.processing_decision.as_mut() else { + bug!("Processing decision should have been created before any conditions are taken"); + }; + if condition_info.true_next_id == ConditionId::NONE { + decision.end_markers.push(true_marker); + } + if condition_info.false_next_id == ConditionId::NONE { + decision.end_markers.push(false_marker); + } + + if decision_ctx.decision_stack.is_empty() { + (Some(condition_info), decision_ctx.processing_decision.take()) + } else { + (Some(condition_info), None) + } + } +} + +pub struct MCDCInfoBuilder { + branch_spans: Vec, + decision_spans: Vec, + state: MCDCState, +} + +impl MCDCInfoBuilder { + pub fn new() -> Self { + Self { branch_spans: vec![], decision_spans: vec![], state: MCDCState::new() } + } + + pub fn visit_evaluated_condition( + &mut self, + tcx: TyCtxt<'_>, + source_info: SourceInfo, + true_block: BasicBlock, + false_block: BasicBlock, + mut inject_block_marker: impl FnMut(SourceInfo, BasicBlock) -> BlockMarkerId, + ) { + let true_marker = inject_block_marker(source_info, true_block); + let false_marker = inject_block_marker(source_info, false_block); + + let decision_depth = self.state.decision_depth(); + let (mut condition_info, decision_result) = + self.state.take_condition(true_marker, false_marker); + // take_condition() returns Some for decision_result when the decision stack + // is empty, i.e. when all the conditions of the decision were instrumented, + // and the decision is "complete". + if let Some(decision) = decision_result { + match decision.conditions_num { + 0 => { + unreachable!("Decision with no condition is not expected"); + } + 1..=MAX_CONDITIONS_NUM_IN_DECISION => { + self.decision_spans.push(decision); + } + _ => { + // Do not generate mcdc mappings and statements for decisions with too many conditions. + let rebase_idx = self.branch_spans.len() - decision.conditions_num + 1; + for branch in &mut self.branch_spans[rebase_idx..] { + branch.condition_info = None; + } + + // ConditionInfo of this branch shall also be reset. + condition_info = None; + + tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit { + span: decision.span, + conditions_num: decision.conditions_num, + max_conditions_num: MAX_CONDITIONS_NUM_IN_DECISION, + }); + } + } + } + self.branch_spans.push(MCDCBranchSpan { + span: source_info.span, + condition_info, + true_marker, + false_marker, + decision_depth, + }); + } + + pub fn into_done(self) -> (Vec, Vec) { + (self.decision_spans, self.branch_spans) + } +} + +impl Builder<'_, '_> { + pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) { + if let Some(branch_info) = self.coverage_branch_info.as_mut() + && let Some(mcdc_info) = branch_info.mcdc_info.as_mut() + { + mcdc_info.state.record_conditions(logical_op, span); + } + } + + pub(crate) fn mcdc_increment_depth_if_enabled(&mut self) { + if let Some(branch_info) = self.coverage_branch_info.as_mut() + && let Some(mcdc_info) = branch_info.mcdc_info.as_mut() + { + mcdc_info.state.decision_ctx_stack.push(MCDCDecisionCtx::default()); + }; + } + + pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) { + if let Some(branch_info) = self.coverage_branch_info.as_mut() + && let Some(mcdc_info) = branch_info.mcdc_info.as_mut() + { + if mcdc_info.state.decision_ctx_stack.pop().is_none() { + bug!("Unexpected empty decision stack"); + } + }; + } +} diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index 288b787798b49..30877e38318d6 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -6,7 +6,7 @@ //! present, and if so we branch off into this module, which implements the attribute by //! implementing a custom lowering from THIR to MIR. //! -//! The result of this lowering is returned "normally" from the `mir_built` query, with the only +//! The result of this lowering is returned "normally" from the `build_mir` hook, with the only //! notable difference being that the `injected` field in the body is set. Various components of the //! MIR pipeline, like borrowck and the pass manager will then consult this field (via //! `body.should_skip()`) to skip the parts of the MIR pipeline that precede the MIR phase the user @@ -56,6 +56,7 @@ pub(super) fn build_custom_mir<'tcx>( var_debug_info: Vec::new(), span, required_consts: Vec::new(), + mentioned_items: Vec::new(), is_polymorphic: false, tainted_by_errors: None, injection_phase: None, @@ -71,10 +72,7 @@ pub(super) fn build_custom_mir<'tcx>( parent_scope: None, inlined: None, inlined_parent_scope: None, - local_data: ClearCrossCrate::Set(SourceScopeLocalData { - lint_root: hir_id, - safety: Safety::Safe, - }), + local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }), }); body.injection_phase = Some(parse_attribute(attr)); diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs index a6f9caada2d19..0384b9bc154e2 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse.rs @@ -215,7 +215,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { fn parse_local_decls(&mut self, mut stmts: impl Iterator) -> PResult<()> { let (ret_var, ..) = self.parse_let_statement(stmts.next().unwrap())?; - self.local_map.insert(ret_var, Local::from_u32(0)); + self.local_map.insert(ret_var, Local::ZERO); for stmt in stmts { let (var, ty, span) = self.parse_let_statement(stmt)?; diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index f12e25db6fcd5..060b328ef48ae 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -685,7 +685,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fake_borrow_temp.into(), Rvalue::Ref( tcx.lifetimes.re_erased, - BorrowKind::Fake, + BorrowKind::Fake(FakeBorrowKind::Shallow), Place { local: base_place.local, projection }, ), ); diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index c77f4a06d0569..260ab058e600c 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -118,19 +118,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Box { value } => { let value_ty = this.thir[value].ty; let tcx = this.tcx; - - // `exchange_malloc` is unsafe but box is safe, so need a new scope. - let synth_scope = this.new_source_scope( - expr_span, - LintLevel::Inherited, - Some(Safety::BuiltinUnsafe), - ); - let synth_info = SourceInfo { span: expr_span, scope: synth_scope }; + let source_info = this.source_info(expr_span); let size = this.temp(tcx.types.usize, expr_span); this.cfg.push_assign( block, - synth_info, + source_info, size, Rvalue::NullaryOp(NullOp::SizeOf, value_ty), ); @@ -138,7 +131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let align = this.temp(tcx.types.usize, expr_span); this.cfg.push_assign( block, - synth_info, + source_info, align, Rvalue::NullaryOp(NullOp::AlignOf, value_ty), ); @@ -154,7 +147,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let success = this.cfg.start_new_block(); this.cfg.terminate( block, - synth_info, + source_info, TerminatorKind::Call { func: exchange_malloc, args: vec![ @@ -580,7 +573,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { result_value, Rvalue::CheckedBinaryOp(op, Box::new((lhs.to_copy(), rhs.to_copy()))), ); - let val_fld = FieldIdx::new(0); + let val_fld = FieldIdx::ZERO; let of_fld = FieldIdx::new(1); let tcx = self.tcx; diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index b4eeeccc127b4..c8360b6a5fdd2 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // FIXME: Does this need extra logic to handle let-chains? let source_info = if this.is_let(cond) { let variable_scope = - this.new_source_scope(then_span, LintLevel::Inherited, None); + this.new_source_scope(then_span, LintLevel::Inherited); this.source_scope = variable_scope; SourceInfo { span: then_span, scope: variable_scope } } else { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index e7808ff880b57..bce1526775963 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -10,10 +10,8 @@ use crate::build::scope::DropKind; use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode}; -use rustc_data_structures::{ - fx::{FxHashSet, FxIndexMap, FxIndexSet}, - stack::ensure_sufficient_stack, -}; +use rustc_data_structures::{fx::FxIndexMap, stack::ensure_sufficient_stack}; +use rustc_hir::{BindingMode, ByRef}; use rustc_middle::middle::region; use rustc_middle::mir::{self, *}; use rustc_middle::thir::{self, *}; @@ -75,12 +73,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let expr_span = expr.span; match expr.kind { - ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => { + ExprKind::LogicalOp { op: op @ LogicalOp::And, lhs, rhs } => { + this.visit_coverage_branch_operation(op, expr_span); let lhs_then_block = unpack!(this.then_else_break_inner(block, lhs, args)); let rhs_then_block = unpack!(this.then_else_break_inner(lhs_then_block, rhs, args)); rhs_then_block.unit() } - ExprKind::LogicalOp { op: LogicalOp::Or, lhs, rhs } => { + ExprKind::LogicalOp { op: op @ LogicalOp::Or, lhs, rhs } => { + this.visit_coverage_branch_operation(op, expr_span); let local_scope = this.local_scope(); let (lhs_success_block, failure_block) = this.in_if_then_scope(local_scope, expr_span, |this| { @@ -148,8 +148,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut block = block; let temp_scope = args.temp_scope_override.unwrap_or_else(|| this.local_scope()); let mutability = Mutability::Mut; + + // Increment the decision depth, in case we encounter boolean expressions + // further down. + this.mcdc_increment_depth_if_enabled(); let place = unpack!(block = this.as_temp(block, Some(temp_scope), expr_id, mutability)); + this.mcdc_decrement_depth_if_enabled(); + let operand = Operand::Move(Place::from(place)); let then_block = this.cfg.start_new_block(); @@ -208,17 +214,82 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// 2. Create the decision tree ([Builder::lower_match_tree]). /// 3. Determine the fake borrows that are needed from the places that were /// matched against and create the required temporaries for them - /// ([Builder::calculate_fake_borrows]). + /// ([util::collect_fake_borrows]). /// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]). /// /// ## False edges /// - /// We don't want to have the exact structure of the decision tree be - /// visible through borrow checking. False edges ensure that the CFG as - /// seen by borrow checking doesn't encode this. False edges are added: + /// We don't want to have the exact structure of the decision tree be visible through borrow + /// checking. Specifically we want borrowck to think that: + /// - at any point, any or none of the patterns and guards seen so far may have been tested; + /// - after the match, any of the patterns may have matched. + /// + /// For example, all of these would fail to error if borrowck could see the real CFG (examples + /// taken from `tests/ui/nll/match-cfg-fake-edges.rs`): + /// ```ignore (too many errors, this is already in the test suite) + /// let x = String::new(); + /// let _ = match true { + /// _ => {}, + /// _ => drop(x), + /// }; + /// // Borrowck must not know the second arm is never run. + /// drop(x); //~ ERROR use of moved value + /// + /// let x; + /// # let y = true; + /// match y { + /// _ if { x = 2; true } => {}, + /// // Borrowck must not know the guard is always run. + /// _ => drop(x), //~ ERROR used binding `x` is possibly-uninitialized + /// }; + /// + /// let x = String::new(); + /// # let y = true; + /// match y { + /// false if { drop(x); true } => {}, + /// // Borrowck must not know the guard is not run in the `true` case. + /// true => drop(x), //~ ERROR use of moved value: `x` + /// false => {}, + /// }; /// - /// * From each pre-binding block to the next pre-binding block. - /// * From each otherwise block to the next pre-binding block. + /// # let mut y = (true, true); + /// let r = &mut y.1; + /// match y { + /// //~^ ERROR cannot use `y.1` because it was mutably borrowed + /// (false, true) => {} + /// // Borrowck must not know we don't test `y.1` when `y.0` is `true`. + /// (true, _) => drop(r), + /// (false, _) => {} + /// }; + /// ``` + /// + /// We add false edges to act as if we were naively matching each arm in order. What we need is + /// a (fake) path from each candidate to the next, specifically from candidate C's pre-binding + /// block to next candidate D's pre-binding block. For maximum precision (needed for deref + /// patterns), we choose the earliest node on D's success path that doesn't also lead to C (to + /// avoid loops). + /// + /// This turns out to be easy to compute: that block is the `start_block` of the first call to + /// `match_candidates` where D is the first candidate in the list. + /// + /// For example: + /// ```rust + /// # let (x, y) = (true, true); + /// match (x, y) { + /// (true, true) => 1, + /// (false, true) => 2, + /// (true, false) => 3, + /// _ => 4, + /// } + /// # ; + /// ``` + /// In this example, the pre-binding block of arm 1 has a false edge to the block for result + /// `false` of the first test on `x`. The other arms have false edges to the pre-binding blocks + /// of the next arm. + /// + /// On top of this, we also add a false edge from the otherwise_block of each guard to the + /// aforementioned start block of the next candidate, to ensure borrock doesn't rely on which + /// guards may have run. #[instrument(level = "debug", skip(self, arms))] pub(crate) fn match_expr( &mut self, @@ -229,7 +300,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, ) -> BlockAnd<()> { - let scrutinee_span = scrutinee_span; let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span)); @@ -313,22 +383,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_start_span: Span, match_has_guard: bool, candidates: &mut [&mut Candidate<'pat, 'tcx>], - ) -> Vec<(Place<'tcx>, Local)> { - // The set of places that we are creating fake borrows of. If there are - // no match guards then we don't need any fake borrows, so don't track - // them. - let fake_borrows = match_has_guard - .then(|| util::FakeBorrowCollector::collect_fake_borrows(self, candidates)); + ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> { + // The set of places that we are creating fake borrows of. If there are no match guards then + // we don't need any fake borrows, so don't track them. + let fake_borrows: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard { + util::collect_fake_borrows( + self, + candidates, + scrutinee_span, + scrutinee_place_builder.base(), + ) + } else { + Vec::new() + }; + // See the doc comment on `match_candidates` for why we have an + // otherwise block. Match checking will ensure this is actually + // unreachable. let otherwise_block = self.cfg.start_new_block(); // This will generate code to test scrutinee_place and // branch to the appropriate arm block self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates); - // See the doc comment on `match_candidates` for why we may have an - // otherwise block. Match checking will ensure this is actually - // unreachable. let source_info = self.source_info(scrutinee_span); // Matching on a `scrutinee_place` with an uninhabited type doesn't @@ -365,17 +442,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for candidate in candidates { candidate.visit_leaves(|leaf_candidate| { if let Some(ref mut prev) = previous_candidate { - prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block; + assert!(leaf_candidate.false_edge_start_block.is_some()); + prev.next_candidate_start_block = leaf_candidate.false_edge_start_block; } previous_candidate = Some(leaf_candidate); }); } - if let Some(ref borrows) = fake_borrows { - self.calculate_fake_borrows(borrows, scrutinee_span) - } else { - Vec::new() - } + fake_borrows } /// Lower the bindings, guards and arm bodies of a `match` expression. @@ -391,7 +465,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee_span: Span, arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>, outer_source_info: SourceInfo, - fake_borrow_temps: Vec<(Place<'tcx>, Local)>, + fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>, ) -> BlockAnd<()> { let arm_end_blocks: Vec<_> = arm_candidates .into_iter() @@ -475,7 +549,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, outer_source_info: SourceInfo, candidate: Candidate<'_, 'tcx>, - fake_borrow_temps: &[(Place<'tcx>, Local)], + fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, storages_alive: bool, @@ -555,7 +629,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> BlockAnd<()> { match irrefutable_pat.kind { // Optimize the case of `let x = ...` to write directly into `x` - PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { + PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); unpack!(block = self.expr_into_dest(place, block, initializer_id)); @@ -581,7 +655,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { box Pat { kind: PatKind::Binding { - mode: BindingMode::ByValue, var, subpattern: None, .. + mode: BindingMode(ByRef::No, _), + var, + subpattern: None, + .. }, .. }, @@ -721,17 +798,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_primary_bindings( pattern, UserTypeProjections::none(), - &mut |this, mutability, name, mode, var, span, ty, user_ty| { + &mut |this, name, mode, var, span, ty, user_ty| { if visibility_scope.is_none() { visibility_scope = - Some(this.new_source_scope(scope_span, LintLevel::Inherited, None)); + Some(this.new_source_scope(scope_span, LintLevel::Inherited)); } let source_info = SourceInfo { span, scope: this.source_scope }; let visibility_scope = visibility_scope.unwrap(); this.declare_binding( source_info, visibility_scope, - mutability, name, mode, var, @@ -819,7 +895,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pattern_user_ty: UserTypeProjections, f: &mut impl FnMut( &mut Self, - Mutability, Symbol, BindingMode, LocalVarId, @@ -833,18 +908,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pattern, pattern_user_ty ); match pattern.kind { - PatKind::Binding { - mutability, - name, - mode, - var, - ty, - ref subpattern, - is_primary, - .. - } => { + PatKind::Binding { name, mode, var, ty, ref subpattern, is_primary, .. } => { if is_primary { - f(self, mutability, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); + f(self, name, mode, var, pattern.span, ty, pattern_user_ty.clone()); } if let Some(subpattern) = subpattern.as_ref() { self.visit_primary_bindings(subpattern, pattern_user_ty, f); @@ -880,6 +946,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f); } + PatKind::DerefPattern { ref subpattern, .. } => { + self.visit_primary_bindings(subpattern, UserTypeProjections::none(), f); + } + PatKind::AscribeUserType { ref subpattern, ascription: thir::Ascription { ref annotation, variance: _ }, @@ -1003,10 +1073,18 @@ struct Candidate<'pat, 'tcx> { /// If the candidate matches, bindings and ascriptions must be established. extra_data: PatternExtraData<'tcx>, + /// If we filled `self.subcandidate`, we store here the span of the or-pattern they came from. + // Invariant: it is `None` iff `subcandidates.is_empty()`. + or_span: Option, + /// The block before the `bindings` have been established. pre_binding_block: Option, - /// The pre-binding block of the next candidate. - next_candidate_pre_binding_block: Option, + + /// The earliest block that has only candidates >= this one as descendents. Used for false + /// edges, see the doc for [`Builder::match_expr`]. + false_edge_start_block: Option, + /// The `false_edge_start_block` of the next candidate. + next_candidate_start_block: Option, } impl<'tcx, 'pat> Candidate<'pat, 'tcx> { @@ -1025,9 +1103,11 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { extra_data: flat_pat.extra_data, has_guard, subcandidates: Vec::new(), + or_span: None, otherwise_block: None, pre_binding_block: None, - next_candidate_pre_binding_block: None, + false_edge_start_block: None, + next_candidate_start_block: None, } } @@ -1091,6 +1171,7 @@ enum TestCase<'pat, 'tcx> { Constant { value: mir::Const<'tcx> }, Range(&'pat PatRange<'tcx>), Slice { len: usize, variable_length: bool }, + Deref { temp: Place<'tcx>, mutability: Mutability }, Or { pats: Box<[FlatPat<'pat, 'tcx>]> }, } @@ -1103,7 +1184,10 @@ impl<'pat, 'tcx> TestCase<'pat, 'tcx> { #[derive(Debug, Clone)] pub(crate) struct MatchPair<'pat, 'tcx> { /// This place... - place: PlaceBuilder<'tcx>, + // This can be `None` if it referred to a non-captured place in a closure. + // Invariant: place.is_none() => test_case is Irrefutable + // In other words this must be `Some(_)` after simplification. + place: Option>, /// ... must pass this test... // Invariant: after creation and simplification in `Candidate::new()`, this must not be @@ -1147,6 +1231,13 @@ enum TestKind<'tcx> { /// Test that the length of the slice is equal to `len`. Len { len: u64, op: BinOp }, + + /// Call `Deref::deref[_mut]` on the value. + Deref { + /// Temporary to store the result of `deref()`/`deref_mut()`. + temp: Place<'tcx>, + mutability: Mutability, + }, } /// A test to perform to determine which [`Candidate`] matches a value. @@ -1261,9 +1352,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let mut split_or_candidate = false; for candidate in &mut *candidates { - if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] = - &*candidate.match_pairs - { + if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs { // Split a candidate in which the only match-pair is an or-pattern into multiple // candidates. This is so that // @@ -1273,8 +1362,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // } // // only generates a single switch. - candidate.subcandidates = self.create_or_subcandidates(pats, candidate.has_guard); - candidate.match_pairs.pop(); + let match_pair = candidate.match_pairs.pop().unwrap(); + self.create_or_subcandidates(candidate, match_pair); split_or_candidate = true; } } @@ -1284,17 +1373,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // At least one of the candidates has been split into subcandidates. // We need to change the candidate list to include those. let mut new_candidates = Vec::new(); - - for candidate in candidates { + for candidate in candidates.iter_mut() { candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); } - self.match_simplified_candidates( + self.match_candidates( span, scrutinee_span, start_block, otherwise_block, &mut *new_candidates, ); + + for candidate in candidates { + self.merge_trivial_subcandidates(candidate); + } } else { self.match_simplified_candidates( span, @@ -1315,6 +1407,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'_, 'tcx>], ) { + if let [first, ..] = candidates { + if first.false_edge_start_block.is_none() { + first.false_edge_start_block = Some(start_block); + } + } + match candidates { [] => { // If there are no candidates that still need testing, we're done. Since all matches are @@ -1459,14 +1557,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return; } - let match_pairs = mem::take(&mut first_candidate.match_pairs); - let (first_match_pair, remaining_match_pairs) = match_pairs.split_first().unwrap(); - let TestCase::Or { ref pats } = &first_match_pair.test_case else { unreachable!() }; - + let first_match_pair = first_candidate.match_pairs.remove(0); + let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs); let remainder_start = self.cfg.start_new_block(); - let or_span = first_match_pair.pattern.span; // Test the alternatives of this or-pattern. - self.test_or_pattern(first_candidate, start_block, remainder_start, pats, or_span); + self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair); if !remaining_match_pairs.is_empty() { // If more match pairs remain, test them after each subcandidate. @@ -1501,25 +1596,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } - #[instrument( - skip(self, start_block, otherwise_block, or_span, candidate, pats), - level = "debug" - )] + #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")] fn test_or_pattern<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, start_block: BasicBlock, otherwise_block: BasicBlock, - pats: &[FlatPat<'pat, 'tcx>], - or_span: Span, + match_pair: MatchPair<'pat, 'tcx>, ) { - debug!("candidate={:#?}\npats={:#?}", candidate, pats); - let mut or_candidates: Vec<_> = pats - .iter() - .cloned() - .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) - .collect(); - let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); + let or_span = match_pair.pattern.span; + self.create_or_subcandidates(candidate, match_pair); + let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect(); self.match_candidates( or_span, or_span, @@ -1527,35 +1614,49 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block, &mut or_candidate_refs, ); - candidate.subcandidates = or_candidates; - self.merge_trivial_subcandidates(candidate, self.source_info(or_span)); + self.merge_trivial_subcandidates(candidate); } - /// Try to merge all of the subcandidates of the given candidate into one. - /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. - fn merge_trivial_subcandidates( + /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new + /// subcandidate. Any candidate that has been expanded that way should be passed to + /// `merge_trivial_subcandidates` after its subcandidates have been processed. + fn create_or_subcandidates<'pat>( &mut self, - candidate: &mut Candidate<'_, 'tcx>, - source_info: SourceInfo, + candidate: &mut Candidate<'pat, 'tcx>, + match_pair: MatchPair<'pat, 'tcx>, ) { + let TestCase::Or { pats } = match_pair.test_case else { bug!() }; + debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); + candidate.or_span = Some(match_pair.pattern.span); + candidate.subcandidates = pats + .into_vec() + .into_iter() + .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) + .collect(); + candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; + } + + /// Try to merge all of the subcandidates of the given candidate into one. This avoids + /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The or-pattern should have + /// been expanded with `create_or_subcandidates`. + fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { if candidate.subcandidates.is_empty() || candidate.has_guard { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; } - let mut can_merge = true; - - // Not `Iterator::all` because we don't want to short-circuit. - for subcandidate in &mut candidate.subcandidates { - self.merge_trivial_subcandidates(subcandidate, source_info); - - // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. - can_merge &= - subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty(); - } - + // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. + let can_merge = candidate.subcandidates.iter().all(|subcandidate| { + subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty() + }); if can_merge { let any_matches = self.cfg.start_new_block(); + let or_span = candidate.or_span.take().unwrap(); + let source_info = self.source_info(or_span); + if candidate.false_edge_start_block.is_none() { + candidate.false_edge_start_block = + candidate.subcandidates[0].false_edge_start_block; + } for subcandidate in mem::take(&mut candidate.subcandidates) { let or_block = subcandidate.pre_binding_block.unwrap(); self.cfg.goto(or_block, source_info, any_matches); @@ -1581,14 +1682,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [`Switch`]: TestKind::Switch /// [`SwitchInt`]: TestKind::SwitchInt /// [`Range`]: TestKind::Range - fn pick_test( - &mut self, - candidates: &mut [&mut Candidate<'_, 'tcx>], - ) -> (PlaceBuilder<'tcx>, Test<'tcx>) { + fn pick_test(&mut self, candidates: &[&mut Candidate<'_, 'tcx>]) -> (Place<'tcx>, Test<'tcx>) { // Extract the match-pair from the highest priority candidate let match_pair = &candidates.first().unwrap().match_pairs[0]; let test = self.test(match_pair); - let match_place = match_pair.place.clone(); + // Unwrap is ok after simplification. + let match_place = match_pair.place.unwrap(); debug!(?test, ?match_pair); (match_place, test) @@ -1629,7 +1728,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// - candidate 1 becomes `[y @ false]` since we know that `x` was `false`. fn sort_candidates<'b, 'c, 'pat>( &mut self, - match_place: &PlaceBuilder<'tcx>, + match_place: Place<'tcx>, test: &Test<'tcx>, mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], ) -> ( @@ -1647,7 +1746,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // sorting. while let Some(candidate) = candidates.first_mut() { let Some(branch) = - self.sort_candidate(&match_place, test, candidate, &target_candidates) + self.sort_candidate(match_place, test, candidate, &target_candidates) else { break; }; @@ -1775,7 +1874,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // For each of the N possible test outcomes, build the vector of candidates that applies if // the test has that particular outcome. let (remaining_candidates, target_candidates) = - self.sort_candidates(&match_place, &test, candidates); + self.sort_candidates(match_place, &test, candidates); // The block that we should branch to if none of the // `target_candidates` match. @@ -1815,86 +1914,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee_span, start_block, remainder_start, - &match_place, + match_place, &test, target_blocks, ); } - - /// Determine the fake borrows that are needed from a set of places that - /// have to be stable across match guards. - /// - /// Returns a list of places that need a fake borrow and the temporary - /// that's used to store the fake borrow. - /// - /// Match exhaustiveness checking is not able to handle the case where the - /// place being matched on is mutated in the guards. We add "fake borrows" - /// to the guards that prevent any mutation of the place being matched. - /// There are a some subtleties: - /// - /// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared - /// reference, the borrow isn't even tracked. As such we have to add fake - /// borrows of any prefixes of a place - /// 2. We don't want `match x { _ => (), }` to conflict with mutable - /// borrows of `x`, so we only add fake borrows for places which are - /// bound or tested by the match. - /// 3. We don't want the fake borrows to conflict with `ref mut` bindings, - /// so we use a special BorrowKind for them. - /// 4. The fake borrows may be of places in inactive variants, so it would - /// be UB to generate code for them. They therefore have to be removed - /// by a MIR pass run after borrow checking. - fn calculate_fake_borrows<'b>( - &mut self, - fake_borrows: &'b FxIndexSet>, - temp_span: Span, - ) -> Vec<(Place<'tcx>, Local)> { - let tcx = self.tcx; - - debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows); - - let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len()); - - // Insert a Shallow borrow of the prefixes of any fake borrows. - for place in fake_borrows { - let mut cursor = place.projection.as_ref(); - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - - if let ProjectionElem::Deref = elem { - // Insert a shallow borrow after a deref. For other - // projections the borrow of prefix_cursor will - // conflict with any mutation of base. - all_fake_borrows.push(PlaceRef { local: place.local, projection: proj_base }); - } - } - - all_fake_borrows.push(place.as_ref()); - } - - // Deduplicate - let mut dedup = FxHashSet::default(); - all_fake_borrows.retain(|b| dedup.insert(*b)); - - debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows); - - all_fake_borrows - .into_iter() - .map(|matched_place_ref| { - let matched_place = Place { - local: matched_place_ref.local, - projection: tcx.mk_place_elems(matched_place_ref.projection), - }; - let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty; - let fake_borrow_ty = - Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty); - let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span); - fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow)); - let fake_borrow_temp = self.local_decls.push(fake_borrow_temp); - - (matched_place, fake_borrow_temp) - }) - .collect() - } } /////////////////////////////////////////////////////////////////////////// @@ -1959,7 +1983,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, candidate: Candidate<'pat, 'tcx>, parent_data: &[PatternExtraData<'tcx>], - fake_borrows: &[(Place<'tcx>, Local)], + fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, schedule_drops: bool, @@ -1973,12 +1997,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut block = candidate.pre_binding_block.unwrap(); - if candidate.next_candidate_pre_binding_block.is_some() { + if candidate.next_candidate_start_block.is_some() { let fresh_block = self.cfg.start_new_block(); self.false_edges( block, fresh_block, - candidate.next_candidate_pre_binding_block, + candidate.next_candidate_start_block, candidate_source_info, ); block = fresh_block; @@ -2082,16 +2106,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings); self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); - let guard_frame = GuardFrame { - locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(), - }; + let guard_frame = + GuardFrame { locals: bindings.map(|b| GuardFrameLocal::new(b.var_id)).collect() }; debug!("entering guard building context: {:?}", guard_frame); self.guard_context.push(guard_frame); let re_erased = tcx.lifetimes.re_erased; let scrutinee_source_info = self.source_info(scrutinee_span); - for &(place, temp) in fake_borrows { - let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake, place); + for &(place, temp, kind) in fake_borrows { + let borrow = Rvalue::Ref(re_erased, BorrowKind::Fake(kind), place); self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); } @@ -2114,7 +2137,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let guard_frame = self.guard_context.pop().unwrap(); debug!("Exiting guard building context with locals: {:?}", guard_frame); - for &(_, temp) in fake_borrows { + for &(_, temp, _) in fake_borrows { let cause = FakeReadCause::ForMatchGuard; self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp)); } @@ -2127,7 +2150,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.false_edges( otherwise_post_guard_block, otherwise_block, - candidate.next_candidate_pre_binding_block, + candidate.next_candidate_start_block, source_info, ); @@ -2161,7 +2184,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .iter() .flat_map(|d| &d.bindings) .chain(&candidate.extra_data.bindings) - .filter(|binding| matches!(binding.binding_mode, BindingMode::ByValue)); + .filter(|binding| matches!(binding.binding_mode.0, ByRef::No)); // Read all of the by reference bindings to ensure that the // place they refer to can't be modified by the guard. for binding in by_value_bindings.clone() { @@ -2248,12 +2271,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { RefWithinGuard, schedule_drops, ); - match binding.binding_mode { - BindingMode::ByValue => { + match binding.binding_mode.0 { + ByRef::No => { let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } - BindingMode::ByRef(borrow_kind) => { + ByRef::Yes(mutbl) => { let value_for_arm = self.storage_live_binding( block, binding.var_id, @@ -2262,7 +2285,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { schedule_drops, ); - let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source); + let rvalue = + Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); self.cfg.push_assign(block, source_info, value_for_arm, rvalue); let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); @@ -2303,10 +2327,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if schedule_drops { self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); } - let rvalue = match binding.binding_mode { - BindingMode::ByValue => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), - BindingMode::ByRef(borrow_kind) => { - Rvalue::Ref(re_erased, borrow_kind, binding.source) + let rvalue = match binding.binding_mode.0 { + ByRef::No => Rvalue::Use(self.consume_by_copy_or_move(binding.source)), + ByRef::Yes(mutbl) => { + Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source) } }; self.cfg.push_assign(block, source_info, local, rvalue); @@ -2323,7 +2347,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, source_info: SourceInfo, visibility_scope: SourceScope, - mutability: Mutability, name: Symbol, mode: BindingMode, var_id: LocalVarId, @@ -2335,18 +2358,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let tcx = self.tcx; let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; - let binding_mode = match mode { - BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), - BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), - }; let local = LocalDecl { - mutability, + mutability: mode.1, ty: var_ty, user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) }, source_info, local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(BindingForm::Var( VarBindingForm { - binding_mode, + binding_mode: mode, // hypothetically, `visit_primary_bindings` could try to unzip // an outermost hir::Ty as we descend, matching up // idents in pat; but complex w/ unclear UI payoff. diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 97b94a0b362af..90f12e55ff4c7 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -12,7 +12,7 @@ //! sort of test: for example, testing which variant an enum is, or //! testing a value against a constant. -use crate::build::matches::{Candidate, FlatPat, MatchPair, PatternExtraData, TestCase}; +use crate::build::matches::{MatchPair, PatternExtraData, TestCase}; use crate::build::Builder; use std::mem; @@ -66,26 +66,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); debug!(simplified = ?match_pairs, "simplify_match_pairs"); } - - /// Create a new candidate for each pattern in `pats`, and recursively simplify tje - /// single-or-pattern case. - pub(super) fn create_or_subcandidates<'pat>( - &mut self, - pats: &[FlatPat<'pat, 'tcx>], - has_guard: bool, - ) -> Vec> { - pats.iter() - .cloned() - .map(|flat_pat| { - let mut candidate = Candidate::from_flat_pat(flat_pat, has_guard); - if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] = - &*candidate.match_pairs - { - candidate.subcandidates = self.create_or_subcandidates(pats, has_guard); - candidate.match_pairs.pop(); - } - candidate - }) - .collect() - } } diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 9d77bd063e114..5dd478aa4224a 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -5,7 +5,6 @@ // identify what tests are needed, perform the tests, and then filter // the candidates based on the result. -use crate::build::expr::as_place::PlaceBuilder; use crate::build::matches::{Candidate, MatchPair, Test, TestBranch, TestCase, TestKind}; use crate::build::Builder; use rustc_data_structures::fx::FxIndexMap; @@ -43,6 +42,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestKind::Len { len: len as u64, op } } + TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability }, + TestCase::Or { .. } => bug!("or-patterns should have already been handled"), TestCase::Irrefutable { .. } => span_bug!( @@ -55,18 +56,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Test { span: match_pair.pattern.span, kind } } - #[instrument(skip(self, target_blocks, place_builder), level = "debug")] + #[instrument(skip(self, target_blocks, place), level = "debug")] pub(super) fn perform_test( &mut self, match_start_span: Span, scrutinee_span: Span, block: BasicBlock, otherwise_block: BasicBlock, - place_builder: &PlaceBuilder<'tcx>, + place: Place<'tcx>, test: &Test<'tcx>, target_blocks: FxIndexMap, BasicBlock>, ) { - let place = place_builder.to_place(self); let place_ty = place.ty(&self.local_decls, self.tcx); debug!(?place, ?place_ty); let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block); @@ -145,34 +145,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } let re_erased = tcx.lifetimes.re_erased; - let ref_string = self.temp(Ty::new_imm_ref(tcx, re_erased, ty), test.span); let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); let ref_str = self.temp(ref_str_ty, test.span); - let deref = tcx.require_lang_item(LangItem::Deref, None); - let method = trait_method(tcx, deref, sym::deref, [ty]); let eq_block = self.cfg.start_new_block(); - self.cfg.push_assign( - block, - source_info, - ref_string, - Rvalue::Ref(re_erased, BorrowKind::Shared, place), - ); - self.cfg.terminate( + // `let ref_str: &str = ::deref(&place);` + self.call_deref( block, - source_info, - TerminatorKind::Call { - func: Operand::Constant(Box::new(ConstOperand { - span: test.span, - user_ty: None, - const_: method, - })), - args: vec![Spanned { node: Operand::Move(ref_string), span: DUMMY_SP }], - destination: ref_str, - target: Some(eq_block), - unwind: UnwindAction::Continue, - call_source: CallSource::Misc, - fn_span: source_info.span, - }, + eq_block, + place, + Mutability::Not, + ty, + ref_str, + test.span, ); self.non_scalar_compare( eq_block, @@ -272,9 +256,66 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Operand::Move(expected), ); } + + TestKind::Deref { temp, mutability } => { + let ty = place_ty.ty; + let target = target_block(TestBranch::Success); + self.call_deref(block, target, place, mutability, ty, temp, test.span); + } } } + /// Perform `let temp = ::deref(&place)`. + /// or `let temp = ::deref_mut(&mut place)`. + pub(super) fn call_deref( + &mut self, + block: BasicBlock, + target_block: BasicBlock, + place: Place<'tcx>, + mutability: Mutability, + ty: Ty<'tcx>, + temp: Place<'tcx>, + span: Span, + ) { + let (trait_item, method) = match mutability { + Mutability::Not => (LangItem::Deref, sym::deref), + Mutability::Mut => (LangItem::DerefMut, sym::deref_mut), + }; + let borrow_kind = super::util::ref_pat_borrow_kind(mutability); + let source_info = self.source_info(span); + let re_erased = self.tcx.lifetimes.re_erased; + let trait_item = self.tcx.require_lang_item(trait_item, None); + let method = trait_method(self.tcx, trait_item, method, [ty]); + let ref_src = self.temp(Ty::new_ref(self.tcx, re_erased, ty, mutability), span); + // `let ref_src = &src_place;` + // or `let ref_src = &mut src_place;` + self.cfg.push_assign( + block, + source_info, + ref_src, + Rvalue::Ref(re_erased, borrow_kind, place), + ); + // `let temp = ::deref(ref_src);` + // or `let temp = ::deref_mut(ref_src);` + self.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: Operand::Constant(Box::new(ConstOperand { + span, + user_ty: None, + const_: method, + })), + args: vec![Spanned { node: Operand::Move(ref_src), span }], + destination: temp, + target: Some(target_block), + unwind: UnwindAction::Continue, + call_source: CallSource::Misc, + fn_span: source_info.span, + }, + ); + } + /// Compare using the provided built-in comparison operator fn compare( &mut self, @@ -475,7 +516,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// tighter match code if we do something a bit different. pub(super) fn sort_candidate( &mut self, - test_place: &PlaceBuilder<'tcx>, + test_place: Place<'tcx>, test: &Test<'tcx>, candidate: &mut Candidate<'_, 'tcx>, sorted_candidates: &FxIndexMap, Vec<&mut Candidate<'_, 'tcx>>>, @@ -486,8 +527,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // than one, but it'd be very unusual to have two sides that // both require tests; you'd expect one side to be simplified // away.) - let (match_pair_index, match_pair) = - candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?; + let (match_pair_index, match_pair) = candidate + .match_pairs + .iter() + .enumerate() + .find(|&(_, mp)| mp.place == Some(test_place))?; let fully_matched; let ret = match (&test.kind, &match_pair.test_case) { @@ -521,7 +565,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate .match_pairs .iter() - .any(|mp| mp.place == *test_place && is_covering_range(&mp.test_case)) + .any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case)) }; if sorted_candidates .get(&TestBranch::Failure) @@ -649,9 +693,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - // FIXME(#29623): return `Some(1)` when the values are different. - (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) - if test_val == case_val => + (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => { + if test_val == case_val { + fully_matched = true; + Some(TestBranch::Success) + } else { + fully_matched = false; + Some(TestBranch::Failure) + } + } + + (TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. }) + if test_temp == temp => { fully_matched = true; Some(TestBranch::Success) @@ -663,7 +716,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | TestKind::If | TestKind::Len { .. } | TestKind::Range { .. } - | TestKind::Eq { .. }, + | TestKind::Eq { .. } + | TestKind::Deref { .. }, _, ) => { fully_matched = false; diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index d0d49c13f133d..2f9390c22a89d 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -1,12 +1,13 @@ use crate::build::expr::as_place::{PlaceBase, PlaceBuilder}; use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase}; use crate::build::Builder; -use rustc_data_structures::fx::FxIndexSet; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_data_structures::fx::FxIndexMap; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; -use rustc_middle::ty; use rustc_middle::ty::TypeVisitableExt; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn field_match_pairs<'pat>( @@ -95,35 +96,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { pub(in crate::build) fn new( - mut place: PlaceBuilder<'tcx>, + mut place_builder: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, cx: &mut Builder<'_, 'tcx>, ) -> MatchPair<'pat, 'tcx> { // Force the place type to the pattern's type. // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? - if let Some(resolved) = place.resolve_upvar(cx) { - place = resolved; + if let Some(resolved) = place_builder.resolve_upvar(cx) { + place_builder = resolved; } // Only add the OpaqueCast projection if the given place is an opaque type and the // expected type from the pattern is not. - let may_need_cast = match place.base() { + let may_need_cast = match place_builder.base() { PlaceBase::Local(local) => { - let ty = Place::ty_from(local, place.projection(), &cx.local_decls, cx.tcx).ty; + let ty = + Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty; ty != pattern.ty && ty.has_opaque_types() } _ => true, }; if may_need_cast { - place = place.project(ProjectionElem::OpaqueCast(pattern.ty)); + place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty)); } + let place = place_builder.try_to_place(cx); let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None }; let mut subpairs = Vec::new(); let test_case = match pattern.kind { PatKind::Never | PatKind::Wild | PatKind::Error(_) => default_irrefutable(), PatKind::Or { ref pats } => TestCase::Or { - pats: pats.iter().map(|pat| FlatPat::new(place.clone(), pat, cx)).collect(), + pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(), }, PatKind::Range(ref range) => { @@ -142,26 +145,18 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { .. } => { // Apply the type ascription to the value at `match_pair.place` - let ascription = place.try_to_place(cx).map(|source| super::Ascription { + let ascription = place.map(|source| super::Ascription { annotation: annotation.clone(), source, variance, }); - subpairs.push(MatchPair::new(place.clone(), subpattern, cx)); + subpairs.push(MatchPair::new(place_builder, subpattern, cx)); TestCase::Irrefutable { ascription, binding: None } } - PatKind::Binding { - name: _, - mutability: _, - mode, - var, - ty: _, - ref subpattern, - is_primary: _, - } => { - let binding = place.try_to_place(cx).map(|source| super::Binding { + PatKind::Binding { mode, var, ref subpattern, .. } => { + let binding = place.map(|source| super::Binding { span: pattern.span, source, var_id: var, @@ -170,24 +165,23 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { if let Some(subpattern) = subpattern.as_ref() { // this is the `x @ P` case; have to keep matching against `P` now - subpairs.push(MatchPair::new(place.clone(), subpattern, cx)); + subpairs.push(MatchPair::new(place_builder, subpattern, cx)); } TestCase::Irrefutable { ascription: None, binding } } PatKind::InlineConstant { subpattern: ref pattern, def, .. } => { // Apply a type ascription for the inline constant to the value at `match_pair.place` - let ascription = place.try_to_place(cx).map(|source| { + let ascription = place.map(|source| { let span = pattern.span; let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id()); let args = ty::InlineConstArgs::new( cx.tcx, ty::InlineConstArgsParts { parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id), - ty: cx.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - }), + ty: cx + .infcx + .next_ty_var(TypeVariableOrigin { param_def_id: None, span }), }, ) .args; @@ -203,16 +197,16 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { super::Ascription { annotation, source, variance: ty::Contravariant } }); - subpairs.push(MatchPair::new(place.clone(), pattern, cx)); + subpairs.push(MatchPair::new(place_builder, pattern, cx)); TestCase::Irrefutable { ascription, binding: None } } PatKind::Array { ref prefix, ref slice, ref suffix } => { - cx.prefix_slice_suffix(&mut subpairs, &place, prefix, slice, suffix); + cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); default_irrefutable() } PatKind::Slice { ref prefix, ref slice, ref suffix } => { - cx.prefix_slice_suffix(&mut subpairs, &place, prefix, slice, suffix); + cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); if prefix.is_empty() && slice.is_some() && suffix.is_empty() { default_irrefutable() @@ -225,7 +219,7 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { } PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => { - let downcast_place = place.clone().downcast(adt_def, variant_index); // `(x as Variant)` + let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` subpairs = cx.field_match_pairs(downcast_place, subpatterns); let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { @@ -247,15 +241,25 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { } PatKind::Leaf { ref subpatterns } => { - subpairs = cx.field_match_pairs(place.clone(), subpatterns); + subpairs = cx.field_match_pairs(place_builder, subpatterns); default_irrefutable() } PatKind::Deref { ref subpattern } => { - let place_builder = place.clone().deref(); - subpairs.push(MatchPair::new(place_builder, subpattern, cx)); + subpairs.push(MatchPair::new(place_builder.deref(), subpattern, cx)); default_irrefutable() } + + PatKind::DerefPattern { ref subpattern, mutability } => { + // Create a new temporary for each deref pattern. + // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? + let temp = cx.temp( + Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), + pattern.span, + ); + subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx)); + TestCase::Deref { temp, mutability } + } }; MatchPair { place, test_case, subpairs, pattern } @@ -264,19 +268,103 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> { cx: &'a mut Builder<'b, 'tcx>, - fake_borrows: FxIndexSet>, + /// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from + /// bindings inside deref patterns. + scrutinee_base: PlaceBase, + /// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest + /// borrow (i.e. Deep > Shallow). + /// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are + /// dereferences are also borrowed with the same of stronger borrow kind. + fake_borrows: FxIndexMap, FakeBorrowKind>, +} + +/// Determine the set of places that have to be stable across match guards. +/// +/// Returns a list of places that need a fake borrow along with a local to store it. +/// +/// Match exhaustiveness checking is not able to handle the case where the place being matched on is +/// mutated in the guards. We add "fake borrows" to the guards that prevent any mutation of the +/// place being matched. There are a some subtleties: +/// +/// 1. Borrowing `*x` doesn't prevent assigning to `x`. If `x` is a shared reference, the borrow +/// isn't even tracked. As such we have to add fake borrows of any prefixes of a place. +/// 2. We don't want `match x { (Some(_), _) => (), .. }` to conflict with mutable borrows of `x.1`, so we +/// only add fake borrows for places which are bound or tested by the match. +/// 3. We don't want `match x { Some(_) => (), .. }` to conflict with mutable borrows of `(x as +/// Some).0`, so the borrows are a special shallow borrow that only affects the place and not its +/// projections. +/// ```rust +/// let mut x = (Some(0), true); +/// match x { +/// (Some(_), false) => {} +/// _ if { if let Some(ref mut y) = x.0 { *y += 1 }; true } => {} +/// _ => {} +/// } +/// ``` +/// 4. The fake borrows may be of places in inactive variants, e.g. here we need to fake borrow `x` +/// and `(x as Some).0`, but when we reach the guard `x` may not be `Some`. +/// ```rust +/// let mut x = (Some(Some(0)), true); +/// match x { +/// (Some(Some(_)), false) => {} +/// _ if { if let Some(Some(ref mut y)) = x.0 { *y += 1 }; true } => {} +/// _ => {} +/// } +/// ``` +/// So it would be UB to generate code for the fake borrows. They therefore have to be removed by +/// a MIR pass run after borrow checking. +pub(super) fn collect_fake_borrows<'tcx>( + cx: &mut Builder<'_, 'tcx>, + candidates: &[&mut Candidate<'_, 'tcx>], + temp_span: Span, + scrutinee_base: PlaceBase, +) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> { + let mut collector = + FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() }; + for candidate in candidates.iter() { + collector.visit_candidate(candidate); + } + let fake_borrows = collector.fake_borrows; + debug!("add_fake_borrows fake_borrows = {:?}", fake_borrows); + let tcx = cx.tcx; + fake_borrows + .iter() + .map(|(matched_place, borrow_kind)| { + let fake_borrow_deref_ty = matched_place.ty(&cx.local_decls, tcx).ty; + let fake_borrow_ty = + Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty); + let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span); + fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow)); + let fake_borrow_temp = cx.local_decls.push(fake_borrow_temp); + (*matched_place, fake_borrow_temp, *borrow_kind) + }) + .collect() } impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { - pub(super) fn collect_fake_borrows( - cx: &'a mut Builder<'b, 'tcx>, - candidates: &[&mut Candidate<'_, 'tcx>], - ) -> FxIndexSet> { - let mut collector = Self { cx, fake_borrows: FxIndexSet::default() }; - for candidate in candidates.iter() { - collector.visit_candidate(candidate); + // Fake borrow this place and its dereference prefixes. + fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) { + if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) { + return; + } + self.fake_borrows.insert(place, kind); + // Also fake borrow the prefixes of any fake borrow. + self.fake_borrow_deref_prefixes(place, kind); + } + + // Fake borrow the prefixes of this place that are dereferences. + fn fake_borrow_deref_prefixes(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) { + for (place_ref, elem) in place.as_ref().iter_projections().rev() { + if let ProjectionElem::Deref = elem { + // Insert a shallow borrow after a deref. For other projections the borrow of + // `place_ref` will conflict with any mutation of `place.base`. + let place = place_ref.to_place(self.cx.tcx); + if self.fake_borrows.get(&place).is_some_and(|k| *k >= kind) { + return; + } + self.fake_borrows.insert(place, kind); + } } - collector.fake_borrows } fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) { @@ -302,10 +390,27 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { for flat_pat in pats.iter() { self.visit_flat_pat(flat_pat) } + } else if matches!(match_pair.test_case, TestCase::Deref { .. }) { + // The subpairs of a deref pattern are all places relative to the deref temporary, so we + // don't fake borrow them. Problem is, if we only shallowly fake-borrowed + // `match_pair.place`, this would allow: + // ``` + // let mut b = Box::new(false); + // match b { + // deref!(true) => {} // not reached because `*b == false` + // _ if { *b = true; false } => {} // not reached because the guard is `false` + // deref!(false) => {} // not reached because the guard changed it + // // UB because we reached the unreachable. + // } + // ``` + // Hence we fake borrow using a deep borrow. + if let Some(place) = match_pair.place { + self.fake_borrow(place, FakeBorrowKind::Deep); + } } else { // Insert a Shallow borrow of any place that is switched on. - if let Some(resolved_place) = match_pair.place.try_to_place(self.cx) { - self.fake_borrows.insert(resolved_place); + if let Some(place) = match_pair.place { + self.fake_borrow(place, FakeBorrowKind::Shallow); } for subpair in &match_pair.subpairs { @@ -315,6 +420,14 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } fn visit_binding(&mut self, Binding { source, .. }: &Binding<'tcx>) { + if let PlaceBase::Local(l) = self.scrutinee_base + && l != source.local + { + // The base of this place is a temporary created for deref patterns. We don't emit fake + // borrows for these as they are not initialized in all branches. + return; + } + // Insert a borrows of prefixes of places that are bound and are // behind a dereference projection. // @@ -331,12 +444,20 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { // y if { y == 1 && (x = &2) == () } => y, // _ => 3, // } - if let Some(i) = source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) { - let proj_base = &source.projection[..i]; - self.fake_borrows.insert(Place { - local: source.local, - projection: self.cx.tcx.mk_place_elems(proj_base), - }); - } + // + // We don't just fake borrow the whole place because this is allowed: + // match u { + // _ if { u = true; false } => (), + // x => (), + // } + self.fake_borrow_deref_prefixes(*source, FakeBorrowKind::Shallow); + } +} + +#[must_use] +pub fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind { + match ref_mutability { + Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default }, + Mutability::Not => BorrowKind::Shared, } } diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 411119b521bcd..d6e6cc957b4be 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -7,10 +7,9 @@ use rustc_ast::attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::Node; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Node}; use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -18,9 +17,8 @@ use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; -use rustc_middle::thir::{ - self, BindingMode, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir, -}; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -30,13 +28,6 @@ use rustc_target::spec::abi::Abi; use super::lints; -pub(crate) fn mir_built( - tcx: TyCtxt<'_>, - def: LocalDefId, -) -> &rustc_data_structures::steal::Steal> { - tcx.alloc_steal_mir(mir_build(tcx, def)) -} - pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, @@ -54,7 +45,8 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( } /// Construct the MIR for a given `DefId`. -fn mir_build<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> { +pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> { + let tcx = tcx.tcx; tcx.ensure_with_value().thir_abstract_const(def); if let Err(e) = tcx.check_match(def) { return construct_error(tcx, def, e); @@ -74,17 +66,10 @@ fn mir_build<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> { // maybe move the check to a MIR pass? tcx.ensure().check_liveness(def); - if tcx.sess.opts.unstable_opts.thir_unsafeck { - // Don't steal here if THIR unsafeck is being used. Instead - // steal in unsafeck. This is so that pattern inline constants - // can be evaluated as part of building the THIR of the parent - // function without a cycle. - build_mir(&thir.borrow()) - } else { - // We ran all queries that depended on THIR at the beginning - // of `mir_build`, so now we can steal it - build_mir(&thir.steal()) - } + // Don't steal here, instead steal in unsafeck. This is so that + // pattern inline constants can be evaluated as part of building the + // THIR of the parent function without a cycle. + build_mir(&thir.borrow()) } }; @@ -173,7 +158,7 @@ struct Builder<'a, 'tcx> { cfg: CFG<'tcx>, def_id: LocalDefId, - hir_id: hir::HirId, + hir_id: HirId, parent_module: DefId, check_overflow: bool, fn_span: Span, @@ -198,9 +183,6 @@ struct Builder<'a, 'tcx> { /// `{ STMTS; EXPR1 } + EXPR2`. block_context: BlockContext, - /// The current unsafe block in scope - in_scope_unsafe: Safety, - /// The vector of all scopes that we have created thus far; /// we track this for debuginfo later. source_scopes: IndexVec>, @@ -240,7 +222,7 @@ struct Builder<'a, 'tcx> { coverage_branch_info: Option, } -type CaptureMap<'tcx> = SortedIndexMultiMap>; +type CaptureMap<'tcx> = SortedIndexMultiMap>; #[derive(Debug)] struct Capture<'tcx> { @@ -342,7 +324,7 @@ struct GuardFrameLocal { } impl GuardFrameLocal { - fn new(id: LocalVarId, _binding_mode: BindingMode) -> Self { + fn new(id: LocalVarId) -> Self { GuardFrameLocal { id } } } @@ -478,11 +460,6 @@ fn construct_fn<'tcx>( .output .span(); - let safety = match fn_sig.unsafety { - hir::Unsafety::Normal => Safety::Safe, - hir::Unsafety::Unsafe => Safety::FnUnsafe, - }; - let mut abi = fn_sig.abi; if let DefKind::Closure = tcx.def_kind(fn_def) { // HACK(eddyb) Avoid having RustCall on closures, @@ -528,7 +505,6 @@ fn construct_fn<'tcx>( fn_id, span_with_body, arguments.len(), - safety, return_ty, return_ty_span, coroutine, @@ -598,18 +574,8 @@ fn construct_const<'a, 'tcx>( }; let infcx = tcx.infer_ctxt().build(); - let mut builder = Builder::new( - thir, - infcx, - def, - hir_id, - span, - 0, - Safety::Safe, - const_ty, - const_ty_span, - None, - ); + let mut builder = + Builder::new(thir, infcx, def, hir_id, span, 0, const_ty, const_ty_span, None); let mut block = START_BLOCK; unpack!(block = builder.expr_into_dest(Place::return_place(), block, expr)); @@ -731,10 +697,7 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) - parent_scope: None, inlined: None, inlined_parent_scope: None, - local_data: ClearCrossCrate::Set(SourceScopeLocalData { - lint_root: hir_id, - safety: Safety::Safe, - }), + local_data: ClearCrossCrate::Set(SourceScopeLocalData { lint_root: hir_id }), }); cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); @@ -758,10 +721,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { thir: &'a Thir<'tcx>, infcx: InferCtxt<'tcx>, def: LocalDefId, - hir_id: hir::HirId, + hir_id: HirId, span: Span, arg_count: usize, - safety: Safety, return_ty: Ty<'tcx>, return_span: Span, coroutine: Option>>, @@ -803,7 +765,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard_context: vec![], fixed_temps: Default::default(), fixed_temps_scope: None, - in_scope_unsafe: safety, local_decls: IndexVec::from_elem_n(LocalDecl::new(return_ty, return_span), 1), canonical_user_type_annotations: IndexVec::new(), upvars: CaptureMap::new(), @@ -815,10 +776,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); - assert_eq!( - builder.new_source_scope(span, lint_level, Some(safety)), - OUTERMOST_SOURCE_SCOPE - ); + assert_eq!(builder.new_source_scope(span, lint_level), OUTERMOST_SOURCE_SCOPE); builder.source_scopes[OUTERMOST_SOURCE_SCOPE].parent_scope = None; builder @@ -972,9 +930,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match pat.kind { // Don't introduce extra copies for simple bindings PatKind::Binding { - mutability, var, - mode: BindingMode::ByValue, + mode: BindingMode(ByRef::No, mutability), subpattern: None, .. } => { @@ -984,7 +941,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(kind) = param.self_kind { LocalInfo::User(BindingForm::ImplicitSelf(kind)) } else { - let binding_mode = ty::BindingMode::BindByValue(mutability); + let binding_mode = BindingMode(ByRef::No, mutability); LocalInfo::User(BindingForm::Var(VarBindingForm { binding_mode, opt_ty_info: param.ty_span, @@ -1013,13 +970,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(source_scope) = scope { self.source_scope = source_scope; } - - self.expr_into_dest(Place::return_place(), block, expr_id) + if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden) { + let source_info = self.source_info(rustc_span::DUMMY_SP); + self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); + self.cfg.start_new_block().unit() + } else { + self.expr_into_dest(Place::return_place(), block, expr_id) + } } fn set_correct_source_scope_for_arg( &mut self, - arg_hir_id: hir::HirId, + arg_hir_id: HirId, original_source_scope: SourceScope, pattern_span: Span, ) { @@ -1028,14 +990,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .as_ref() .assert_crate_local() .lint_root; - self.maybe_new_source_scope(pattern_span, None, arg_hir_id, parent_id); + self.maybe_new_source_scope(pattern_span, arg_hir_id, parent_id); } fn get_unit_temp(&mut self) -> Place<'tcx> { match self.unit_temp { Some(tmp) => tmp, None => { - let ty = Ty::new_unit(self.tcx); + let ty = self.tcx.types.unit; let fn_span = self.fn_span; let tmp = self.temp(ty, fn_span); self.unit_temp = Some(tmp); @@ -1061,7 +1023,13 @@ pub(crate) fn parse_float_into_scalar( let num = num.as_str(); match float_ty { // FIXME(f16_f128): When available, compare to the library parser as with `f32` and `f64` - ty::FloatTy::F16 => num.parse::().ok().map(Scalar::from_f16), + ty::FloatTy::F16 => { + let mut f = num.parse::().ok()?; + if neg { + f = -f; + } + Some(Scalar::from_f16(f)) + } ty::FloatTy::F32 => { let Ok(rust_f) = num.parse::() else { return None }; let mut f = num @@ -1109,7 +1077,13 @@ pub(crate) fn parse_float_into_scalar( Some(Scalar::from_f64(f)) } // FIXME(f16_f128): When available, compare to the library parser as with `f32` and `f64` - ty::FloatTy::F128 => num.parse::().ok().map(Scalar::from_f128), + ty::FloatTy::F128 => { + let mut f = num.parse::().ok()?; + if neg { + f = -f; + } + Some(Scalar::from_f128(f)) + } } } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 88ac05cabb659..2d31e84aba7da 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -190,7 +190,7 @@ rustc_index::newtype_index! { struct DropIdx {} } -const ROOT_NODE: DropIdx = DropIdx::from_u32(0); +const ROOT_NODE: DropIdx = DropIdx::ZERO; /// A tree of drops that we have deferred lowering. It's used for: /// @@ -578,7 +578,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let LintLevel::Explicit(current_hir_id) = lint_level { let parent_id = self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root; - self.maybe_new_source_scope(region_scope.1.span, None, current_hir_id, parent_id); + self.maybe_new_source_scope(region_scope.1.span, current_hir_id, parent_id); } self.push_scope(region_scope); let mut block; @@ -767,14 +767,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn maybe_new_source_scope( &mut self, span: Span, - safety: Option, current_id: HirId, parent_id: HirId, ) { let (current_root, parent_root) = if self.tcx.sess.opts.unstable_opts.maximal_hir_to_mir_coverage { // Some consumers of rustc need to map MIR locations back to HIR nodes. Currently - // the the only part of rustc that tracks MIR -> HIR is the + // the only part of rustc that tracks MIR -> HIR is the // `SourceScopeLocalData::lint_root` field that tracks lint levels for MIR // locations. Normally the number of source scopes is limited to the set of nodes // with lint annotations. The -Zmaximal-hir-to-mir-coverage flag changes this @@ -797,7 +796,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if current_root != parent_root { let lint_level = LintLevel::Explicit(current_root); - self.source_scope = self.new_source_scope(span, lint_level, safety); + self.source_scope = self.new_source_scope(span, lint_level); } } @@ -846,18 +845,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Creates a new source scope, nested in the current one. - pub(crate) fn new_source_scope( - &mut self, - span: Span, - lint_level: LintLevel, - safety: Option, - ) -> SourceScope { + pub(crate) fn new_source_scope(&mut self, span: Span, lint_level: LintLevel) -> SourceScope { let parent = self.source_scope; debug!( - "new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}", + "new_source_scope({:?}, {:?}) - parent({:?})={:?}", span, lint_level, - safety, parent, self.source_scopes.get(parent) ); @@ -867,9 +860,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root }, - safety: safety.unwrap_or_else(|| { - self.source_scopes[parent].local_data.as_ref().assert_crate_local().safety - }), }; self.source_scopes.push(SourceScopeData { span, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 1ce8da162bfa5..227d19c3e43c5 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -1,12 +1,10 @@ -use std::borrow::Cow; - use crate::build::ExprCategory; use crate::errors::*; -use rustc_middle::thir::visit::Visitor; use rustc_errors::DiagArgValue; -use rustc_hir as hir; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; use rustc_middle::mir::BorrowKind; +use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::*; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; @@ -16,6 +14,7 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; +use std::borrow::Cow; use std::mem; use std::ops::Bound; @@ -24,7 +23,7 @@ struct UnsafetyVisitor<'a, 'tcx> { thir: &'a Thir<'tcx>, /// The `HirId` of the current scope, which would be the `HirId` /// of the current HIR node, modulo adjustments. Used for lint levels. - hir_context: hir::HirId, + hir_context: HirId, /// The current "safety context". This notably tracks whether we are in an /// `unsafe` block, and whether it has been used. safety_context: SafetyContext, @@ -123,7 +122,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { fn warn_unused_unsafe( &mut self, - hir_id: hir::HirId, + hir_id: HirId, block_span: Span, enclosing_unsafe: Option, ) { @@ -250,6 +249,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | PatKind::Variant { .. } | PatKind::Leaf { .. } | PatKind::Deref { .. } + | PatKind::DerefPattern { .. } | PatKind::Range { .. } | PatKind::Slice { .. } | PatKind::Array { .. } => { @@ -288,29 +288,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_pat(self, pat); } } - PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => { + PatKind::Binding { mode: BindingMode(ByRef::Yes(rm), _), ty, .. } => { if self.inside_adt { let ty::Ref(_, ty, _) = ty.kind() else { span_bug!( pat.span, - "BindingMode::ByRef in pattern, but found non-reference type {}", + "ByRef::Yes in pattern, but found non-reference type {}", ty ); }; - match borrow_kind { - BorrowKind::Fake | BorrowKind::Shared => { + match rm { + Mutability::Not => { if !ty.is_freeze(self.tcx, self.param_env) { self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField); } } - BorrowKind::Mut { .. } => { + Mutability::Mut { .. } => { self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField); } } } visit::walk_pat(self, pat); } - PatKind::Deref { .. } => { + PatKind::Deref { .. } | PatKind::DerefPattern { .. } => { let old_inside_adt = std::mem::replace(&mut self.inside_adt, false); visit::walk_pat(self, pat); self.inside_adt = old_inside_adt; @@ -513,7 +513,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_expr(&mut visitor, expr); if visitor.found { match borrow_kind { - BorrowKind::Fake | BorrowKind::Shared + BorrowKind::Fake(_) | BorrowKind::Shared if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) => { self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField) @@ -521,7 +521,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { BorrowKind::Mut { .. } => { self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField) } - BorrowKind::Fake | BorrowKind::Shared => {} + BorrowKind::Fake(_) | BorrowKind::Shared => {} } } } @@ -536,22 +536,17 @@ enum SafetyContext { Safe, BuiltinUnsafeBlock, UnsafeFn, - UnsafeBlock { - span: Span, - hir_id: hir::HirId, - used: bool, - nested_used_blocks: Vec, - }, + UnsafeBlock { span: Span, hir_id: HirId, used: bool, nested_used_blocks: Vec }, } #[derive(Clone, Copy)] struct NestedUsedBlock { - hir_id: hir::HirId, + hir_id: HirId, span: Span, } struct UnusedUnsafeWarning { - hir_id: hir::HirId, + hir_id: HirId, block_span: Span, enclosing_unsafe: Option, } @@ -584,7 +579,7 @@ impl UnsafeOpKind { pub fn emit_unsafe_op_in_unsafe_fn_lint( &self, tcx: TyCtxt<'_>, - hir_id: hir::HirId, + hir_id: HirId, span: Span, suggest_unsafe_block: bool, ) { @@ -725,7 +720,7 @@ impl UnsafeOpKind { &self, tcx: TyCtxt<'_>, span: Span, - hir_context: hir::HirId, + hir_context: HirId, unsafe_op_in_unsafe_fn_allowed: bool, ) { let note_non_inherited = tcx.hir().parent_iter(hir_context).find(|(id, node)| { @@ -908,11 +903,6 @@ impl UnsafeOpKind { } pub fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { - // THIR unsafeck can be disabled with `-Z thir-unsafeck=off` - if !tcx.sess.opts.unstable_opts.thir_unsafeck { - return; - } - // Closures and inline consts are handled by their owner, if it has a body // Also, don't safety check custom MIR if tcx.is_typeck_child(def.to_def_id()) || tcx.has_attr(def, sym::custom_mir) { diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 848da56f98168..f67113afd6d9d 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -423,7 +423,7 @@ impl Subdiagnostic for UnsafeNotInheritedLintNote { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body); let body_start = self.body_span.shrink_to_lo(); @@ -456,8 +456,8 @@ pub enum UnusedUnsafeEnclosing { pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> { pub cx: &'m RustcPatCtxt<'p, 'tcx>, - pub expr_span: Span, - pub span: Span, + pub scrut_span: Span, + pub braces_span: Option, pub ty: Ty<'tcx>, } @@ -465,7 +465,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'_, G> { let mut diag = Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty); - diag.span(self.span); + diag.span(self.scrut_span); diag.code(E0004); let peeled_ty = self.ty.peel_refs(); diag.arg("ty", self.ty); @@ -502,26 +502,19 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo } } - let mut suggestion = None; let sm = self.cx.tcx.sess.source_map(); - if self.span.eq_ctxt(self.expr_span) { + if let Some(braces_span) = self.braces_span { // Get the span for the empty match body `{}`. - let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) { + let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.scrut_span) + { (format!("\n{snippet}"), " ") } else { (" ".to_string(), "") }; - suggestion = Some(( - self.span.shrink_to_hi().with_hi(self.expr_span.hi()), - format!(" {{{indentation}{more}_ => todo!(),{indentation}}}",), - )); - } - - if let Some((span, sugg)) = suggestion { diag.span_suggestion_verbose( - span, + braces_span, fluent::mir_build_suggestion, - sugg, + format!(" {{{indentation}{more}_ => todo!(),{indentation}}}"), Applicability::HasPlaceholders, ); } else { @@ -825,6 +818,15 @@ pub struct NontrivialStructuralMatch<'tcx> { pub non_sm_ty: Ty<'tcx>, } +#[derive(Diagnostic)] +#[diag(mir_build_exceeds_mcdc_condition_num_limit)] +pub(crate) struct MCDCExceedsConditionNumLimit { + #[primary_span] + pub span: Span, + pub conditions_num: usize, + pub max_conditions_num: usize, +} + #[derive(Diagnostic)] #[diag(mir_build_pattern_not_covered, code = E0005)] pub(crate) struct PatternNotCovered<'s, 'tcx> { @@ -869,7 +871,7 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { diag.arg("ty", self.ty); let mut spans = MultiSpan::from(self.adt_def_span); diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index e3f202b7f1882..e79e3b887fb43 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -5,11 +5,9 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(assert_matches)] -#![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![feature(try_blocks)] #[macro_use] @@ -23,18 +21,18 @@ mod errors; pub mod lints; mod thir; -use rustc_middle::query::Providers; +use rustc_middle::util::Providers; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { providers.check_match = thir::pattern::check_match; providers.lit_to_const = thir::constant::lit_to_const; - providers.mir_built = build::mir_built; + providers.hooks.build_mir = build::mir_build; providers.closure_saved_names_of_captured_variables = build::closure_saved_names_of_captured_variables; providers.check_unsafety = check_unsafety::check_unsafety; providers.thir_body = thir::cx::thir_body; - providers.thir_tree = thir::print::thir_tree; - providers.thir_flat = thir::print::thir_flat; + providers.hooks.thir_tree = thir::print::thir_tree; + providers.hooks.thir_flat = thir::print::thir_flat; } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 52d32a3b6266e..c697e16217bd4 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -125,11 +125,7 @@ impl<'tcx> Cx<'tcx> { expr = Expr { temp_lifetime, - ty: Ty::new_ref( - self.tcx, - deref.region, - ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }, - ), + ty: Ty::new_ref(self.tcx, deref.region, expr.ty, deref.mutbl), span, kind: ExprKind::Borrow { borrow_kind: deref.mutbl.to_borrow_kind(), @@ -720,10 +716,11 @@ impl<'tcx> Cx<'tcx> { then: self.mirror_expr(then), else_opt: else_opt.map(|el| self.mirror_expr(el)), }, - hir::ExprKind::Match(discr, arms, _) => ExprKind::Match { + hir::ExprKind::Match(discr, arms, match_source) => ExprKind::Match { scrutinee: self.mirror_expr(discr), scrutinee_hir_id: discr.hir_id, arms: arms.iter().map(|a| self.convert_arm(a)).collect(), + match_source, }, hir::ExprKind::Loop(body, ..) => { let block_ty = self.typeck_results().node_type(body.hir_id); @@ -1021,7 +1018,7 @@ impl<'tcx> Cx<'tcx> { let ty::Ref(region, _, mutbl) = *self.thir[args[0]].ty.kind() else { span_bug!(span, "overloaded_place: receiver is not a reference"); }; - let ref_ty = Ty::new_ref(self.tcx, region, ty::TypeAndMut { ty: place_ty, mutbl }); + let ref_ty = Ty::new_ref(self.tcx, region, place_ty, mutbl); // construct the complete expression `foo()` for the overloaded call, // which will yield the &T type diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index f4f591d15b933..79738b5403548 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -15,7 +15,7 @@ use rustc_hir::HirId; use rustc_hir::Node; use rustc_middle::middle::region; use rustc_middle::thir::*; -use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt}; +use rustc_middle::ty::{self, RvalueScopes, TyCtxt}; pub(crate) fn thir_body( tcx: TyCtxt<'_>, @@ -39,7 +39,7 @@ pub(crate) fn thir_body( // It will always be `()` in this case. if tcx.is_coroutine(owner_def.to_def_id()) && body.params.is_empty() { cx.thir.params.push(Param { - ty: Ty::new_unit(tcx), + ty: tcx.types.unit, pat: None, ty_span: None, self_kind: None, 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 20d3b3f98ce31..25ab904670618 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -10,13 +10,10 @@ use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, -}; -use rustc_hir as hir; +use rustc_errors::{codes::*, struct_span_code_err, Applicability, ErrorGuaranteed, MultiSpan}; use rustc_hir::def::*; use rustc_hir::def_id::LocalDefId; -use rustc_hir::HirId; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId}; use rustc_middle::middle::limits::get_limit_size; use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::*; @@ -25,7 +22,6 @@ use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_session::lint::builtin::{ BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS, }; -use rustc_session::Session; use rustc_span::hygiene::DesugaringKind; use rustc_span::{sym, Span}; @@ -65,10 +61,6 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err visitor.error } -fn create_e0004(sess: &Session, sp: Span, error_message: String) -> Diag<'_> { - struct_span_code_err!(sess.dcx(), sp, E0004, "{}", &error_message) -} - #[derive(Debug, Copy, Clone, PartialEq)] enum RefutableFlag { Irrefutable, @@ -145,16 +137,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> { }); return; } - ExprKind::Match { scrutinee, scrutinee_hir_id, box ref arms } => { - let source = match ex.span.desugaring_kind() { - Some(DesugaringKind::ForLoop) => hir::MatchSource::ForLoopDesugar, - Some(DesugaringKind::QuestionMark) => { - hir::MatchSource::TryDesugar(scrutinee_hir_id) - } - Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar, - _ => hir::MatchSource::Normal, - }; - self.check_match(scrutinee, arms, source, ex.span); + ExprKind::Match { scrutinee, scrutinee_hir_id: _, box ref arms, match_source } => { + self.check_match(scrutinee, arms, match_source, ex.span); } ExprKind::Let { box ref pat, expr } => { self.check_let(pat, Some(expr), ex.span); @@ -481,6 +465,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { // when the iterator is an uninhabited type. unreachable_code will trigger instead. hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {} hir::MatchSource::ForLoopDesugar + | hir::MatchSource::Postfix | hir::MatchSource::Normal | hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report), // Unreachable patterns in try and await expressions occur when one of @@ -505,8 +490,41 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { None, ); } else { + // span after scrutinee, or after `.match`. That is, the braces, arms, + // and any whitespace preceding the braces. + let braces_span = match source { + hir::MatchSource::Normal => scrut + .span + .find_ancestor_in_same_ctxt(expr_span) + .map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())), + hir::MatchSource::Postfix => { + // This is horrendous, and we should deal with it by just + // stashing the span of the braces somewhere (like in the match source). + scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| { + let sm = self.tcx.sess.source_map(); + let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true); + if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") { + let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi()); + // We also need to extend backwards for whitespace + sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok() + } else { + None + } + }) + } + hir::MatchSource::ForLoopDesugar + | hir::MatchSource::TryDesugar(_) + | hir::MatchSource::AwaitDesugar + | hir::MatchSource::FormatArgs => None, + }; self.error = Err(report_non_exhaustive_match( - &cx, self.thir, scrut.ty, scrut.span, witnesses, arms, expr_span, + &cx, + self.thir, + scrut.ty, + scrut.span, + witnesses, + arms, + braces_span, )); } } @@ -649,6 +667,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)); @@ -722,13 +741,14 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: let sess = cx.tcx.sess; // Get the binding move, extract the mutability if by-ref. - let mut_outer = match mode { - BindingMode::ByValue if is_binding_by_move(ty) => { + let mut_outer = match mode.0 { + ByRef::No if is_binding_by_move(ty) => { // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`. let mut conflicts_ref = Vec::new(); - sub.each_binding(|_, mode, _, span| match mode { - BindingMode::ByValue => {} - BindingMode::ByRef(_) => conflicts_ref.push(span), + sub.each_binding(|_, mode, _, span| { + if matches!(mode, ByRef::Yes(_)) { + conflicts_ref.push(span) + } }); if !conflicts_ref.is_empty() { sess.dcx().emit_err(BorrowOfMovedValue { @@ -741,8 +761,8 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: } return; } - BindingMode::ByValue => return, - BindingMode::ByRef(m) => m.mutability(), + ByRef::No => return, + ByRef::Yes(m) => m, }; // We now have `ref $mut_outer binding @ sub` (semantically). @@ -752,7 +772,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: let mut conflicts_mut_ref = Vec::new(); sub.each_binding(|name, mode, ty, span| { match mode { - BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) { + ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) { // Both sides are `ref`. (Mutability::Not, Mutability::Not) => {} // 2x `ref mut`. @@ -766,10 +786,10 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: conflicts_mut_ref.push(Conflict::Ref { span, name }) } }, - BindingMode::ByValue if is_binding_by_move(ty) => { + ByRef::No if is_binding_by_move(ty) => { conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict. } - BindingMode::ByValue => {} // `ref mut?` + by-copy is fine. + ByRef::No => {} // `ref mut?` + by-copy is fine. } }); @@ -812,8 +832,7 @@ fn check_for_bindings_named_same_as_variants( ) { if let PatKind::Binding { name, - mode: BindingMode::ByValue, - mutability: Mutability::Not, + mode: BindingMode(ByRef::No, Mutability::Not), subpattern: None, ty, .. @@ -929,7 +948,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( sp: Span, witnesses: Vec>, arms: &[ArmId], - expr_span: Span, + braces_span: Option, ) -> ErrorGuaranteed { let is_empty_match = arms.is_empty(); let non_empty_enum = match scrut_ty.kind() { @@ -938,43 +957,31 @@ fn report_non_exhaustive_match<'p, 'tcx>( }; // In the case of an empty match, replace the '`_` not covered' diagnostic with something more // informative. - let mut err; - let pattern; - let patterns_len; if is_empty_match && !non_empty_enum { return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty { cx, - expr_span, - span: sp, + scrut_span: sp, + braces_span, ty: scrut_ty, }); - } else { - // FIXME: migration of this diagnostic will require list support - let joined_patterns = joined_uncovered_patterns(cx, &witnesses); - err = create_e0004( - cx.tcx.sess, - sp, - format!("non-exhaustive patterns: {joined_patterns} not covered"), - ); - err.span_label( - sp, - format!( - "pattern{} {} not covered", - rustc_errors::pluralize!(witnesses.len()), - joined_patterns - ), - ); - patterns_len = witnesses.len(); - pattern = if witnesses.len() < 4 { - witnesses - .iter() - .map(|witness| cx.hoist_witness_pat(witness).to_string()) - .collect::>() - .join(" | ") - } else { - "_".to_string() - }; - }; + } + + // FIXME: migration of this diagnostic will require list support + let joined_patterns = joined_uncovered_patterns(cx, &witnesses); + let mut err = struct_span_code_err!( + cx.tcx.dcx(), + sp, + E0004, + "non-exhaustive patterns: {joined_patterns} not covered" + ); + err.span_label( + sp, + format!( + "pattern{} {} not covered", + rustc_errors::pluralize!(witnesses.len()), + joined_patterns + ), + ); // Point at the definition of non-covered `enum` variants. if let Some(AdtDefinedHere { adt_def_span, ty, variants }) = @@ -1021,10 +1028,27 @@ fn report_non_exhaustive_match<'p, 'tcx>( } } + // Whether we suggest the actual missing patterns or `_`. + let suggest_the_witnesses = witnesses.len() < 4; + let suggested_arm = if suggest_the_witnesses { + let pattern = witnesses + .iter() + .map(|witness| cx.hoist_witness_pat(witness).to_string()) + .collect::>() + .join(" | "); + if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns { + // Arms with a never pattern don't take a body. + pattern + } else { + format!("{pattern} => todo!()") + } + } else { + format!("_ => todo!()") + }; let mut suggestion = None; let sm = cx.tcx.sess.source_map(); match arms { - [] if sp.eq_ctxt(expr_span) => { + [] if let Some(braces_span) = braces_span => { // Get the span for the empty match body `{}`. let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) { (format!("\n{snippet}"), " ") @@ -1032,8 +1056,8 @@ fn report_non_exhaustive_match<'p, 'tcx>( (" ".to_string(), "") }; suggestion = Some(( - sp.shrink_to_hi().with_hi(expr_span.hi()), - format!(" {{{indentation}{more}{pattern} => todo!(),{indentation}}}",), + braces_span, + format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",), )); } [only] => { @@ -1059,7 +1083,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( }; suggestion = Some(( only.span.shrink_to_hi(), - format!("{comma}{pre_indentation}{pattern} => todo!()"), + format!("{comma}{pre_indentation}{suggested_arm}"), )); } [.., prev, last] => { @@ -1082,7 +1106,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( if let Some(spacing) = spacing { suggestion = Some(( last.span.shrink_to_hi(), - format!("{comma}{spacing}{pattern} => todo!()"), + format!("{comma}{spacing}{suggested_arm}"), )); } } @@ -1093,13 +1117,13 @@ fn report_non_exhaustive_match<'p, 'tcx>( let msg = format!( "ensure that all possible cases are being handled by adding a match arm with a wildcard \ pattern{}{}", - if patterns_len > 1 && patterns_len < 4 && suggestion.is_some() { + if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() { ", a match arm with multiple or-patterns" } else { // we are either not suggesting anything, or suggesting `_` "" }, - match patterns_len { + match witnesses.len() { // non-exhaustive enum case 0 if suggestion.is_some() => " as shown", 0 => "", diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index ce39aff69cb91..65c53be8ddd9a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -457,7 +457,7 @@ impl<'tcx> ConstToPat<'tcx> { PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } } } - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { + ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { // The raw pointers we see here have been "vetted" by valtree construction to be // just integers, so we simply allow them. PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index ac3043afcff08..5c016682d8d2d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -9,15 +9,14 @@ use crate::errors::*; use crate::thir::util::UserAnnotatedTyHelpers; use rustc_errors::codes::*; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::RangeEnd; +use rustc_hir::{self as hir, RangeEnd}; use rustc_index::Idx; use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput}; -use rustc_middle::mir::{self, BorrowKind, Const, Mutability}; +use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{ - Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, + Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt}; @@ -66,7 +65,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted // gets the least-dereferenced type). - let unadjusted_pat = self.lower_pattern_unadjusted(pat); + let unadjusted_pat = match pat.kind { + hir::PatKind::Ref(inner, _) + if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) => + { + self.lower_pattern(inner) + } + _ => self.lower_pattern_unadjusted(pat), + }; self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold( unadjusted_pat, |pat: Box<_>, ref_ty| { @@ -257,6 +263,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { return self.lower_path(qpath, pat.hir_id, pat.span); } + hir::PatKind::Deref(subpattern) => { + let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern); + let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; + PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability } + } hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => { PatKind::Deref { subpattern: self.lower_pattern(subpattern) } } @@ -278,26 +289,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { span = span.with_hi(ident_span.hi()); } - let bm = *self + let mode = *self .typeck_results .pat_binding_modes() .get(pat.hir_id) .expect("missing binding mode"); - let (mutability, mode) = match bm { - ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue), - ty::BindByReference(hir::Mutability::Mut) => ( - Mutability::Not, - BindingMode::ByRef(BorrowKind::Mut { kind: mir::MutBorrowKind::Default }), - ), - ty::BindByReference(hir::Mutability::Not) => { - (Mutability::Not, BindingMode::ByRef(BorrowKind::Shared)) - } - }; // A ref x pattern is the same node used for x, and as such it has // x's type, which is &T, where we want T (the type being matched). let var_ty = ty; - if let ty::BindByReference(_) = bm { + if let hir::ByRef::Yes(_) = mode.0 { if let ty::Ref(_, rty, _) = ty.kind() { ty = *rty; } else { @@ -306,7 +307,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; PatKind::Binding { - mutability, mode, name: ident.name, var: LocalVarId(id), @@ -524,12 +524,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // Prefer valtrees over opaque constants. let const_value = self .tcx - .const_eval_global_id_for_typeck(param_env_reveal_all, cid, Some(span)) + .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span) .map(|val| match val { Some(valtree) => mir::Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), None => mir::Const::Val( self.tcx - .const_eval_global_id(param_env_reveal_all, cid, Some(span)) + .const_eval_global_id(param_env_reveal_all, cid, span) .expect("const_eval_global_id_for_typeck should have already failed"), ty, ), @@ -627,15 +627,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // First try using a valtree in order to destructure the constant into a pattern. // FIXME: replace "try to do a thing, then fall back to another thing" // but something more principled, like a trait query checking whether this can be turned into a valtree. - if let Ok(Some(valtree)) = - self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span)) + if let Ok(Some(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span) { let subpattern = self.const_to_pat(Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), id, span); PatKind::InlineConstant { subpattern, def: def_id } } else { // If that fails, convert it to an opaque constant pattern. - match tcx.const_eval_resolve(self.param_env, uneval, Some(span)) { + match tcx.const_eval_resolve(self.param_env, uneval, span) { Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span).kind, Err(ErrorHandled::TooGeneric(_)) => { // If we land here it means the const can't be evaluated because it's `TooGeneric`. diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index d53704f89e79e..619bfbcf43d3a 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -1,10 +1,11 @@ +use rustc_middle::query::TyCtxtAt; use rustc_middle::thir::*; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use std::fmt::{self, Write}; -pub(crate) fn thir_tree(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String { - match super::cx::thir_body(tcx, owner_def) { +pub(crate) fn thir_tree(tcx: TyCtxtAt<'_>, owner_def: LocalDefId) -> String { + match super::cx::thir_body(*tcx, owner_def) { Ok((thir, _)) => { let thir = thir.steal(); let mut printer = ThirPrinter::new(&thir); @@ -15,8 +16,8 @@ pub(crate) fn thir_tree(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String { } } -pub(crate) fn thir_flat(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String { - match super::cx::thir_body(tcx, owner_def) { +pub(crate) fn thir_flat(tcx: TyCtxtAt<'_>, owner_def: LocalDefId) -> String { + match super::cx::thir_body(*tcx, owner_def) { Ok((thir, _)) => format!("{:#?}", thir.steal()), Err(_) => "error".into(), } @@ -635,9 +636,8 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_pat(subpattern, depth_lvl + 3); print_indented!(self, "}", depth_lvl + 1); } - PatKind::Binding { mutability, name, mode, var, ty, subpattern, is_primary } => { + PatKind::Binding { name, mode, var, ty, subpattern, is_primary } => { print_indented!(self, "Binding {", depth_lvl + 1); - print_indented!(self, format!("mutability: {:?}", mutability), depth_lvl + 2); print_indented!(self, format!("name: {:?}", name), depth_lvl + 2); print_indented!(self, format!("mode: {:?}", mode), depth_lvl + 2); print_indented!(self, format!("var: {:?}", var), depth_lvl + 2); @@ -688,6 +688,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_pat(subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } + PatKind::DerefPattern { subpattern, .. } => { + print_indented!(self, "DerefPattern { ", depth_lvl + 1); + print_indented!(self, "subpattern:", depth_lvl + 2); + self.print_pat(subpattern, depth_lvl + 2); + print_indented!(self, "}", depth_lvl + 1); + } PatKind::Constant { value } => { print_indented!(self, "Constant {", depth_lvl + 1); print_indented!(self, format!("value: {:?}", value), depth_lvl + 2); diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 1b2f2cd9477fc..185e87baed8cf 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -1,8 +1,8 @@ -use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_index::Idx; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; +use rustc_middle::span_bug; use rustc_middle::traits::Reveal; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::GenericArgsRef; @@ -421,14 +421,14 @@ where ) -> BasicBlock { // drop glue is sent straight to codegen // box cannot be directly dereferenced - let unique_ty = adt.non_enum_variant().fields[FieldIdx::new(0)].ty(self.tcx(), args); + let unique_ty = adt.non_enum_variant().fields[FieldIdx::ZERO].ty(self.tcx(), args); let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant(); - let nonnull_ty = unique_variant.fields[FieldIdx::from_u32(0)].ty(self.tcx(), args); + let nonnull_ty = unique_variant.fields[FieldIdx::ZERO].ty(self.tcx(), args); let ptr_ty = Ty::new_imm_ptr(self.tcx(), args[0].expect_ty()); - let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::new(0), unique_ty); - let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::new(0), nonnull_ty); - let ptr_place = self.tcx().mk_place_field(nonnull_place, FieldIdx::new(0), ptr_ty); + let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::ZERO, unique_ty); + let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::ZERO, nonnull_ty); + let ptr_place = self.tcx().mk_place_field(nonnull_place, FieldIdx::ZERO, ptr_ty); let interior = self.tcx().mk_place_deref(ptr_place); let interior_path = self.elaborator.deref_subpath(self.path); @@ -629,13 +629,9 @@ where let drop_fn = tcx.associated_item_def_ids(drop_trait)[0]; let ty = self.place_ty(self.place); - let ref_ty = Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }, - ); + let ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty); let ref_place = self.new_temp(ref_ty); - let unit_temp = Place::from(self.new_temp(Ty::new_unit(tcx))); + let unit_temp = Place::from(self.new_temp(tcx.types.unit)); let result = BasicBlockData { statements: vec![self.assign( @@ -700,7 +696,7 @@ where let move_ = |place: Place<'tcx>| Operand::Move(place); let tcx = self.tcx(); - let ptr_ty = Ty::new_ptr(tcx, ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut }); + let ptr_ty = Ty::new_mut_ptr(tcx, ety); let ptr = Place::from(self.new_temp(ptr_ty)); let can_go = Place::from(self.new_temp(tcx.types.bool)); let one = self.constant_usize(1); diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 44a7dcc82775b..ea9cf6565e7c8 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -13,6 +13,7 @@ use rustc_data_structures::work_queue::WorkQueue; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; use rustc_index::{Idx, IndexVec}; +use rustc_middle::bug; use rustc_middle::mir::{self, traversal, BasicBlock}; use rustc_middle::mir::{create_dump_file, dump_enabled}; use rustc_middle::ty::print::with_no_trimmed_paths; diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 693994b5da76f..bdc70de58e84b 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -102,7 +102,7 @@ where } Rvalue::Cast(..) - | Rvalue::Ref(_, BorrowKind::Fake, _) + | Rvalue::Ref(_, BorrowKind::Fake(_), _) | Rvalue::ShallowInitBox(..) | Rvalue::Use(..) | Rvalue::ThreadLocalRef(..) diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 720515f262db8..180168c731686 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -1,5 +1,6 @@ use rustc_index::bit_set::{BitSet, ChunkedBitSet}; use rustc_index::Idx; +use rustc_middle::bug; use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges}; use rustc_middle::ty::{self, TyCtxt}; diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index f18a2354301e4..5e96a73f6c53f 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -2,13 +2,10 @@ #![feature(box_patterns)] #![feature(exact_size_is_empty)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![feature(try_blocks)] #[macro_use] extern crate tracing; -#[macro_use] -extern crate rustc_middle; use rustc_middle::ty; diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 0f900e6a5573a..6ae7df79d3094 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -2,6 +2,7 @@ use rustc_index::IndexVec; use rustc_middle::mir::tcx::{PlaceTy, RvalueInitializationState}; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::{bug, span_bug}; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -153,6 +154,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -193,8 +195,9 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -433,7 +436,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_), + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks, _, ) => {} } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 22cf3999239c1..830f44df5fb39 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -358,20 +358,15 @@ impl<'tcx> MoveData<'tcx> { builder::gather_moves(body, tcx, param_env, filter) } - /// For the move path `mpi`, returns the root local variable (if any) that starts the path. - /// (e.g., for a path like `a.b.c` returns `Some(a)`) - pub fn base_local(&self, mut mpi: MovePathIndex) -> Option { + /// For the move path `mpi`, returns the root local variable that starts the path. + /// (e.g., for a path like `a.b.c` returns `a`) + pub fn base_local(&self, mut mpi: MovePathIndex) -> Local { loop { let path = &self.move_paths[mpi]; if let Some(l) = path.place.as_local() { - return Some(l); - } - if let Some(parent) = path.parent { - mpi = parent; - continue; - } else { - return None; + return l; } + mpi = path.parent.expect("root move paths should be locals"); } } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 2b2af6ee7da3e..807bef0741178 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -40,6 +40,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::bit_set::BitSet; use rustc_index::{IndexSlice, IndexVec}; +use rustc_middle::bug; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -843,7 +844,7 @@ impl Map { self.value_count += 1; } - if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ty::TypeAndMut { ty: ref_ty, .. }) = ty.kind() + if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind() && let ty::Slice(..) = ref_ty.kind() { assert!(self.places[place].value_index.is_none(), "slices are not scalars"); diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index b8dbdf18db3ea..f9b79d72b0504 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -1,6 +1,4 @@ mir_transform_arithmetic_overflow = this arithmetic operation will overflow -mir_transform_call_to_unsafe_label = call to unsafe function -mir_transform_call_to_unsafe_note = consult the function's documentation for information on how to avoid undefined behavior mir_transform_const_defined_here = `const` item defined here mir_transform_const_modify = attempting to modify a `const` item @@ -11,10 +9,6 @@ mir_transform_const_mut_borrow = taking a mutable reference to a `const` item .note2 = the mutable reference will refer to this temporary, not the original `const` item .note3 = mutable reference created due to call to this method -mir_transform_const_ptr2int_label = cast of pointer to int -mir_transform_const_ptr2int_note = casting pointers to integers in constants -mir_transform_deref_ptr_label = dereference of raw pointer -mir_transform_deref_ptr_note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior mir_transform_ffi_unwind_call = call to {$foreign -> [true] foreign function *[false] function pointer @@ -23,56 +17,13 @@ mir_transform_ffi_unwind_call = call to {$foreign -> mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer .suggestion = cast `{$ident}` to obtain a function pointer -mir_transform_initializing_valid_range_label = initializing type with `rustc_layout_scalar_valid_range` attr -mir_transform_initializing_valid_range_note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be .label = the value is held across this suspend point .note = {$reason} .help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point - -mir_transform_mutation_layout_constrained_borrow_label = borrow of layout constrained field with interior mutability -mir_transform_mutation_layout_constrained_borrow_note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values -mir_transform_mutation_layout_constrained_label = mutation of layout constrained field -mir_transform_mutation_layout_constrained_note = mutating layout constrained fields cannot statically be checked for valid values mir_transform_operation_will_panic = this operation will panic at runtime -mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in_unsafe_fn_allowed -> - [true] function or block - *[false] block - } - .not_inherited = items do not inherit unsafety from separate enclosing items - -mir_transform_target_feature_call_help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count -> - [1] feature - *[count] features - }: {$missing_target_features} - -mir_transform_target_feature_call_label = call to function with `#[target_feature]` -mir_transform_target_feature_call_note = the {$build_target_features} target {$build_target_features_count -> - [1] feature - *[count] features - } being enabled in the build configuration does not remove the requirement to list {$build_target_features_count -> - [1] it - *[count] them - } in `#[target_feature]` - mir_transform_unaligned_packed_ref = reference to packed field is unaligned .note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses .note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) .help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) - -mir_transform_union_access_label = access to union field -mir_transform_union_access_note = the field may not be properly initialized: using uninitialized data will cause undefined behavior -mir_transform_unsafe_op_in_unsafe_fn = {$details} is unsafe and requires unsafe block (error E0133) - .suggestion = consider wrapping the function body in an unsafe block - .note = an unsafe function restricts its caller, but its body is safe by default - -mir_transform_unused_unsafe = unnecessary `unsafe` block - .label = because it's nested under this `unsafe` block - -mir_transform_use_of_asm_label = use of inline assembly -mir_transform_use_of_asm_note = inline assembly is entirely unchecked and can cause undefined behavior -mir_transform_use_of_extern_static_label = use of extern static -mir_transform_use_of_extern_static_note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior -mir_transform_use_of_static_mut_label = use of mutable static -mir_transform_use_of_static_mut_note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 6f668aa4ce850..f880476cec2f2 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -24,7 +24,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b | ty::Str | ty::FnDef(..) | ty::Never => false, - // References + // References and Boxes (`noalias` sources) ty::Ref(..) => true, ty::Adt(..) if ty.is_box() => true, ty::Adt(adt, _) if Some(adt.did()) == tcx.lang_items().ptr_unique() => true, @@ -125,15 +125,39 @@ impl<'tcx> MirPass<'tcx> for AddRetag { for i in (0..block_data.statements.len()).rev() { let (retag_kind, place) = match block_data.statements[i].kind { // Retag after assignments of reference type. - StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => { + StatementKind::Assign(box (ref place, ref rvalue)) => { let add_retag = match rvalue { // Ptr-creating operations already do their own internal retagging, no // need to also add a retag statement. - Rvalue::Ref(..) | Rvalue::AddressOf(..) => false, - _ => true, + // *Except* if we are deref'ing a Box, because those get desugared to directly working + // with the inner raw pointer! That's relevant for `AddressOf` as Miri otherwise makes it + // a NOP when the original pointer is already raw. + Rvalue::AddressOf(_mutbl, place) => { + // Using `is_box_global` here is a bit sketchy: if this code is + // generic over the allocator, we'll not add a retag! This is a hack + // to make Stacked Borrows compatible with custom allocator code. + // Long-term, we'll want to move to an aliasing model where "cast to + // raw pointer" is a complete NOP, and then this will no longer be + // an issue. + if place.is_indirect_first_projection() + && body.local_decls[place.local].ty.is_box_global(tcx) + { + Some(RetagKind::Raw) + } else { + None + } + } + Rvalue::Ref(..) => None, + _ => { + if needs_retag(place) { + Some(RetagKind::Default) + } else { + None + } + } }; - if add_retag { - (RetagKind::Default, *place) + if let Some(kind) = add_retag { + (kind, *place) } else { continue; } diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index 9eec724ef21b9..0af8872988754 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::{ interpret::Scalar, visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}, }; -use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeAndMut}; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_session::Session; pub struct CheckAlignment; @@ -16,7 +16,7 @@ impl<'tcx> MirPass<'tcx> for CheckAlignment { if sess.target.llvm_target == "i686-pc-windows-msvc" { return false; } - sess.opts.debug_assertions + sess.ub_checks() } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -157,7 +157,7 @@ fn insert_alignment_check<'tcx>( new_block: BasicBlock, ) { // Cast the pointer to a *const () - let const_raw_ptr = Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not }); + let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr); let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into(); block_data diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs deleted file mode 100644 index a0c3de3af5862..0000000000000 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ /dev/null @@ -1,615 +0,0 @@ -use rustc_data_structures::unord::{ExtendUnord, UnordItems, UnordSet}; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::hir_id::HirId; -use rustc_hir::intravisit; -use rustc_hir::{BlockCheckMode, ExprKind, Node}; -use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::*; -use rustc_middle::query::Providers; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; -use rustc_session::lint::Level; - -use std::ops::Bound; - -use crate::errors; - -pub struct UnsafetyChecker<'a, 'tcx> { - body: &'a Body<'tcx>, - body_did: LocalDefId, - violations: Vec, - source_info: SourceInfo, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - - /// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint. - used_unsafe_blocks: UnordSet, -} - -impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { - fn new( - body: &'a Body<'tcx>, - body_did: LocalDefId, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Self { - Self { - body, - body_did, - violations: vec![], - source_info: SourceInfo::outermost(body.span), - tcx, - param_env, - used_unsafe_blocks: Default::default(), - } - } -} - -impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { - self.source_info = terminator.source_info; - match terminator.kind { - TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Drop { .. } - | TerminatorKind::Yield { .. } - | TerminatorKind::Assert { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::UnwindResume - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => { - // safe (at least as emitted during MIR construction) - } - - TerminatorKind::Call { ref func, .. } => { - let func_ty = func.ty(self.body, self.tcx); - let func_id = - if let ty::FnDef(func_id, _) = func_ty.kind() { Some(func_id) } else { None }; - let sig = func_ty.fn_sig(self.tcx); - if let hir::Unsafety::Unsafe = sig.unsafety() { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::CallToUnsafeFunction, - ) - } - - if let Some(func_id) = func_id { - self.check_target_features(*func_id); - } - } - - TerminatorKind::InlineAsm { .. } => self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfInlineAssembly, - ), - } - self.super_terminator(terminator, location); - } - - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { - self.source_info = statement.source_info; - match statement.kind { - StatementKind::Assign(..) - | StatementKind::FakeRead(..) - | StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(..) - | StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) - | StatementKind::Retag { .. } - | StatementKind::PlaceMention(..) - | StatementKind::Coverage(..) - | StatementKind::Intrinsic(..) - | StatementKind::ConstEvalCounter - | StatementKind::Nop => { - // safe (at least as emitted during MIR construction) - } - // `AscribeUserType` just exists to help MIR borrowck. - // It has no semantics, and everything is already reported by `PlaceMention`. - StatementKind::AscribeUserType(..) => return, - } - self.super_statement(statement, location); - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - match rvalue { - Rvalue::Aggregate(box ref aggregate, _) => match aggregate { - &AggregateKind::Array(..) | &AggregateKind::Tuple => {} - &AggregateKind::Adt(adt_did, ..) => { - match self.tcx.layout_scalar_valid_range(adt_did) { - (Bound::Unbounded, Bound::Unbounded) => {} - _ => self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::InitializingTypeWith, - ), - } - } - &AggregateKind::Closure(def_id, _) - | &AggregateKind::CoroutineClosure(def_id, _) - | &AggregateKind::Coroutine(def_id, _) => { - let def_id = def_id.expect_local(); - let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } = - self.tcx.mir_unsafety_check_result(def_id); - self.register_violations(violations, used_unsafe_blocks.items().copied()); - } - }, - _ => {} - } - self.super_rvalue(rvalue, location); - } - - fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { - if let Operand::Constant(constant) = op { - let maybe_uneval = match constant.const_ { - Const::Val(..) | Const::Ty(_) => None, - Const::Unevaluated(uv, _) => Some(uv), - }; - - if let Some(uv) = maybe_uneval { - if uv.promoted.is_none() { - let def_id = uv.def; - if self.tcx.def_kind(def_id) == DefKind::InlineConst { - let local_def_id = def_id.expect_local(); - let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } = - self.tcx.mir_unsafety_check_result(local_def_id); - self.register_violations(violations, used_unsafe_blocks.items().copied()); - } - } - } - } - self.super_operand(op, location); - } - - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { - // On types with `scalar_valid_range`, prevent - // * `&mut x.field` - // * `x.field = y;` - // * `&x.field` if `field`'s type has interior mutability - // because either of these would allow modifying the layout constrained field and - // insert values that violate the layout constraints. - if context.is_mutating_use() || context.is_borrow() { - self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use()); - } - - // Some checks below need the extra meta info of the local declaration. - let decl = &self.body.local_decls[place.local]; - - // Check the base local: it might be an unsafe-to-access static. We only check derefs of the - // temporary holding the static pointer to avoid duplicate errors - // . - if place.projection.first() == Some(&ProjectionElem::Deref) { - // If the projection root is an artificial local that we introduced when - // desugaring `static`, give a more specific error message - // (avoid the general "raw pointer" clause below, that would only be confusing). - if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() { - if self.tcx.is_mutable_static(def_id) { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfMutableStatic, - ); - return; - } else if self.tcx.is_foreign_item(def_id) { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::UseOfExternStatic, - ); - return; - } - } - } - - // Check for raw pointer `Deref`. - for (base, proj) in place.iter_projections() { - if proj == ProjectionElem::Deref { - let base_ty = base.ty(self.body, self.tcx).ty; - if base_ty.is_unsafe_ptr() { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::DerefOfRawPointer, - ) - } - } - } - - // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes - // whether we *read* the union field or potentially *write* to it (if this place is being assigned to). - let mut saw_deref = false; - for (base, proj) in place.iter_projections().rev() { - if proj == ProjectionElem::Deref { - saw_deref = true; - continue; - } - - let base_ty = base.ty(self.body, self.tcx).ty; - if base_ty.is_union() { - // If we did not hit a `Deref` yet and the overall place use is an assignment, the - // rules are different. - let assign_to_field = !saw_deref - && matches!( - context, - PlaceContext::MutatingUse( - MutatingUseContext::Store - | MutatingUseContext::Drop - | MutatingUseContext::AsmOutput - ) - ); - // If this is just an assignment, determine if the assigned type needs dropping. - if assign_to_field { - // We have to check the actual type of the assignment, as that determines if the - // old value is being dropped. - let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty; - if assigned_ty.needs_drop(self.tcx, self.param_env) { - // This would be unsafe, but should be outright impossible since we reject - // such unions. - assert!( - self.tcx.dcx().has_errors().is_some(), - "union fields that need dropping should be impossible: {assigned_ty}" - ); - } - } else { - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::AccessToUnionField, - ) - } - } - } - } -} - -impl<'tcx> UnsafetyChecker<'_, 'tcx> { - fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) { - // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such. - assert_ne!(kind, UnsafetyViolationKind::UnsafeFn); - - let source_info = self.source_info; - let lint_root = self.body.source_scopes[self.source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; - self.register_violations( - [&UnsafetyViolation { source_info, lint_root, kind, details }], - UnordItems::empty(), - ); - } - - fn register_violations<'a>( - &mut self, - violations: impl IntoIterator, - new_used_unsafe_blocks: UnordItems>, - ) { - let safety = self.body.source_scopes[self.source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .safety; - match safety { - // `unsafe` blocks are required in safe code - Safety::Safe => violations.into_iter().for_each(|violation| { - match violation.kind { - UnsafetyViolationKind::General => {} - UnsafetyViolationKind::UnsafeFn => { - bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") - } - } - if !self.violations.contains(violation) { - self.violations.push(violation.clone()) - } - }), - // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s - Safety::FnUnsafe => violations.into_iter().for_each(|violation| { - let mut violation = violation.clone(); - violation.kind = UnsafetyViolationKind::UnsafeFn; - if !self.violations.contains(&violation) { - self.violations.push(violation) - } - }), - Safety::BuiltinUnsafe => {} - Safety::ExplicitUnsafe(hir_id) => violations.into_iter().for_each(|_violation| { - self.used_unsafe_blocks.insert(hir_id); - }), - }; - - self.used_unsafe_blocks.extend_unord(new_used_unsafe_blocks); - } - fn check_mut_borrowing_layout_constrained_field( - &mut self, - place: Place<'tcx>, - is_mut_use: bool, - ) { - for (place_base, elem) in place.iter_projections().rev() { - match elem { - // Modifications behind a dereference don't affect the value of - // the pointer. - ProjectionElem::Deref => return, - ProjectionElem::Field(..) => { - let ty = place_base.ty(&self.body.local_decls, self.tcx).ty; - if let ty::Adt(def, _) = ty.kind() { - if self.tcx.layout_scalar_valid_range(def.did()) - != (Bound::Unbounded, Bound::Unbounded) - { - let details = if is_mut_use { - UnsafetyViolationDetails::MutationOfLayoutConstrainedField - - // Check `is_freeze` as late as possible to avoid cycle errors - // with opaque types. - } else if !place - .ty(self.body, self.tcx) - .ty - .is_freeze(self.tcx, self.param_env) - { - UnsafetyViolationDetails::BorrowOfLayoutConstrainedField - } else { - continue; - }; - self.require_unsafe(UnsafetyViolationKind::General, details); - } - } - } - _ => {} - } - } - } - - /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether - /// the called function has target features the calling function hasn't. - fn check_target_features(&mut self, func_did: DefId) { - // Unsafety isn't required on wasm targets. For more information see - // the corresponding check in typeck/src/collect.rs - if self.tcx.sess.target.options.is_like_wasm { - return; - } - - let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; - // The body might be a constant, so it doesn't have codegen attributes. - let self_features = &self.tcx.body_codegen_attrs(self.body_did.to_def_id()).target_features; - - // Is `callee_features` a subset of `calling_features`? - if !callee_features.iter().all(|feature| self_features.contains(feature)) { - let missing: Vec<_> = callee_features - .iter() - .copied() - .filter(|feature| !self_features.contains(feature)) - .collect(); - let build_enabled = self - .tcx - .sess - .target_features - .iter() - .copied() - .filter(|feature| missing.contains(feature)) - .collect(); - self.require_unsafe( - UnsafetyViolationKind::General, - UnsafetyViolationDetails::CallToFunctionWith { missing, build_enabled }, - ) - } - } -} - -pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { mir_unsafety_check_result, ..*providers }; -} - -/// Context information for [`UnusedUnsafeVisitor`] traversal, -/// saves (innermost) relevant context -#[derive(Copy, Clone, Debug)] -enum Context { - Safe, - /// in an `unsafe fn` - UnsafeFn, - /// in a *used* `unsafe` block - /// (i.e. a block without unused-unsafe warning) - UnsafeBlock(HirId), -} - -struct UnusedUnsafeVisitor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - used_unsafe_blocks: &'a UnordSet, - context: Context, - unused_unsafes: &'a mut Vec<(HirId, UnusedUnsafe)>, -} - -impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> { - fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) { - if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules { - let used = match self.tcx.lint_level_at_node(UNUSED_UNSAFE, block.hir_id) { - (Level::Allow, _) => true, - _ => self.used_unsafe_blocks.contains(&block.hir_id), - }; - let unused_unsafe = match (self.context, used) { - (_, false) => UnusedUnsafe::Unused, - (Context::Safe, true) | (Context::UnsafeFn, true) => { - let previous_context = self.context; - self.context = Context::UnsafeBlock(block.hir_id); - intravisit::walk_block(self, block); - self.context = previous_context; - return; - } - (Context::UnsafeBlock(hir_id), true) => UnusedUnsafe::InUnsafeBlock(hir_id), - }; - self.unused_unsafes.push((block.hir_id, unused_unsafe)); - } - intravisit::walk_block(self, block); - } - - fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { - self.visit_body(self.tcx.hir().body(c.body)) - } - - fn visit_fn( - &mut self, - fk: intravisit::FnKind<'tcx>, - _fd: &'tcx hir::FnDecl<'tcx>, - b: hir::BodyId, - _s: rustc_span::Span, - _id: LocalDefId, - ) { - if matches!(fk, intravisit::FnKind::Closure) { - self.visit_body(self.tcx.hir().body(b)) - } - } -} - -fn check_unused_unsafe( - tcx: TyCtxt<'_>, - def_id: LocalDefId, - used_unsafe_blocks: &UnordSet, -) -> Vec<(HirId, UnusedUnsafe)> { - let body_id = tcx.hir().maybe_body_owned_by(def_id); - - let Some(body_id) = body_id else { - debug!("check_unused_unsafe({:?}) - no body found", def_id); - return vec![]; - }; - - let body = tcx.hir().body(body_id); - let hir_id = tcx.local_def_id_to_hir_id(def_id); - let context = match tcx.hir().fn_sig_by_hir_id(hir_id) { - Some(sig) if sig.header.unsafety == hir::Unsafety::Unsafe => Context::UnsafeFn, - _ => Context::Safe, - }; - - debug!( - "check_unused_unsafe({:?}, context={:?}, body={:?}, used_unsafe_blocks={:?})", - def_id, body, context, used_unsafe_blocks - ); - - let mut unused_unsafes = vec![]; - - let mut visitor = UnusedUnsafeVisitor { - tcx, - used_unsafe_blocks, - context, - unused_unsafes: &mut unused_unsafes, - }; - intravisit::Visitor::visit_body(&mut visitor, body); - - unused_unsafes -} - -fn mir_unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResult { - debug!("unsafety_violations({:?})", def); - - // N.B., this borrow is valid because all the consumers of - // `mir_built` force this. - let body = &tcx.mir_built(def).borrow(); - - if body.is_custom_mir() || body.tainted_by_errors.is_some() { - return tcx.arena.alloc(UnsafetyCheckResult { - violations: Vec::new(), - used_unsafe_blocks: Default::default(), - unused_unsafes: Some(Vec::new()), - }); - } - - let param_env = tcx.param_env(def); - - let mut checker = UnsafetyChecker::new(body, def, tcx, param_env); - checker.visit_body(body); - - let unused_unsafes = (!tcx.is_typeck_child(def.to_def_id())) - .then(|| check_unused_unsafe(tcx, def, &checker.used_unsafe_blocks)); - - tcx.arena.alloc(UnsafetyCheckResult { - violations: checker.violations, - used_unsafe_blocks: checker.used_unsafe_blocks, - unused_unsafes, - }) -} - -fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) { - let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id)); - let nested_parent = if let UnusedUnsafe::InUnsafeBlock(id) = kind { - Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id))) - } else { - None - }; - tcx.emit_node_span_lint(UNUSED_UNSAFE, id, span, errors::UnusedUnsafe { span, nested_parent }); -} - -pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { - debug!("check_unsafety({:?})", def_id); - - // closures and inline consts are handled by their parent fn. - if tcx.is_typeck_child(def_id.to_def_id()) { - return; - } - - let UnsafetyCheckResult { violations, unused_unsafes, .. } = - tcx.mir_unsafety_check_result(def_id); - // Only suggest wrapping the entire function body in an unsafe block once - let mut suggest_unsafe_block = true; - - for &UnsafetyViolation { source_info, lint_root, kind, ref details } in violations.iter() { - let details = - errors::RequiresUnsafeDetail { violation: details.clone(), span: source_info.span }; - - match kind { - UnsafetyViolationKind::General => { - let op_in_unsafe_fn_allowed = unsafe_op_in_unsafe_fn_allowed(tcx, lint_root); - let note_non_inherited = tcx.hir().parent_iter(lint_root).find(|(id, node)| { - if let Node::Expr(block) = node - && let ExprKind::Block(block, _) = block.kind - && let BlockCheckMode::UnsafeBlock(_) = block.rules - { - true - } else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id) - && sig.header.is_unsafe() - { - true - } else { - false - } - }); - let enclosing = if let Some((id, _)) = note_non_inherited { - Some(tcx.sess.source_map().guess_head_span(tcx.hir().span(id))) - } else { - None - }; - tcx.dcx().emit_err(errors::RequiresUnsafe { - span: source_info.span, - enclosing, - details, - op_in_unsafe_fn_allowed, - }); - } - UnsafetyViolationKind::UnsafeFn => { - tcx.emit_node_span_lint( - UNSAFE_OP_IN_UNSAFE_FN, - lint_root, - source_info.span, - errors::UnsafeOpInUnsafeFn { - details, - suggest_unsafe_block: suggest_unsafe_block.then(|| { - let hir_id = tcx.local_def_id_to_hir_id(def_id); - let fn_sig = tcx - .hir() - .fn_sig_by_hir_id(hir_id) - .expect("this violation only occurs in fn"); - let body = tcx.hir().body_owned_by(def_id); - let body_span = tcx.hir().body(body).value.span; - let start = tcx.sess.source_map().start_point(body_span).shrink_to_hi(); - let end = tcx.sess.source_map().end_point(body_span).shrink_to_lo(); - (start, end, fn_sig.span) - }), - }, - ); - suggest_unsafe_block = false; - } - } - } - - for &(block_id, kind) in unused_unsafes.as_ref().unwrap() { - report_unused_unsafe(tcx, kind, block_id); - } -} - -fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool { - tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow -} diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 5b4bc4fa13424..48a6a83e14609 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -5,14 +5,19 @@ //! - [`AscribeUserType`] //! - [`FakeRead`] //! - [`Assign`] statements with a [`Fake`] borrow +//! - [`Coverage`] statements of kind [`BlockMarker`] or [`SpanMarker`] //! //! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType //! [`Assign`]: rustc_middle::mir::StatementKind::Assign //! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead //! [`Nop`]: rustc_middle::mir::StatementKind::Nop //! [`Fake`]: rustc_middle::mir::BorrowKind::Fake +//! [`Coverage`]: rustc_middle::mir::StatementKind::Coverage +//! [`BlockMarker`]: rustc_middle::mir::coverage::CoverageKind::BlockMarker +//! [`SpanMarker`]: rustc_middle::mir::coverage::CoverageKind::SpanMarker use crate::MirPass; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::TyCtxt; @@ -24,7 +29,12 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck { for statement in basic_block.statements.iter_mut() { match statement.kind { StatementKind::AscribeUserType(..) - | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake, _))) + | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake(_), _))) + | StatementKind::Coverage( + // These kinds of coverage statements are markers inserted during + // MIR building, and are not needed after InstrumentCoverage. + CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, + ) | StatementKind::FakeRead(..) => statement.make_nop(), _ => (), } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 54b13a40e92dd..3008016863e4c 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -80,6 +80,10 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_target::spec::PanicStrategy; +use rustc_trait_selection::infer::TyCtxtInferExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use std::{iter, ops}; pub struct StateTransform; @@ -168,7 +172,7 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> { Place { local: SELF_ARG, projection: self.tcx().mk_place_elems(&[ProjectionElem::Field( - FieldIdx::new(0), + FieldIdx::ZERO, self.ref_coroutine_ty, )]), }, @@ -267,7 +271,7 @@ impl<'tcx> TransformVisitor<'tcx> { Rvalue::Aggregate( Box::new(AggregateKind::Adt( option_def_id, - VariantIdx::from_usize(0), + VariantIdx::ZERO, self.tcx.mk_args(&[self.old_yield_ty.into()]), None, None, @@ -329,7 +333,7 @@ impl<'tcx> TransformVisitor<'tcx> { Rvalue::Aggregate( Box::new(AggregateKind::Adt( poll_def_id, - VariantIdx::from_usize(0), + VariantIdx::ZERO, args, None, None, @@ -358,7 +362,7 @@ impl<'tcx> TransformVisitor<'tcx> { Rvalue::Aggregate( Box::new(AggregateKind::Adt( option_def_id, - VariantIdx::from_usize(0), + VariantIdx::ZERO, args, None, None, @@ -420,7 +424,7 @@ impl<'tcx> TransformVisitor<'tcx> { Rvalue::Aggregate( Box::new(AggregateKind::Adt( coroutine_state_def_id, - VariantIdx::from_usize(0), + VariantIdx::ZERO, args, None, None, @@ -570,11 +574,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let coroutine_ty = body.local_decls.raw[1].ty; - let ref_coroutine_ty = Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty: coroutine_ty, mutbl: Mutability::Mut }, - ); + let ref_coroutine_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty); // Replace the by value coroutine argument body.local_decls.raw[1].ty = ref_coroutine_ty; @@ -1230,7 +1230,7 @@ fn create_coroutine_drop_shim<'tcx>( tcx: TyCtxt<'tcx>, transform: &TransformVisitor<'tcx>, coroutine_ty: Ty<'tcx>, - body: &mut Body<'tcx>, + body: &Body<'tcx>, drop_clean: BasicBlock, ) -> Body<'tcx> { let mut body = body.clone(); @@ -1260,15 +1260,13 @@ fn create_coroutine_drop_shim<'tcx>( } // Replace the return variable - body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(Ty::new_unit(tcx), source_info); + body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info); make_coroutine_state_argument_indirect(tcx, &mut body); // Change the coroutine argument from &mut to *mut - body.local_decls[SELF_ARG] = LocalDecl::with_source_info( - Ty::new_ptr(tcx, ty::TypeAndMut { ty: coroutine_ty, mutbl: hir::Mutability::Mut }), - source_info, - ); + body.local_decls[SELF_ARG] = + LocalDecl::with_source_info(Ty::new_mut_ptr(tcx, coroutine_ty), source_info); // Make sure we remove dead blocks to remove // unrelated code from the resume part of the function @@ -1590,10 +1588,46 @@ pub(crate) fn mir_coroutine_witnesses<'tcx>( let (_, coroutine_layout, _) = compute_layout(liveness_info, body); check_suspend_tys(tcx, &coroutine_layout, body); + check_field_tys_sized(tcx, &coroutine_layout, def_id); Some(coroutine_layout) } +fn check_field_tys_sized<'tcx>( + tcx: TyCtxt<'tcx>, + coroutine_layout: &CoroutineLayout<'tcx>, + def_id: LocalDefId, +) { + // No need to check if unsized_locals/unsized_fn_params is disabled, + // since we will error during typeck. + if !tcx.features().unsized_locals && !tcx.features().unsized_fn_params { + return; + } + + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + let param_env = tcx.param_env(def_id); + + let ocx = ObligationCtxt::new(&infcx); + for field_ty in &coroutine_layout.field_tys { + ocx.register_bound( + ObligationCause::new( + field_ty.source_info.span, + def_id, + ObligationCauseCode::SizedCoroutineInterior(def_id), + ), + param_env, + field_ty.ty, + tcx.require_lang_item(hir::LangItem::Sized, Some(field_ty.source_info.span)), + ); + } + + let errors = ocx.select_all_or_error(); + debug!(?errors); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(errors); + } +} + impl<'tcx> MirPass<'tcx> for StateTransform { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let Some(old_yield_ty) = body.yield_ty() else { @@ -1964,15 +1998,21 @@ fn check_must_not_suspend_ty<'tcx>( debug!("Checking must_not_suspend for {}", ty); match *ty.kind() { - ty::Adt(..) if ty.is_box() => { - let boxed_ty = ty.boxed_ty(); - let descr_pre = &format!("{}boxed ", data.descr_pre); + ty::Adt(_, args) if ty.is_box() => { + let boxed_ty = args.type_at(0); + let allocator_ty = args.type_at(1); check_must_not_suspend_ty( tcx, boxed_ty, hir_id, param_env, - SuspendCheckData { descr_pre, ..data }, + SuspendCheckData { descr_pre: &format!("{}boxed ", data.descr_pre), ..data }, + ) || check_must_not_suspend_ty( + tcx, + allocator_ty, + hir_id, + param_env, + SuspendCheckData { descr_pre: &format!("{}allocator ", data.descr_pre), ..data }, ) } ty::Adt(def, _) => check_must_not_suspend_def(tcx, def.did(), hir_id, data), 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 e40f4520671bb..3d6c1a952041c 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -1,19 +1,88 @@ -//! A MIR pass which duplicates a coroutine's body and removes any derefs which -//! would be present for upvars that are taken by-ref. The result of which will -//! be a coroutine body that takes all of its upvars by-move, and which we stash -//! into the `CoroutineInfo` for all coroutines returned by coroutine-closures. +//! This pass constructs a second coroutine body sufficient for return from +//! `FnOnce`/`AsyncFnOnce` implementations for coroutine-closures (e.g. async closures). +//! +//! Consider an async closure like: +//! ```rust +//! #![feature(async_closure)] +//! +//! let x = vec![1, 2, 3]; +//! +//! let closure = async move || { +//! println!("{x:#?}"); +//! }; +//! ``` +//! +//! This desugars to something like: +//! ```rust,ignore (invalid-borrowck) +//! let x = vec![1, 2, 3]; +//! +//! let closure = move || { +//! async { +//! println!("{x:#?}"); +//! } +//! }; +//! ``` +//! +//! Important to note here is that while the outer closure *moves* `x: Vec` +//! into its upvars, the inner `async` coroutine simply captures a ref of `x`. +//! This is the "magic" of async closures -- the futures that they return are +//! allowed to borrow from their parent closure's upvars. +//! +//! However, what happens when we call `closure` with `AsyncFnOnce` (or `FnOnce`, +//! since all async closures implement that too)? Well, recall the signature: +//! ``` +//! use std::future::Future; +//! pub trait AsyncFnOnce +//! { +//! type CallOnceFuture: Future; +//! type Output; +//! fn async_call_once( +//! self, +//! args: Args +//! ) -> Self::CallOnceFuture; +//! } +//! ``` +//! +//! This signature *consumes* the async closure (`self`) and returns a `CallOnceFuture`. +//! How do we deal with the fact that the coroutine is supposed to take a reference +//! to the captured `x` from the parent closure, when that parent closure has been +//! destroyed? +//! +//! This is the second piece of magic of async closures. We can simply create a +//! *second* `async` coroutine body where that `x` that was previously captured +//! by reference is now captured by value. This means that we consume the outer +//! closure and return a new coroutine that will hold onto all of these captures, +//! and drop them when it is finished (i.e. after it has been `.await`ed). +//! +//! We do this with the analysis below, which detects the captures that come from +//! borrowing from the outer closure, and we simply peel off a `deref` projection +//! from them. This second body is stored alongside the first body, and optimized +//! with it in lockstep. When we need to resolve a body for `FnOnce` or `AsyncFnOnce`, +//! we use this "by-move" body instead. +//! +//! ## How does this work? +//! +//! This pass essentially remaps the body of the (child) closure of the coroutine-closure +//! to take the set of upvars of the parent closure by value. This at least requires +//! changing a by-ref upvar to be by-value in the case that the outer coroutine-closure +//! captures something by value; however, it may also require renumbering field indices +//! in case precise captures (edition 2021 closure capture rules) caused the inner coroutine +//! to split one field capture into two. -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; +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}; -use rustc_target::abi::FieldIdx; +use rustc_target::abi::{FieldIdx, VariantIdx}; pub struct ByMoveBody; impl<'tcx> MirPass<'tcx> for ByMoveBody { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { + // We only need to generate by-move coroutine bodies for coroutines that come + // from coroutine-closures. let Some(coroutine_def_id) = body.source.def_id().as_local() else { return; }; @@ -22,96 +91,117 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { else { return; }; + + // Also, let's skip processing any bodies with errors, since there's no guarantee + // the MIR body will be constructed well. let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; if coroutine_ty.references_error() { return; } - let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!("{body:#?}") }; - let coroutine_kind = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap(); - if coroutine_kind == ty::ClosureKind::FnOnce { + // We don't need to generate a by-move coroutine if the coroutine body was + // produced by the `CoroutineKindShim`, since it's already by-move. + if matches!(body.source.instance, ty::InstanceDef::CoroutineKindShim { .. }) { return; } - let mut by_ref_fields = FxIndexSet::default(); - let by_move_upvars = Ty::new_tup_from_iter( - tcx, - tcx.closure_captures(coroutine_def_id).iter().enumerate().map(|(idx, capture)| { - if capture.is_by_ref() { - by_ref_fields.insert(FieldIdx::from_usize(idx)); + let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!("{body:#?}") }; + let args = args.as_coroutine(); + + let coroutine_kind = args.kind_ty().to_opt_closure_kind().unwrap(); + + let parent_def_id = tcx.local_parent(coroutine_def_id); + let ty::CoroutineClosure(_, parent_args) = + *tcx.type_of(parent_def_id).instantiate_identity().kind() + else { + bug!(); + }; + let parent_closure_args = parent_args.as_coroutine_closure(); + let num_args = parent_closure_args + .coroutine_closure_sig() + .skip_binder() + .tupled_inputs_ty + .tuple_fields() + .len(); + + 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 = + &child_capture.place.projections[parent_capture.place.projections.len()..]; + + // If the parent captures by-move, and the child captures by-ref, then we + // need to peel an additional `deref` off of the body of the child. + let needs_deref = child_capture.is_by_ref() && !parent_capture.is_by_ref(); + if needs_deref { + assert_ne!( + coroutine_kind, + ty::ClosureKind::FnOnce, + "`FnOnce` coroutine-closures return coroutines that capture from \ + their body; it will always result in a borrowck error!" + ); } - capture.place.ty() - }), - ); - let by_move_coroutine_ty = Ty::new_coroutine( - tcx, - coroutine_def_id.to_def_id(), - ty::CoroutineArgs::new( - tcx, - ty::CoroutineArgsParts { - parent_args: args.as_coroutine().parent_args(), - kind_ty: Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce), - resume_ty: args.as_coroutine().resume_ty(), - yield_ty: args.as_coroutine().yield_ty(), - return_ty: args.as_coroutine().return_ty(), - witness: args.as_coroutine().witness(), - tupled_upvars_ty: by_move_upvars, - }, - ) - .args, - ); - let mut by_move_body = body.clone(); - MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body); - dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); - by_move_body.source = mir::MirSource { - instance: InstanceDef::CoroutineKindShim { - coroutine_def_id: coroutine_def_id.to_def_id(), - target_kind: ty::ClosureKind::FnOnce, + // Finally, store the type of the parent's captured place. We need + // this when building the field projection in the MIR body later on. + let mut parent_capture_ty = parent_capture.place.ty(); + parent_capture_ty = match parent_capture.info.capture_kind { + ty::UpvarCapture::ByValue => parent_capture_ty, + ty::UpvarCapture::ByRef(kind) => Ty::new_ref( + tcx, + tcx.lifetimes.re_erased, + parent_capture_ty, + kind.to_mutbl_lossy(), + ), + }; + + ( + FieldIdx::from_usize(child_field_idx + num_args), + ( + FieldIdx::from_usize(parent_field_idx + num_args), + parent_capture_ty, + needs_deref, + child_precise_captures, + ), + ) }, - promoted: None, - }; - body.coroutine.as_mut().unwrap().by_move_body = Some(by_move_body); + ) + .collect(); + + if coroutine_kind == ty::ClosureKind::FnOnce { + assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len()); + return; + } - // If this is coming from an `AsyncFn` coroutine-closure, we must also create a by-mut body. - // This is actually just a copy of the by-ref body, but with a different self type. - // FIXME(async_closures): We could probably unify this with the by-ref body somehow. - if coroutine_kind == ty::ClosureKind::Fn { - let by_mut_coroutine_ty = Ty::new_coroutine( + let by_move_coroutine_ty = tcx + .instantiate_bound_regions_with_erased(parent_closure_args.coroutine_closure_sig()) + .to_coroutine_given_kind_and_upvars( tcx, + parent_closure_args.parent_args(), coroutine_def_id.to_def_id(), - ty::CoroutineArgs::new( - tcx, - ty::CoroutineArgsParts { - parent_args: args.as_coroutine().parent_args(), - kind_ty: Ty::from_closure_kind(tcx, ty::ClosureKind::FnMut), - resume_ty: args.as_coroutine().resume_ty(), - yield_ty: args.as_coroutine().yield_ty(), - return_ty: args.as_coroutine().return_ty(), - witness: args.as_coroutine().witness(), - tupled_upvars_ty: args.as_coroutine().tupled_upvars_ty(), - }, - ) - .args, + ty::ClosureKind::FnOnce, + tcx.lifetimes.re_erased, + parent_closure_args.tupled_upvars_ty(), + parent_closure_args.coroutine_captures_by_ref_ty(), ); - let mut by_mut_body = body.clone(); - by_mut_body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty = by_mut_coroutine_ty; - dump_mir(tcx, false, "coroutine_by_mut", &0, &by_mut_body, |_, _| Ok(())); - by_mut_body.source = mir::MirSource { - instance: InstanceDef::CoroutineKindShim { - coroutine_def_id: coroutine_def_id.to_def_id(), - target_kind: ty::ClosureKind::FnMut, - }, - promoted: None, - }; - body.coroutine.as_mut().unwrap().by_mut_body = Some(by_mut_body); - } + + let mut by_move_body = body.clone(); + MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); + dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); + // FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body. + by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim { + coroutine_def_id: coroutine_def_id.to_def_id(), + }); + body.coroutine.as_mut().unwrap().by_move_body = Some(by_move_body); } } struct MakeByMoveBody<'tcx> { tcx: TyCtxt<'tcx>, - by_ref_fields: FxIndexSet, + field_remapping: UnordMap, bool, &'tcx [Projection<'tcx>])>, by_move_coroutine_ty: Ty<'tcx>, } @@ -126,24 +216,57 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { context: mir::visit::PlaceContext, location: mir::Location, ) { + // Initializing an upvar local always starts with `CAPTURE_STRUCT_LOCAL` and a + // field projection. If this is in `field_remapping`, then it must not be an + // arg from calling the closure, but instead an upvar. if place.local == ty::CAPTURE_STRUCT_LOCAL - && !place.projection.is_empty() - && let mir::ProjectionElem::Field(idx, ty) = place.projection[0] - && self.by_ref_fields.contains(&idx) + && let Some((&mir::ProjectionElem::Field(idx, _), projection)) = + place.projection.split_first() + && let Some(&(remapped_idx, remapped_ty, needs_deref, bridging_projections)) = + self.field_remapping.get(&idx) { - let (begin, end) = place.projection[1..].split_first().unwrap(); - // FIXME(async_closures): I'm actually a bit surprised to see that we always - // initially deref the by-ref upvars. If this is not actually true, then we - // will at least get an ICE that explains why this isn't true :^) - assert_eq!(*begin, mir::ProjectionElem::Deref); - // Peel one ref off of the ty. - let peeled_ty = ty.builtin_deref(true).unwrap().ty; + // As noted before, if the parent closure captures a field by value, and + // the child captures a field by ref, then for the by-move body we're + // generating, we also are taking that field by value. Peel off a deref, + // since a layer of ref'ing has now become redundant. + let final_projections = if needs_deref { + let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first() + else { + bug!( + "There should be at least a single deref for an upvar local initialization, found {projection:#?}" + ); + }; + // There may be more derefs, since we may also implicitly reborrow + // a captured mut pointer. + projection + } else { + projection + }; + + // These projections are applied in order to "bridge" the local that we are + // currently transforming *from* the old upvar that the by-ref coroutine used + // to capture *to* the upvar of the parent coroutine-closure. For example, if + // the parent captures `&s` but the child captures `&(s.field)`, then we will + // apply a field projection. + let bridging_projections = bridging_projections.iter().map(|elem| match elem.kind { + ProjectionKind::Deref => mir::ProjectionElem::Deref, + ProjectionKind::Field(idx, VariantIdx::ZERO) => { + mir::ProjectionElem::Field(idx, elem.ty) + } + _ => unreachable!("precise captures only through fields and derefs"), + }); + + // We start out with an adjusted field index (and ty), representing the + // upvar that we get from our parent closure. We apply any of the additional + // projections to make sure that to the rest of the body of the closure, the + // place looks the same, and then apply that final deref if necessary. *place = mir::Place { local: place.local, projection: self.tcx.mk_place_elems_from_iter( - [mir::ProjectionElem::Field(idx, peeled_ty)] + [mir::ProjectionElem::Field(remapped_idx, remapped_ty)] .into_iter() - .chain(end.iter().copied()), + .chain(bridging_projections) + .chain(final_projections.iter().copied()), ), }; } diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 9a1d8bae6b410..6e73a476421f2 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -1,13 +1,12 @@ +use std::fmt::{self, Debug}; + use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::graph::WithNumNodes; -use rustc_index::bit_set::BitSet; +use rustc_data_structures::graph::DirectedGraph; use rustc_index::IndexVec; -use rustc_middle::mir::coverage::*; +use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; -use super::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops}; - -use std::fmt::{self, Debug}; +use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops}; /// The coverage counter or counter expression associated with a particular /// BCB node or BCB edge. @@ -18,10 +17,6 @@ pub(super) enum BcbCounter { } impl BcbCounter { - fn is_expression(&self) -> bool { - matches!(self, Self::Expression { .. }) - } - pub(super) fn as_term(&self) -> CovTerm { match *self { BcbCounter::Counter { id, .. } => CovTerm::Counter(id), @@ -60,10 +55,6 @@ pub(super) struct CoverageCounters { /// We currently don't iterate over this map, but if we do in the future, /// switch it back to `FxIndexMap` to avoid query stability hazards. bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, - /// Tracks which BCBs have a counter associated with some incoming edge. - /// Only used by assertions, to verify that BCBs with incoming edge - /// counters do not have their own physical counters (expressions are allowed). - bcb_has_incoming_edge_counters: BitSet, /// Table of expression data, associating each expression ID with its /// corresponding operator (+ or -) and its LHS/RHS operands. expressions: IndexVec, @@ -83,7 +74,6 @@ impl CoverageCounters { counter_increment_sites: IndexVec::new(), bcb_counters: IndexVec::from_elem_n(None, num_bcbs), bcb_edge_counters: FxHashMap::default(), - bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs), expressions: IndexVec::new(), }; @@ -122,14 +112,6 @@ impl CoverageCounters { } fn set_bcb_counter(&mut self, bcb: BasicCoverageBlock, counter_kind: BcbCounter) -> BcbCounter { - assert!( - // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also - // have an expression (to be injected into an existing `BasicBlock` represented by this - // `BasicCoverageBlock`). - counter_kind.is_expression() || !self.bcb_has_incoming_edge_counters.contains(bcb), - "attempt to add a `Counter` to a BCB target with existing incoming edge counters" - ); - if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) { bug!( "attempt to set a BasicCoverageBlock coverage counter more than once; \ @@ -146,19 +128,6 @@ impl CoverageCounters { to_bcb: BasicCoverageBlock, counter_kind: BcbCounter, ) -> BcbCounter { - // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also - // have an expression (to be injected into an existing `BasicBlock` represented by this - // `BasicCoverageBlock`). - if let Some(node_counter) = self.bcb_counter(to_bcb) - && !node_counter.is_expression() - { - bug!( - "attempt to add an incoming edge counter from {from_bcb:?} \ - when the target BCB already has {node_counter:?}" - ); - } - - self.bcb_has_incoming_edge_counters.insert(to_bcb); if let Some(replaced) = self.bcb_edge_counters.insert((from_bcb, to_bcb), counter_kind) { bug!( "attempt to set an edge counter more than once; from_bcb: \ diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index ed8c4d8283d98..1895735ab3523 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -1,7 +1,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::dominators::{self, Dominators}; -use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; +use rustc_data_structures::graph::{self, DirectedGraph, StartNode}; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind}; @@ -193,16 +193,14 @@ impl IndexMut for CoverageGraph { impl graph::DirectedGraph for CoverageGraph { type Node = BasicCoverageBlock; -} -impl graph::WithNumNodes for CoverageGraph { #[inline] fn num_nodes(&self) -> usize { self.bcbs.len() } } -impl graph::WithStartNode for CoverageGraph { +impl graph::StartNode for CoverageGraph { #[inline] fn start_node(&self) -> Self::Node { self.bcb_from_bb(mir::START_BLOCK) @@ -210,28 +208,16 @@ impl graph::WithStartNode for CoverageGraph { } } -type BcbSuccessors<'graph> = std::slice::Iter<'graph, BasicCoverageBlock>; - -impl<'graph> graph::GraphSuccessors<'graph> for CoverageGraph { - type Item = BasicCoverageBlock; - type Iter = std::iter::Cloned>; -} - -impl graph::WithSuccessors for CoverageGraph { +impl graph::Successors for CoverageGraph { #[inline] - fn successors(&self, node: Self::Node) -> >::Iter { + fn successors(&self, node: Self::Node) -> impl Iterator { self.successors[node].iter().cloned() } } -impl<'graph> graph::GraphPredecessors<'graph> for CoverageGraph { - type Item = BasicCoverageBlock; - type Iter = std::iter::Copied>; -} - -impl graph::WithPredecessors for CoverageGraph { +impl graph::Predecessors for CoverageGraph { #[inline] - fn predecessors(&self, node: Self::Node) -> >::Iter { + fn predecessors(&self, node: Self::Node) -> impl Iterator { self.predecessors[node].iter().copied() } } diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs new file mode 100644 index 0000000000000..d364658efb6d6 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -0,0 +1,275 @@ +use std::collections::BTreeSet; + +use rustc_data_structures::graph::DirectedGraph; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::coverage::{ + BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind, MCDCBranchSpan, MCDCDecisionSpan, +}; +use rustc_middle::mir::{self, BasicBlock, StatementKind}; +use rustc_span::Span; + +use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; +use crate::coverage::spans::{ + extract_refined_covspans, unexpand_into_body_span_with_visible_macro, +}; +use crate::coverage::ExtractedHirInfo; +use rustc_index::IndexVec; + +#[derive(Clone, Debug)] +pub(super) enum BcbMappingKind { + /// Associates an ordinary executable code span with its corresponding BCB. + Code(BasicCoverageBlock), + + // Ordinary branch mappings are stored separately, so they don't have a + // variant in this enum. + // + /// Associates a mcdc branch span with condition info besides fields for normal branch. + MCDCBranch { + true_bcb: BasicCoverageBlock, + false_bcb: BasicCoverageBlock, + /// If `None`, this actually represents a normal branch mapping inserted + /// for code that was too complex for MC/DC. + condition_info: Option, + decision_depth: u16, + }, + /// Associates a mcdc decision with its join BCB. + MCDCDecision { + end_bcbs: BTreeSet, + bitmap_idx: u32, + conditions_num: u16, + decision_depth: u16, + }, +} + +#[derive(Debug)] +pub(super) struct BcbMapping { + pub(super) kind: BcbMappingKind, + pub(super) span: Span, +} + +/// This is separate from [`BcbMappingKind`] to help prepare for larger changes +/// that will be needed for improved branch coverage in the future. +/// (See .) +#[derive(Debug)] +pub(super) struct BcbBranchPair { + pub(super) span: Span, + pub(super) true_bcb: BasicCoverageBlock, + pub(super) false_bcb: BasicCoverageBlock, +} + +pub(super) struct CoverageSpans { + bcb_has_mappings: BitSet, + pub(super) mappings: Vec, + pub(super) branch_pairs: Vec, + test_vector_bitmap_bytes: u32, +} + +impl CoverageSpans { + pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool { + self.bcb_has_mappings.contains(bcb) + } + + pub(super) fn test_vector_bitmap_bytes(&self) -> u32 { + self.test_vector_bitmap_bytes + } +} + +/// Extracts coverage-relevant spans from MIR, and associates them with +/// their corresponding BCBs. +/// +/// Returns `None` if no coverage-relevant spans could be extracted. +pub(super) fn generate_coverage_spans( + mir_body: &mir::Body<'_>, + hir_info: &ExtractedHirInfo, + basic_coverage_blocks: &CoverageGraph, +) -> Option { + let mut mappings = vec![]; + let mut branch_pairs = vec![]; + + if hir_info.is_async_fn { + // An async function desugars into a function that returns a future, + // with the user code wrapped in a closure. Any spans in the desugared + // outer function will be unhelpful, so just keep the signature span + // and ignore all of the spans in the MIR body. + if let Some(span) = hir_info.fn_sig_span_extended { + mappings.push(BcbMapping { kind: BcbMappingKind::Code(START_BCB), span }); + } + } else { + extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut mappings); + + branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks)); + + mappings.extend(extract_mcdc_mappings(mir_body, hir_info.body_span, basic_coverage_blocks)); + } + + if mappings.is_empty() && branch_pairs.is_empty() { + return None; + } + + // Identify which BCBs have one or more mappings. + let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + let mut insert = |bcb| { + bcb_has_mappings.insert(bcb); + }; + let mut test_vector_bitmap_bytes = 0; + for BcbMapping { kind, span: _ } in &mappings { + match *kind { + BcbMappingKind::Code(bcb) => insert(bcb), + BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => { + insert(true_bcb); + insert(false_bcb); + } + BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => { + // `bcb_has_mappings` is used for inject coverage counters + // but they are not needed for decision BCBs. + // While the length of test vector bitmap should be calculated here. + test_vector_bitmap_bytes = test_vector_bitmap_bytes + .max(bitmap_idx + (1_u32 << conditions_num as u32).div_ceil(8)); + } + } + } + for &BcbBranchPair { true_bcb, false_bcb, .. } in &branch_pairs { + insert(true_bcb); + insert(false_bcb); + } + + Some(CoverageSpans { bcb_has_mappings, mappings, branch_pairs, test_vector_bitmap_bytes }) +} + +fn resolve_block_markers( + branch_info: &mir::coverage::BranchInfo, + mir_body: &mir::Body<'_>, +) -> IndexVec> { + let mut block_markers = IndexVec::>::from_elem_n( + None, + branch_info.num_block_markers, + ); + + // Fill out the mapping from block marker IDs to their enclosing blocks. + for (bb, data) in mir_body.basic_blocks.iter_enumerated() { + for statement in &data.statements { + if let StatementKind::Coverage(CoverageKind::BlockMarker { id }) = statement.kind { + block_markers[id] = Some(bb); + } + } + } + + block_markers +} + +// FIXME: There is currently a lot of redundancy between +// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so +// that they can each be modified without interfering with the other, but in +// the long term we should try to bring them together again when branch coverage +// and MC/DC coverage support are more mature. + +pub(super) fn extract_branch_pairs( + mir_body: &mir::Body<'_>, + hir_info: &ExtractedHirInfo, + basic_coverage_blocks: &CoverageGraph, +) -> Vec { + let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] }; + + let block_markers = resolve_block_markers(branch_info, mir_body); + + branch_info + .branch_spans + .iter() + .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| { + // For now, ignore any branch span that was introduced by + // expansion. This makes things like assert macros less noisy. + if !raw_span.ctxt().outer_expn_data().is_root() { + return None; + } + let (span, _) = + unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?; + + let bcb_from_marker = + |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); + + let true_bcb = bcb_from_marker(true_marker)?; + let false_bcb = bcb_from_marker(false_marker)?; + + Some(BcbBranchPair { span, true_bcb, false_bcb }) + }) + .collect::>() +} + +pub(super) fn extract_mcdc_mappings( + mir_body: &mir::Body<'_>, + body_span: Span, + basic_coverage_blocks: &CoverageGraph, +) -> Vec { + let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { + return vec![]; + }; + + let block_markers = resolve_block_markers(branch_info, mir_body); + + let bcb_from_marker = + |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); + + let check_branch_bcb = + |raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| { + // For now, ignore any branch span that was introduced by + // expansion. This makes things like assert macros less noisy. + if !raw_span.ctxt().outer_expn_data().is_root() { + return None; + } + let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?; + + let true_bcb = bcb_from_marker(true_marker)?; + let false_bcb = bcb_from_marker(false_marker)?; + Some((span, true_bcb, false_bcb)) + }; + + let mcdc_branch_filter_map = |&MCDCBranchSpan { + span: raw_span, + true_marker, + false_marker, + condition_info, + decision_depth, + }| { + check_branch_bcb(raw_span, true_marker, false_marker).map(|(span, true_bcb, false_bcb)| { + BcbMapping { + kind: BcbMappingKind::MCDCBranch { + true_bcb, + false_bcb, + condition_info, + decision_depth, + }, + span, + } + }) + }; + + let mut next_bitmap_idx = 0; + + let decision_filter_map = |decision: &MCDCDecisionSpan| { + let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?; + + let end_bcbs = decision + .end_markers + .iter() + .map(|&marker| bcb_from_marker(marker)) + .collect::>()?; + + let bitmap_idx = next_bitmap_idx; + next_bitmap_idx += (1_u32 << decision.conditions_num).div_ceil(8); + + Some(BcbMapping { + kind: BcbMappingKind::MCDCDecision { + end_bcbs, + bitmap_idx, + conditions_num: decision.conditions_num as u16, + decision_depth: decision.decision_depth, + }, + span, + }) + }; + + std::iter::empty() + .chain(branch_info.mcdc_branch_spans.iter().filter_map(mcdc_branch_filter_map)) + .chain(branch_info.mcdc_decision_spans.iter().filter_map(decision_filter_map)) + .collect::>() +} diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index b2407c545071b..9edde6662469e 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -2,21 +2,20 @@ pub mod query; mod counters; mod graph; +mod mappings; mod spans; - #[cfg(test)] mod tests; use self::counters::{CounterIncrementSite, CoverageCounters}; use self::graph::{BasicCoverageBlock, CoverageGraph}; -use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans}; +use self::mappings::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans}; use crate::MirPass; -use rustc_middle::hir; use rustc_middle::mir::coverage::*; use rustc_middle::mir::{ - self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator, + self, BasicBlock, BasicBlockData, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::TyCtxt; @@ -72,7 +71,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: //////////////////////////////////////////////////// // Compute coverage spans from the `CoverageGraph`. let Some(coverage_spans) = - spans::generate_coverage_spans(mir_body, &hir_info, &basic_coverage_blocks) + mappings::generate_coverage_spans(mir_body, &hir_info, &basic_coverage_blocks) else { // No relevant spans were found in MIR, so skip instrumenting this function. return; @@ -101,11 +100,25 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: &coverage_counters, ); + inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans); + + let mcdc_max_decision_depth = coverage_spans + .mappings + .iter() + .filter_map(|bcb_mapping| match bcb_mapping.kind { + BcbMappingKind::MCDCDecision { decision_depth, .. } => Some(decision_depth), + _ => None, + }) + .max() + .unwrap_or(0); + mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { function_source_hash: hir_info.function_source_hash, num_counters: coverage_counters.num_counters(), + mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(), expressions: coverage_counters.into_expressions(), mappings, + mcdc_max_decision_depth, })); } @@ -124,8 +137,11 @@ fn create_mappings<'tcx>( let body_span = hir_info.body_span; let source_file = source_map.lookup_source_file(body_span.lo()); - use rustc_session::RemapFileNameExt; - let file_name = Symbol::intern(&source_file.name.for_codegen(tcx.sess).to_string_lossy()); + + use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; + let file_name = Symbol::intern( + &source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(), + ); let term_for_bcb = |bcb| { coverage_counters @@ -134,20 +150,48 @@ fn create_mappings<'tcx>( .as_term() }; - coverage_spans - .all_bcb_mappings() - .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { - let kind = match bcb_mapping_kind { + let mut mappings = Vec::new(); + + mappings.extend(coverage_spans.mappings.iter().filter_map( + |BcbMapping { kind: bcb_mapping_kind, span }| { + let kind = match *bcb_mapping_kind { BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), - BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch { + BcbMappingKind::MCDCBranch { + true_bcb, false_bcb, condition_info: None, .. + } => MappingKind::Branch { + true_term: term_for_bcb(true_bcb), + false_term: term_for_bcb(false_bcb), + }, + BcbMappingKind::MCDCBranch { + true_bcb, + false_bcb, + condition_info: Some(mcdc_params), + .. + } => MappingKind::MCDCBranch { true_term: term_for_bcb(true_bcb), false_term: term_for_bcb(false_bcb), + mcdc_params, }, + BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => { + MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, conditions_num }) + } }; + let code_region = make_code_region(source_map, file_name, *span, body_span)?; + Some(Mapping { kind, code_region }) + }, + )); + + mappings.extend(coverage_spans.branch_pairs.iter().filter_map( + |&BcbBranchPair { span, true_bcb, false_bcb }| { + let true_term = term_for_bcb(true_bcb); + let false_term = term_for_bcb(false_bcb); + let kind = MappingKind::Branch { true_term, false_term }; let code_region = make_code_region(source_map, file_name, span, body_span)?; Some(Mapping { kind, code_region }) - }) - .collect::>() + }, + )); + + mappings } /// For each BCB node or BCB edge that has an associated coverage counter, @@ -202,6 +246,59 @@ fn inject_coverage_statements<'tcx>( } } +/// For each conditions inject statements to update condition bitmap after it has been evaluated. +/// For each decision inject statements to update test vector bitmap after it has been evaluated. +fn inject_mcdc_statements<'tcx>( + mir_body: &mut mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + coverage_spans: &CoverageSpans, +) { + if coverage_spans.test_vector_bitmap_bytes() == 0 { + return; + } + + // Inject test vector update first because `inject_statement` always insert new statement at head. + for (end_bcbs, bitmap_idx, decision_depth) in + coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind { + BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, decision_depth, .. } => { + Some((end_bcbs, *bitmap_idx, *decision_depth)) + } + _ => None, + }) + { + for end in end_bcbs { + let end_bb = basic_coverage_blocks[*end].leader_bb(); + inject_statement( + mir_body, + CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth }, + end_bb, + ); + } + } + + for (true_bcb, false_bcb, condition_id, decision_depth) in + coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind { + BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info, decision_depth } => { + Some((true_bcb, false_bcb, condition_info?.condition_id, decision_depth)) + } + _ => None, + }) + { + let true_bb = basic_coverage_blocks[true_bcb].leader_bb(); + inject_statement( + mir_body, + CoverageKind::CondBitmapUpdate { id: condition_id, value: true, decision_depth }, + true_bb, + ); + let false_bb = basic_coverage_blocks[false_bcb].leader_bb(); + inject_statement( + mir_body, + CoverageKind::CondBitmapUpdate { id: condition_id, value: false, decision_depth }, + false_bb, + ); + } +} + /// Given two basic blocks that have a control-flow edge between them, creates /// and returns a new block that sits between those blocks. fn inject_edge_counter_basic_block( @@ -231,10 +328,7 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb debug!(" injecting statement {counter_kind:?} for {bb:?}"); let data = &mut mir_body[bb]; let source_info = data.terminator().source_info; - let statement = Statement { - source_info, - kind: StatementKind::Coverage(Box::new(Coverage { kind: counter_kind })), - }; + let statement = Statement { source_info, kind: StatementKind::Coverage(counter_kind) }; data.statements.insert(0, statement); } @@ -368,8 +462,7 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir // to HIR for it. let hir_node = tcx.hir_node_by_def_id(def_id); - let (_, fn_body_id) = - hir::map::associated_body(hir_node).expect("HIR node is a function with body"); + let fn_body_id = hir_node.body_id().expect("HIR node is a function with body"); let hir_body = tcx.hir().body(fn_body_id); let maybe_fn_sig = hir_node.fn_sig(); diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 1de7b6f66a7bc..f77ee63d02c20 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,7 +1,7 @@ use rustc_data_structures::captures::Captures; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::{CounterId, CoverageKind}; -use rustc_middle::mir::{Body, Coverage, CoverageIdsInfo, Statement, StatementKind}; +use rustc_middle::mir::{Body, CoverageIdsInfo, Statement, StatementKind}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; @@ -54,24 +54,32 @@ fn coverage_ids_info<'tcx>( let mir_body = tcx.instance_mir(instance_def); let max_counter_id = all_coverage_in_mir_body(mir_body) - .filter_map(|coverage| match coverage.kind { + .filter_map(|kind| match *kind { CoverageKind::CounterIncrement { id } => Some(id), _ => None, }) .max() - .unwrap_or(CounterId::START); + .unwrap_or(CounterId::ZERO); - CoverageIdsInfo { max_counter_id } + let mcdc_bitmap_bytes = mir_body + .coverage_branch_info + .as_deref() + .map(|info| { + info.mcdc_decision_spans + .iter() + .fold(0, |acc, decision| acc + (1_u32 << decision.conditions_num).div_ceil(8)) + }) + .unwrap_or_default(); + + CoverageIdsInfo { max_counter_id, mcdc_bitmap_bytes } } fn all_coverage_in_mir_body<'a, 'tcx>( body: &'a Body<'tcx>, -) -> impl Iterator + Captures<'tcx> { +) -> impl Iterator + Captures<'tcx> { body.basic_blocks.iter().flat_map(|bb_data| &bb_data.statements).filter_map(|statement| { match statement.kind { - StatementKind::Coverage(box ref coverage) if !is_inlined(body, statement) => { - Some(coverage) - } + StatementKind::Coverage(ref kind) if !is_inlined(body, statement) => Some(kind), _ => None, } }) diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 672de1fbe60fd..d6432e2e9d41c 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,101 +1,31 @@ -use rustc_data_structures::graph::WithNumNodes; -use rustc_index::bit_set::BitSet; use rustc_middle::mir; use rustc_span::{BytePos, Span}; -use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; +use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; +use crate::coverage::mappings::{BcbMapping, BcbMappingKind}; use crate::coverage::spans::from_mir::SpanFromMir; use crate::coverage::ExtractedHirInfo; mod from_mir; -#[derive(Clone, Copy, Debug)] -pub(super) enum BcbMappingKind { - /// Associates an ordinary executable code span with its corresponding BCB. - Code(BasicCoverageBlock), - /// Associates a branch span with BCBs for its true and false arms. - Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock }, -} - -#[derive(Debug)] -pub(super) struct BcbMapping { - pub(super) kind: BcbMappingKind, - pub(super) span: Span, -} - -pub(super) struct CoverageSpans { - bcb_has_mappings: BitSet, - mappings: Vec, -} - -impl CoverageSpans { - pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool { - self.bcb_has_mappings.contains(bcb) - } - - pub(super) fn all_bcb_mappings(&self) -> impl Iterator { - self.mappings.iter() - } -} +// FIXME(#124545) It's awkward that we have to re-export this, because it's an +// internal detail of `from_mir` that is also needed when handling branch and +// MC/DC spans. Ideally we would find a more natural home for it. +pub(super) use from_mir::unexpand_into_body_span_with_visible_macro; -/// Extracts coverage-relevant spans from MIR, and associates them with -/// their corresponding BCBs. -/// -/// Returns `None` if no coverage-relevant spans could be extracted. -pub(super) fn generate_coverage_spans( +pub(super) fn extract_refined_covspans( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, -) -> Option { - let mut mappings = vec![]; - - if hir_info.is_async_fn { - // An async function desugars into a function that returns a future, - // with the user code wrapped in a closure. Any spans in the desugared - // outer function will be unhelpful, so just keep the signature span - // and ignore all of the spans in the MIR body. - if let Some(span) = hir_info.fn_sig_span_extended { - mappings.push(BcbMapping { kind: BcbMappingKind::Code(START_BCB), span }); - } - } else { - let sorted_spans = from_mir::mir_to_initial_sorted_coverage_spans( - mir_body, - hir_info, - basic_coverage_blocks, - ); - let coverage_spans = SpansRefiner::refine_sorted_spans(sorted_spans); - mappings.extend(coverage_spans.into_iter().map(|RefinedCovspan { bcb, span, .. }| { - // Each span produced by the generator represents an ordinary code region. - BcbMapping { kind: BcbMappingKind::Code(bcb), span } - })); - - mappings.extend(from_mir::extract_branch_mappings( - mir_body, - hir_info.body_span, - basic_coverage_blocks, - )); - } - - if mappings.is_empty() { - return None; - } - - // Identify which BCBs have one or more mappings. - let mut bcb_has_mappings = BitSet::new_empty(basic_coverage_blocks.num_nodes()); - let mut insert = |bcb| { - bcb_has_mappings.insert(bcb); - }; - for &BcbMapping { kind, span: _ } in &mappings { - match kind { - BcbMappingKind::Code(bcb) => insert(bcb), - BcbMappingKind::Branch { true_bcb, false_bcb } => { - insert(true_bcb); - insert(false_bcb); - } - } - } - - Some(CoverageSpans { bcb_has_mappings, mappings }) + mappings: &mut impl Extend, +) { + let sorted_spans = + from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks); + let coverage_spans = SpansRefiner::refine_sorted_spans(sorted_spans); + mappings.extend(coverage_spans.into_iter().map(|RefinedCovspan { bcb, span, .. }| { + // Each span produced by the generator represents an ordinary code region. + BcbMapping { kind: BcbMappingKind::Code(bcb), span } + })); } #[derive(Debug)] diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 86097bdcd9537..4ce37b5defcee 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,9 +1,8 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; -use rustc_index::IndexVec; -use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::{ - self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, + self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; @@ -11,7 +10,6 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; -use crate::coverage::spans::{BcbMapping, BcbMappingKind}; use crate::coverage::ExtractedHirInfo; /// Traverses the MIR body to produce an initial collection of coverage-relevant @@ -187,9 +185,7 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option { // for their parent `BasicBlock`. StatementKind::StorageLive(_) | StatementKind::StorageDead(_) - // Ignore `ConstEvalCounter`s | StatementKind::ConstEvalCounter - // Ignore `Nop`s | StatementKind::Nop => None, // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead` @@ -211,30 +207,31 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option { StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None, // Retain spans from most other statements. - StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding` + StatementKind::FakeRead(_) | StatementKind::Intrinsic(..) - | StatementKind::Coverage(box mir::Coverage { + | StatementKind::Coverage( // The purpose of `SpanMarker` is to be matched and accepted here. - kind: CoverageKind::SpanMarker - }) + CoverageKind::SpanMarker, + ) | StatementKind::Assign(_) | StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(..) | StatementKind::Retag(_, _) | StatementKind::PlaceMention(..) - | StatementKind::AscribeUserType(_, _) => { - Some(statement.source_info.span) - } - - StatementKind::Coverage(box mir::Coverage { - // Block markers are used for branch coverage, so ignore them here. - kind: CoverageKind::BlockMarker {..} - }) => None, - - StatementKind::Coverage(box mir::Coverage { - // These coverage statements should not exist prior to coverage instrumentation. - kind: CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } - }) => bug!("Unexpected coverage statement found during coverage instrumentation: {statement:?}"), + | StatementKind::AscribeUserType(_, _) => Some(statement.source_info.span), + + // Block markers are used for branch coverage, so ignore them here. + StatementKind::Coverage(CoverageKind::BlockMarker { .. }) => None, + + // These coverage statements should not exist prior to coverage instrumentation. + StatementKind::Coverage( + CoverageKind::CounterIncrement { .. } + | CoverageKind::ExpressionUsed { .. } + | CoverageKind::CondBitmapUpdate { .. } + | CoverageKind::TestVectorBitmapUpdate { .. }, + ) => bug!( + "Unexpected coverage statement found during coverage instrumentation: {statement:?}" + ), } } @@ -286,7 +283,7 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option { /// /// [^1]Expansions result from Rust syntax including macros, syntactic sugar, /// etc.). -fn unexpand_into_body_span_with_visible_macro( +pub(crate) fn unexpand_into_body_span_with_visible_macro( original_span: Span, body_span: Span, ) -> Option<(Span, Option)> { @@ -364,51 +361,3 @@ impl SpanFromMir { Self { span, visible_macro, bcb, is_hole } } } - -pub(super) fn extract_branch_mappings( - mir_body: &mir::Body<'_>, - body_span: Span, - basic_coverage_blocks: &CoverageGraph, -) -> Vec { - let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { - return vec![]; - }; - - let mut block_markers = IndexVec::>::from_elem_n( - None, - branch_info.num_block_markers, - ); - - // Fill out the mapping from block marker IDs to their enclosing blocks. - for (bb, data) in mir_body.basic_blocks.iter_enumerated() { - for statement in &data.statements { - if let StatementKind::Coverage(coverage) = &statement.kind - && let CoverageKind::BlockMarker { id } = coverage.kind - { - block_markers[id] = Some(bb); - } - } - } - - branch_info - .branch_spans - .iter() - .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| { - // For now, ignore any branch span that was introduced by - // expansion. This makes things like assert macros less noisy. - if !raw_span.ctxt().outer_expn_data().is_root() { - return None; - } - let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?; - - let bcb_from_marker = |marker: BlockMarkerId| { - Some(basic_coverage_blocks.bcb_from_bb(block_markers[marker]?)?) - }; - - let true_bcb = bcb_from_marker(true_marker)?; - let false_bcb = bcb_from_marker(false_marker)?; - - Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span }) - }) - .collect::>() -} diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index 569998de35e0b..cf1a2b399f951 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -28,8 +28,7 @@ use super::counters; use super::graph::{self, BasicCoverageBlock}; use itertools::Itertools; -use rustc_data_structures::graph::WithNumNodes; -use rustc_data_structures::graph::WithSuccessors; +use rustc_data_structures::graph::{DirectedGraph, Successors}; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::*; use rustc_middle::ty; diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 07e6ecccaa42c..483fd753e7077 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -23,10 +23,6 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { return false; } - if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) { - return false; - } - // This just reproduces the logic from Instance::requires_inline. match tcx.def_kind(def_id) { DefKind::Ctor(..) | DefKind::Closure => return true, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index f456196b2822d..d0f6ec8f21f84 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -2,52 +2,22 @@ //! //! Currently, this pass only propagates scalar values. -use rustc_const_eval::interpret::{ - HasStaticRootDefId, ImmTy, Immediate, InterpCx, OpTy, PlaceTy, PointerArithmetic, Projectable, -}; +use rustc_const_eval::const_eval::{throw_machine_stop_str, DummyMachine}; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; -use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar}; +use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::value_analysis::{ Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, }; use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor}; -use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT}; -/// Macro for machine-specific `InterpError` without allocation. -/// (These will never be shown to the user, but they help diagnose ICEs.) -pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{ - // We make a new local type for it. The type itself does not carry any information, - // but its vtable (for the `MachineStopType` trait) does. - #[derive(Debug)] - struct Zst; - // Printing this type shows the desired string. - impl std::fmt::Display for Zst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $($tt)*) - } - } - - impl rustc_middle::mir::interpret::MachineStopType for Zst { - fn diagnostic_message(&self) -> rustc_errors::DiagMessage { - self.to_string().into() - } - - fn add_args( - self: Box, - _: &mut dyn FnMut(rustc_errors::DiagArgName, rustc_errors::DiagArgValue), - ) {} - } - throw_machine_stop!(Zst) -}} - // These constants are somewhat random guesses and have not been optimized. // If `tcx.sess.mir_opt_level() >= 4`, we ignore the limits (this can become very expensive). const BLOCK_LIMIT: usize = 100; @@ -394,7 +364,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } Operand::Constant(box constant) => { if let Ok(constant) = - self.ecx.eval_mir_constant(&constant.const_, Some(constant.span), None) + self.ecx.eval_mir_constant(&constant.const_, constant.span, None) { self.assign_constant(state, place, constant, &[]); } @@ -714,6 +684,7 @@ fn try_write_constant<'tcx>( // Unsupported for now. ty::Array(_, _) + | ty::Pat(_, _) // Do not attempt to support indirection in constants. | ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) | ty::Str | ty::Slice(_) @@ -888,165 +859,3 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { } } } - -pub(crate) struct DummyMachine; - -impl HasStaticRootDefId for DummyMachine { - fn static_def_id(&self) -> Option { - None - } -} - -impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine { - rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>); - type MemoryKind = !; - const PANIC_ON_ALLOC_FAIL: bool = true; - - #[inline(always)] - fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { - false // no reason to enforce alignment - } - - fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { - false - } - - fn before_access_global( - _tcx: TyCtxtAt<'tcx>, - _machine: &Self, - _alloc_id: AllocId, - alloc: ConstAllocation<'tcx>, - _static_def_id: Option, - is_write: bool, - ) -> InterpResult<'tcx> { - if is_write { - throw_machine_stop_str!("can't write to global"); - } - - // If the static allocation is mutable, then we can't const prop it as its content - // might be different at runtime. - if alloc.inner().mutability.is_mut() { - throw_machine_stop_str!("can't access mutable globals in ConstProp"); - } - - Ok(()) - } - - fn find_mir_or_eval_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _abi: rustc_target::spec::abi::Abi, - _args: &[rustc_const_eval::interpret::FnArg<'tcx, Self::Provenance>], - _destination: &rustc_const_eval::interpret::MPlaceTy<'tcx, Self::Provenance>, - _target: Option, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { - unimplemented!() - } - - fn panic_nounwind( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &str, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn call_intrinsic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _args: &[rustc_const_eval::interpret::OpTy<'tcx, Self::Provenance>], - _destination: &rustc_const_eval::interpret::MPlaceTy<'tcx, Self::Provenance>, - _target: Option, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn assert_panic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &rustc_middle::mir::AssertMessage<'tcx>, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn binary_ptr_op( - ecx: &InterpCx<'mir, 'tcx, Self>, - bin_op: BinOp, - left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, - right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, - ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { - use rustc_middle::mir::BinOp::*; - Ok(match bin_op { - Eq | Ne | Lt | Le | Gt | Ge => { - // Types can differ, e.g. fn ptrs with different `for`. - assert_eq!(left.layout.abi, right.layout.abi); - let size = ecx.pointer_size(); - // Just compare the bits. ScalarPairs are compared lexicographically. - // We thus always compare pairs and simply fill scalars up with 0. - // If the pointer has provenance, `to_bits` will return `Err` and we bail out. - let left = match **left { - Immediate::Scalar(l) => (l.to_bits(size)?, 0), - Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?), - Immediate::Uninit => panic!("we should never see uninit data here"), - }; - let right = match **right { - Immediate::Scalar(r) => (r.to_bits(size)?, 0), - Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?), - Immediate::Uninit => panic!("we should never see uninit data here"), - }; - let res = match bin_op { - Eq => left == right, - Ne => left != right, - Lt => left < right, - Le => left <= right, - Gt => left > right, - Ge => left >= right, - _ => bug!(), - }; - (ImmTy::from_bool(res, *ecx.tcx), false) - } - - // Some more operations are possible with atomics. - // The return value always has the provenance of the *left* operand. - Add | Sub | BitOr | BitAnd | BitXor => { - throw_machine_stop_str!("pointer arithmetic is not handled") - } - - _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op), - }) - } - - fn expose_ptr( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _ptr: interpret::Pointer, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn init_frame_extra( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance>, - ) -> interpret::InterpResult< - 'tcx, - rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, - > { - unimplemented!() - } - - fn stack<'a>( - _ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] - { - // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants. - &[] - } - - fn stack_mut<'a>( - _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec< - rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, - > { - unimplemented!() - } -} diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index ca63f5550ae2e..370e930b74098 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -160,7 +160,7 @@ pub fn deduced_param_attrs<'tcx>( return &[]; } - // If the Freeze language item isn't present, then don't bother. + // If the Freeze lang item isn't present, then don't bother. if tcx.lang_items().freeze_trait().is_none() { return &[]; } diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 0d600f0f937de..9edb8bcee6e48 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use std::fmt::Debug; use super::simplify::simplify_cfg; @@ -11,6 +11,7 @@ use super::simplify::simplify_cfg; /// let y: Option<()>; /// match (x,y) { /// (Some(_), Some(_)) => {0}, +/// (None, None) => {2}, /// _ => {1} /// } /// ``` @@ -23,10 +24,10 @@ use super::simplify::simplify_cfg; /// if discriminant_x == discriminant_y { /// match x { /// Some(_) => 0, -/// _ => 1, // <---- -/// } // | Actually the same bb -/// } else { // | -/// 1 // <-------------- +/// None => 2, +/// } +/// } else { +/// 1 /// } /// ``` /// @@ -47,18 +48,18 @@ use super::simplify::simplify_cfg; /// | | | /// ================= | | | /// | BBU | <-| | | ============================ -/// |---------------| | \-------> | BBD | -/// |---------------| | | |--------------------------| -/// | unreachable | | | | _dl = discriminant(P) | -/// ================= | | |--------------------------| -/// | | | switchInt(_dl) | -/// ================= | | | d | ---> BBD.2 +/// |---------------| \-------> | BBD | +/// |---------------| | |--------------------------| +/// | unreachable | | | _dl = discriminant(P) | +/// ================= | |--------------------------| +/// | | switchInt(_dl) | +/// ================= | | d | ---> BBD.2 /// | BB9 | <--------------- | otherwise | /// |---------------| ============================ /// | ... | /// ================= /// ``` -/// Where the `otherwise` branch on `BB1` is permitted to either go to `BBU` or to `BB9`. In the +/// Where the `otherwise` branch on `BB1` is permitted to either go to `BBU`. In the /// code: /// - `BB1` is `parent` and `BBC, BBD` are children /// - `P` is `child_place` @@ -78,7 +79,7 @@ use super::simplify::simplify_cfg; /// |---------------------| | | switchInt(Q) | /// | switchInt(_t) | | | c | ---> BBC.2 /// | false | --------/ | d | ---> BBD.2 -/// | otherwise | ---------------- | otherwise | +/// | otherwise | /--------- | otherwise | /// ======================= | ============================ /// | /// ================= | @@ -87,16 +88,11 @@ use super::simplify::simplify_cfg; /// | ... | /// ================= /// ``` -/// -/// This is only correct for some `P`, since `P` is now computed outside the original `switchInt`. -/// The filter on which `P` are allowed (together with discussion of its correctness) is found in -/// `may_hoist`. pub struct EarlyOtherwiseBranch; impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // unsound: https://github.com/rust-lang/rust/issues/95162 - sess.mir_opt_level() >= 3 && sess.opts.unstable_opts.unsound_mir_opts + sess.mir_opt_level() >= 2 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { @@ -172,7 +168,8 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { }; (value, targets.target_for_value(value)) }); - let eq_targets = SwitchTargets::new(eq_new_targets, opt_data.destination); + // The otherwise either is the same target branch or an unreachable. + let eq_targets = SwitchTargets::new(eq_new_targets, parent_targets.otherwise()); // Create `bbEq` in example above let eq_switch = BasicBlockData::new(Some(Terminator { @@ -217,85 +214,6 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { } } -/// Returns true if computing the discriminant of `place` may be hoisted out of the branch -fn may_hoist<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, place: Place<'tcx>) -> bool { - // FIXME(JakobDegen): This is unsound. Someone could write code like this: - // ```rust - // let Q = val; - // if discriminant(P) == otherwise { - // let ptr = &mut Q as *mut _ as *mut u8; - // unsafe { *ptr = 10; } // Any invalid value for the type - // } - // - // match P { - // A => match Q { - // A => { - // // code - // } - // _ => { - // // don't use Q - // } - // } - // _ => { - // // don't use Q - // } - // }; - // ``` - // - // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an - // invalid value, which is UB. - // - // In order to fix this, we would either need to show that the discriminant computation of - // `place` is computed in all branches, including the `otherwise` branch, or we would need - // another analysis pass to determine that the place is fully initialized. It might even be best - // to have the hoisting be performed in a different pass and just do the CFG changing in this - // pass. - for (place, proj) in place.iter_projections() { - match proj { - // Dereferencing in the computation of `place` might cause issues from one of two - // categories. First, the referent might be invalid. We protect against this by - // dereferencing references only (not pointers). Second, the use of a reference may - // invalidate other references that are used later (for aliasing reasons). Consider - // where such an invalidated reference may appear: - // - In `Q`: Not possible since `Q` is used as the operand of a `SwitchInt` and so - // cannot contain referenced data. - // - In `BBU`: Not possible since that block contains only the `unreachable` terminator - // - In `BBC.2, BBD.2`: Not possible, since `discriminant(P)` was computed prior to - // reaching that block in the input to our transformation, and so any data - // invalidated by that computation could not have been used there. - // - In `BB9`: Not possible since control flow might have reached `BB9` via the - // `otherwise` branch in `BBC, BBD` in the input to our transformation, which would - // have invalidated the data when computing `discriminant(P)` - // So dereferencing here is correct. - ProjectionElem::Deref => match place.ty(body.local_decls(), tcx).ty.kind() { - ty::Ref(..) => {} - _ => return false, - }, - // Field projections are always valid - ProjectionElem::Field(..) => {} - // We cannot allow - // downcasts either, since the correctness of the downcast may depend on the parent - // branch being taken. An easy example of this is - // ``` - // Q = discriminant(_3) - // P = (_3 as Variant) - // ``` - // However, checking if the child and parent place are the same and only erroring then - // is not sufficient either, since the `discriminant(_3) == 1` (or whatever) check may - // be replaced by another optimization pass with any other condition that can be proven - // equivalent. - ProjectionElem::Downcast(..) => { - return false; - } - // We cannot allow indexing since the index may be out of bounds. - _ => { - return false; - } - } - } - true -} - #[derive(Debug)] struct OptimizationData<'tcx> { destination: BasicBlock, @@ -315,18 +233,40 @@ fn evaluate_candidate<'tcx>( return None; }; let parent_ty = parent_discr.ty(body.local_decls(), tcx); - let parent_dest = { - let poss = targets.otherwise(); - // If the fallthrough on the parent is trivially unreachable, we can let the - // children choose the destination - if bbs[poss].statements.len() == 0 - && bbs[poss].terminator().kind == TerminatorKind::Unreachable - { - None - } else { - Some(poss) - } - }; + if !bbs[targets.otherwise()].is_empty_unreachable() { + // Someone could write code like this: + // ```rust + // let Q = val; + // if discriminant(P) == otherwise { + // let ptr = &mut Q as *mut _ as *mut u8; + // // It may be difficult for us to effectively determine whether values are valid. + // // Invalid values can come from all sorts of corners. + // unsafe { *ptr = 10; } + // } + // + // match P { + // A => match Q { + // A => { + // // code + // } + // _ => { + // // don't use Q + // } + // } + // _ => { + // // don't use Q + // } + // }; + // ``` + // + // Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an + // invalid value, which is UB. + // In order to fix this, **we would either need to show that the discriminant computation of + // `place` is computed in all branches**. + // FIXME(#95162) For the moment, we adopt a conservative approach and + // consider only the `otherwise` branch has no statements and an unreachable terminator. + return None; + } let (_, child) = targets.iter().next()?; let child_terminator = &bbs[child].terminator(); let TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr } = @@ -344,13 +284,7 @@ fn evaluate_candidate<'tcx>( let (_, Rvalue::Discriminant(child_place)) = &**boxed else { return None; }; - let destination = parent_dest.unwrap_or(child_targets.otherwise()); - - // Verify that the optimization is legal in general - // We can hoist evaluating the child discriminant out of the branch - if !may_hoist(tcx, body, *child_place) { - return None; - } + let destination = child_targets.otherwise(); // Verify that the optimization is legal for each branch for (value, child) in targets.iter() { @@ -411,5 +345,5 @@ fn verify_candidate_branch<'tcx>( if let Some(_) = iter.next() { return false; } - return true; + true } diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index 96943435bab89..318674f24e7ab 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -3,7 +3,6 @@ //! Box is not actually a pointer so it is incorrect to dereference it directly. use rustc_hir::def_id::DefId; -use rustc_index::Idx; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; @@ -32,9 +31,9 @@ pub fn build_projection<'tcx>( ptr_ty: Ty<'tcx>, ) -> [PlaceElem<'tcx>; 3] { [ - PlaceElem::Field(FieldIdx::new(0), unique_ty), - PlaceElem::Field(FieldIdx::new(0), nonnull_ty), - PlaceElem::Field(FieldIdx::new(0), ptr_ty), + PlaceElem::Field(FieldIdx::ZERO, unique_ty), + PlaceElem::Field(FieldIdx::ZERO, nonnull_ty), + PlaceElem::Field(FieldIdx::ZERO, ptr_ty), ] } @@ -91,15 +90,14 @@ pub struct ElaborateBoxDerefs; impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { if let Some(def_id) = tcx.lang_items().owned_box() { - let unique_did = - tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::from_u32(0)].did; + let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[FieldIdx::ZERO].did; let Some(nonnull_def) = tcx.type_of(unique_did).instantiate_identity().ty_adt_def() else { span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique") }; - let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::from_u32(0)].did; + let nonnull_did = nonnull_def.non_enum_variant().fields[FieldIdx::ZERO].did; let patch = MirPatch::new(body); diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 9297bc51fad99..0634e321ea303 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -1,11 +1,6 @@ -use std::borrow::Cow; - -use rustc_errors::{ - codes::*, Applicability, Diag, DiagArgValue, DiagCtxt, DiagMessage, Diagnostic, - EmissionGuarantee, Level, LintDiagnostic, -}; +use rustc_errors::{codes::*, Diag, DiagMessage, LintDiagnostic}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; -use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails}; +use rustc_middle::mir::AssertKind; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; @@ -42,168 +37,6 @@ pub(crate) struct UnalignedPackedRef { pub span: Span, } -#[derive(LintDiagnostic)] -#[diag(mir_transform_unused_unsafe)] -pub(crate) struct UnusedUnsafe { - #[label(mir_transform_unused_unsafe)] - pub span: Span, - #[label] - pub nested_parent: Option, -} - -pub(crate) struct RequiresUnsafe { - pub span: Span, - pub details: RequiresUnsafeDetail, - pub enclosing: Option, - pub op_in_unsafe_fn_allowed: bool, -} - -// The primary message for this diagnostic should be '{$label} is unsafe and...', -// so we need to eagerly translate the label here, which isn't supported by the derive API -// We could also exhaustively list out the primary messages for all unsafe violations, -// but this would result in a lot of duplication. -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for RequiresUnsafe { - #[track_caller] - fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { - let mut diag = Diag::new(dcx, level, fluent::mir_transform_requires_unsafe); - diag.code(E0133); - diag.span(self.span); - diag.span_label(self.span, self.details.label()); - let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter()); - diag.arg("details", desc); - diag.arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed); - self.details.add_subdiagnostics(&mut diag); - if let Some(sp) = self.enclosing { - diag.span_label(sp, fluent::mir_transform_not_inherited); - } - diag - } -} - -#[derive(Clone)] -pub(crate) struct RequiresUnsafeDetail { - pub span: Span, - pub violation: UnsafetyViolationDetails, -} - -impl RequiresUnsafeDetail { - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] - fn add_subdiagnostics(&self, diag: &mut Diag<'_, G>) { - use UnsafetyViolationDetails::*; - match self.violation { - CallToUnsafeFunction => { - diag.note(fluent::mir_transform_call_to_unsafe_note); - } - UseOfInlineAssembly => { - diag.note(fluent::mir_transform_use_of_asm_note); - } - InitializingTypeWith => { - diag.note(fluent::mir_transform_initializing_valid_range_note); - } - CastOfPointerToInt => { - diag.note(fluent::mir_transform_const_ptr2int_note); - } - UseOfMutableStatic => { - diag.note(fluent::mir_transform_use_of_static_mut_note); - } - UseOfExternStatic => { - diag.note(fluent::mir_transform_use_of_extern_static_note); - } - DerefOfRawPointer => { - diag.note(fluent::mir_transform_deref_ptr_note); - } - AccessToUnionField => { - diag.note(fluent::mir_transform_union_access_note); - } - MutationOfLayoutConstrainedField => { - diag.note(fluent::mir_transform_mutation_layout_constrained_note); - } - BorrowOfLayoutConstrainedField => { - diag.note(fluent::mir_transform_mutation_layout_constrained_borrow_note); - } - CallToFunctionWith { ref missing, ref build_enabled } => { - diag.help(fluent::mir_transform_target_feature_call_help); - diag.arg( - "missing_target_features", - DiagArgValue::StrListSepByAnd( - missing.iter().map(|feature| Cow::from(feature.to_string())).collect(), - ), - ); - diag.arg("missing_target_features_count", missing.len()); - if !build_enabled.is_empty() { - diag.note(fluent::mir_transform_target_feature_call_note); - diag.arg( - "build_target_features", - DiagArgValue::StrListSepByAnd( - build_enabled - .iter() - .map(|feature| Cow::from(feature.to_string())) - .collect(), - ), - ); - diag.arg("build_target_features_count", build_enabled.len()); - } - } - } - } - - fn label(&self) -> DiagMessage { - use UnsafetyViolationDetails::*; - match self.violation { - CallToUnsafeFunction => fluent::mir_transform_call_to_unsafe_label, - UseOfInlineAssembly => fluent::mir_transform_use_of_asm_label, - InitializingTypeWith => fluent::mir_transform_initializing_valid_range_label, - CastOfPointerToInt => fluent::mir_transform_const_ptr2int_label, - UseOfMutableStatic => fluent::mir_transform_use_of_static_mut_label, - UseOfExternStatic => fluent::mir_transform_use_of_extern_static_label, - DerefOfRawPointer => fluent::mir_transform_deref_ptr_label, - AccessToUnionField => fluent::mir_transform_union_access_label, - MutationOfLayoutConstrainedField => { - fluent::mir_transform_mutation_layout_constrained_label - } - BorrowOfLayoutConstrainedField => { - fluent::mir_transform_mutation_layout_constrained_borrow_label - } - CallToFunctionWith { .. } => fluent::mir_transform_target_feature_call_label, - } - } -} - -pub(crate) struct UnsafeOpInUnsafeFn { - pub details: RequiresUnsafeDetail, - - /// These spans point to: - /// 1. the start of the function body - /// 2. the end of the function body - /// 3. the function signature - pub suggest_unsafe_block: Option<(Span, Span, Span)>, -} - -impl<'a> LintDiagnostic<'a, ()> for UnsafeOpInUnsafeFn { - #[track_caller] - fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { - let desc = diag.dcx.eagerly_translate_to_string(self.details.label(), [].into_iter()); - diag.arg("details", desc); - diag.span_label(self.details.span, self.details.label()); - self.details.add_subdiagnostics(diag); - - if let Some((start, end, fn_sig)) = self.suggest_unsafe_block { - diag.span_note(fn_sig, fluent::mir_transform_note); - diag.tool_only_multipart_suggestion( - fluent::mir_transform_suggestion, - vec![(start, " unsafe {".into()), (end, "}".into())], - Applicability::MaybeIncorrect, - ); - } - } - - fn msg(&self) -> DiagMessage { - fluent::mir_transform_unsafe_op_in_unsafe_fn - } -} - pub(crate) struct AssertLint

{ pub span: Span, pub assert_kind: AssertKind

::Metadata; +} +impl AggregateRawPtr<*mut T> for *mut P { + type Metadata =

::Metadata; } +// Some functions are defined here because they accidentally got made +// available in this module on stable. See . +// (`transmute` also falls into this category, but it cannot be wrapped due to the +// check that `T` and `U` have the same size.) + /// Copies `count * size_of::()` bytes from `src` to `dst`. The source /// and destination must *not* overlap. /// @@ -2966,7 +2905,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us pub fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -2977,9 +2916,9 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us align: usize = align_of::(), count: usize = count, ) => - is_aligned_and_not_null(src, align) - && is_aligned_and_not_null(dst, align) - && is_nonoverlapping(src, dst, size, count) + ub_checks::is_aligned_and_not_null(src, align) + && ub_checks::is_aligned_and_not_null(dst, align) + && ub_checks::is_nonoverlapping(src, dst, size, count) ); // SAFETY: the safety contract for `copy_nonoverlapping` must be @@ -3070,7 +3009,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -3079,8 +3018,8 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { dst: *mut () = dst as *mut (), align: usize = align_of::(), ) => - is_aligned_and_not_null(src, align) - && is_aligned_and_not_null(dst, align) + ub_checks::is_aligned_and_not_null(src, align) + && ub_checks::is_aligned_and_not_null(dst, align) ); copy(src, dst, count) } @@ -3151,13 +3090,13 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write_bytes requires that the destination pointer is aligned and non-null", ( addr: *const () = dst as *const (), align: usize = align_of::(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); write_bytes(dst, val, count) } @@ -3182,9 +3121,5 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize const fn compiletime(_ptr: *const (), _align: usize) {} - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: the extra behavior at runtime is for UB checks only. - unsafe { - const_eval_select((ptr, align), compiletime, runtime); - } + const_eval_select((ptr, align), compiletime, runtime); } diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 2eaca3b6fe4d5..ceea67901294a 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -2,11 +2,7 @@ //! //! In this module, a "vector" is any `repr(simd)` type. -// Temporary macro while we switch the ABI from "platform-intrinsics" to "intrinsics". -#[rustfmt::skip] -macro_rules! declare_intrinsics { -($abi:tt) => { -extern $abi { +extern "rust-intrinsic" { /// Insert an element into a vector, returning the updated vector. /// /// `T` must be a vector with element type `U`. @@ -474,7 +470,7 @@ extern $abi { /// No matter whether the output is an array or an unsigned integer, it is treated as a single /// contiguous list of bits. The bitmask is always packed on the least-significant side of the /// output, and padded with 0s in the most-significant bits. The order of the bits depends on - /// endianess: + /// endianness: /// /// * On little endian, the least significant bit corresponds to the first vector element. /// * On big endian, the least significant bit corresponds to the last vector element. @@ -545,7 +541,7 @@ extern $abi { /// /// `U` must be a vector of `usize` with the same length as `T`. #[rustc_nounwind] - pub fn simd_expose_addr(ptr: T) -> U; + pub fn simd_expose_provenance(ptr: T) -> U; /// Create a vector of pointers from a vector of addresses. /// @@ -553,7 +549,7 @@ extern $abi { /// /// `U` must be a vector of pointers, with the same length as `T`. #[rustc_nounwind] - pub fn simd_from_exposed_addr(addr: T) -> U; + pub fn simd_with_exposed_provenance(addr: T) -> U; /// Swap bytes of each element. /// @@ -659,10 +655,3 @@ extern $abi { #[rustc_nounwind] pub fn simd_flog(a: T) -> T; } -} -} - -#[cfg(bootstrap)] -declare_intrinsics!("platform-intrinsic"); -#[cfg(not(bootstrap))] -declare_intrinsics!("rust-intrinsic"); diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 778d38b153764..81371708b51e9 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/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 8c68ea114dbd2..8f1744fc5fbb7 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -34,9 +34,22 @@ where /// Returns an iterator over the remaining elements of the original iterator /// that are not going to be returned by this iterator. The returned /// iterator will yield at most `N-1` elements. + /// + /// # Example + /// ``` + /// # // Also serves as a regression test for https://github.com/rust-lang/rust/issues/123333 + /// # #![feature(iter_array_chunks)] + /// let x = [1,2,3,4,5].into_iter().array_chunks::<2>(); + /// let mut rem = x.into_remainder().unwrap(); + /// assert_eq!(rem.next(), Some(5)); + /// assert_eq!(rem.next(), None); + /// ``` #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] #[inline] - pub fn into_remainder(self) -> Option> { + pub fn into_remainder(mut self) -> Option> { + if self.remainder.is_none() { + while let Some(_) = self.next() {} + } self.remainder } } diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs index ef46040f0a703..7adbabf69e490 100644 --- a/library/core/src/iter/adapters/enumerate.rs +++ b/library/core/src/iter/adapters/enumerate.rs @@ -15,6 +15,7 @@ use crate::ops::Try; #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Enumerate")] pub struct Enumerate { iter: I, count: usize, diff --git a/library/core/src/iter/adapters/step_by.rs b/library/core/src/iter/adapters/step_by.rs index 54ed4c952fb8b..a5cf069d407c0 100644 --- a/library/core/src/iter/adapters/step_by.rs +++ b/library/core/src/iter/adapters/step_by.rs @@ -1,7 +1,7 @@ -use crate::convert::TryFrom; use crate::{ intrinsics, iter::{from_fn, TrustedLen, TrustedRandomAccess}, + num::NonZeroUsize, ops::{Range, Try}, }; @@ -23,7 +23,11 @@ pub struct StepBy { /// Additionally this type-dependent preprocessing means specialized implementations /// cannot be used interchangeably. iter: I, - step: usize, + /// This field is `step - 1`, aka the correct amount to pass to `nth` when iterating. + /// It MUST NOT be `usize::MAX`, as `unsafe` code depends on being able to add one + /// without the risk of overflow. (This is important so that length calculations + /// don't need to check for division-by-zero, for example.) + step_minus_one: usize, first_take: bool, } @@ -32,7 +36,16 @@ impl StepBy { pub(in crate::iter) fn new(iter: I, step: usize) -> StepBy { assert!(step != 0); let iter = >::setup(iter, step); - StepBy { iter, step: step - 1, first_take: true } + StepBy { iter, step_minus_one: step - 1, first_take: true } + } + + /// The `step` that was originally passed to `Iterator::step_by(step)`, + /// aka `self.step_minus_one + 1`. + #[inline] + fn original_step(&self) -> NonZeroUsize { + // SAFETY: By type invariant, `step_minus_one` cannot be `MAX`, which + // means the addition cannot overflow and the result cannot be zero. + unsafe { NonZeroUsize::new_unchecked(intrinsics::unchecked_add(self.step_minus_one, 1)) } } } @@ -82,8 +95,8 @@ where // The zero-based index starting from the end of the iterator of the // last element. Used in the `DoubleEndedIterator` implementation. fn next_back_index(&self) -> usize { - let rem = self.iter.len() % (self.step + 1); - if self.first_take { if rem == 0 { self.step } else { rem - 1 } } else { rem } + let rem = self.iter.len() % self.original_step(); + if self.first_take { if rem == 0 { self.step_minus_one } else { rem - 1 } } else { rem } } } @@ -210,7 +223,7 @@ unsafe impl StepByImpl for StepBy { #[inline] default fn spec_next(&mut self) -> Option { - let step_size = if self.first_take { 0 } else { self.step }; + let step_size = if self.first_take { 0 } else { self.step_minus_one }; self.first_take = false; self.iter.nth(step_size) } @@ -218,22 +231,22 @@ unsafe impl StepByImpl for StepBy { #[inline] default fn spec_size_hint(&self) -> (usize, Option) { #[inline] - fn first_size(step: usize) -> impl Fn(usize) -> usize { - move |n| if n == 0 { 0 } else { 1 + (n - 1) / (step + 1) } + fn first_size(step: NonZeroUsize) -> impl Fn(usize) -> usize { + move |n| if n == 0 { 0 } else { 1 + (n - 1) / step } } #[inline] - fn other_size(step: usize) -> impl Fn(usize) -> usize { - move |n| n / (step + 1) + fn other_size(step: NonZeroUsize) -> impl Fn(usize) -> usize { + move |n| n / step } let (low, high) = self.iter.size_hint(); if self.first_take { - let f = first_size(self.step); + let f = first_size(self.original_step()); (f(low), high.map(f)) } else { - let f = other_size(self.step); + let f = other_size(self.original_step()); (f(low), high.map(f)) } } @@ -248,10 +261,9 @@ unsafe impl StepByImpl for StepBy { } n -= 1; } - // n and self.step are indices, we need to add 1 to get the amount of elements + // n and self.step_minus_one are indices, we need to add 1 to get the amount of elements // When calling `.nth`, we need to subtract 1 again to convert back to an index - // step + 1 can't overflow because `.step_by` sets `self.step` to `step - 1` - let mut step = self.step + 1; + let mut step = self.original_step().get(); // n + 1 could overflow // thus, if n is usize::MAX, instead of adding one, we call .nth(step) if n == usize::MAX { @@ -289,8 +301,11 @@ unsafe impl StepByImpl for StepBy { R: Try, { #[inline] - fn nth(iter: &mut I, step: usize) -> impl FnMut() -> Option + '_ { - move || iter.nth(step) + fn nth( + iter: &mut I, + step_minus_one: usize, + ) -> impl FnMut() -> Option + '_ { + move || iter.nth(step_minus_one) } if self.first_take { @@ -300,7 +315,7 @@ unsafe impl StepByImpl for StepBy { Some(x) => acc = f(acc, x)?, } } - from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f) + from_fn(nth(&mut self.iter, self.step_minus_one)).try_fold(acc, f) } default fn spec_fold(mut self, mut acc: Acc, mut f: F) -> Acc @@ -308,8 +323,11 @@ unsafe impl StepByImpl for StepBy { F: FnMut(Acc, Self::Item) -> Acc, { #[inline] - fn nth(iter: &mut I, step: usize) -> impl FnMut() -> Option + '_ { - move || iter.nth(step) + fn nth( + iter: &mut I, + step_minus_one: usize, + ) -> impl FnMut() -> Option + '_ { + move || iter.nth(step_minus_one) } if self.first_take { @@ -319,7 +337,7 @@ unsafe impl StepByImpl for StepBy { Some(x) => acc = f(acc, x), } } - from_fn(nth(&mut self.iter, self.step)).fold(acc, f) + from_fn(nth(&mut self.iter, self.step_minus_one)).fold(acc, f) } } @@ -337,7 +355,7 @@ unsafe impl StepByBackImpl for St // is out of bounds because the length of `self.iter` does not exceed // `usize::MAX` (because `I: ExactSizeIterator`) and `nth_back` is // zero-indexed - let n = n.saturating_mul(self.step + 1).saturating_add(self.next_back_index()); + let n = n.saturating_mul(self.original_step().get()).saturating_add(self.next_back_index()); self.iter.nth_back(n) } @@ -349,16 +367,16 @@ unsafe impl StepByBackImpl for St #[inline] fn nth_back( iter: &mut I, - step: usize, + step_minus_one: usize, ) -> impl FnMut() -> Option + '_ { - move || iter.nth_back(step) + move || iter.nth_back(step_minus_one) } match self.next_back() { None => try { init }, Some(x) => { let acc = f(init, x)?; - from_fn(nth_back(&mut self.iter, self.step)).try_fold(acc, f) + from_fn(nth_back(&mut self.iter, self.step_minus_one)).try_fold(acc, f) } } } @@ -372,16 +390,16 @@ unsafe impl StepByBackImpl for St #[inline] fn nth_back( iter: &mut I, - step: usize, + step_minus_one: usize, ) -> impl FnMut() -> Option + '_ { - move || iter.nth_back(step) + move || iter.nth_back(step_minus_one) } match self.next_back() { None => init, Some(x) => { let acc = f(init, x); - from_fn(nth_back(&mut self.iter, self.step)).fold(acc, f) + from_fn(nth_back(&mut self.iter, self.step_minus_one)).fold(acc, f) } } } @@ -425,8 +443,7 @@ macro_rules! spec_int_ranges { fn spec_next(&mut self) -> Option<$t> { // if a step size larger than the type has been specified fall back to // t::MAX, in which case remaining will be at most 1. - // The `+ 1` can't overflow since the constructor substracted 1 from the original value. - let step = <$t>::try_from(self.step + 1).unwrap_or(<$t>::MAX); + let step = <$t>::try_from(self.original_step().get()).unwrap_or(<$t>::MAX); let remaining = self.iter.end; if remaining > 0 { let val = self.iter.start; @@ -475,7 +492,7 @@ macro_rules! spec_int_ranges { { // if a step size larger than the type has been specified fall back to // t::MAX, in which case remaining will be at most 1. - let step = <$t>::try_from(self.step + 1).unwrap_or(<$t>::MAX); + let step = <$t>::try_from(self.original_step().get()).unwrap_or(<$t>::MAX); let remaining = self.iter.end; let mut acc = init; let mut val = self.iter.start; @@ -498,10 +515,8 @@ macro_rules! spec_int_ranges_r { unsafe impl StepByBackImpl> for StepBy> { #[inline] - fn spec_next_back(&mut self) -> Option - where Range<$t>: DoubleEndedIterator + ExactSizeIterator, - { - let step = (self.step + 1) as $t; + fn spec_next_back(&mut self) -> Option { + let step = self.original_step().get() as $t; let remaining = self.iter.end; if remaining > 0 { let start = self.iter.start; @@ -516,9 +531,7 @@ macro_rules! spec_int_ranges_r { // We have to repeat them here so that the specialization overrides the StepByImplBack defaults #[inline] - fn spec_nth_back(&mut self, n: usize) -> Option - where Self: DoubleEndedIterator, - { + fn spec_nth_back(&mut self, n: usize) -> Option { if self.advance_back_by(n).is_err() { return None; } @@ -527,10 +540,9 @@ macro_rules! spec_int_ranges_r { #[inline] fn spec_try_rfold(&mut self, init: Acc, mut f: F) -> R - where - Self: DoubleEndedIterator, - F: FnMut(Acc, Self::Item) -> R, - R: Try + where + F: FnMut(Acc, Self::Item) -> R, + R: Try { let mut accum = init; while let Some(x) = self.next_back() { @@ -541,9 +553,8 @@ macro_rules! spec_int_ranges_r { #[inline] fn spec_rfold(mut self, init: Acc, mut f: F) -> Acc - where - Self: DoubleEndedIterator, - F: FnMut(Acc, Self::Item) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc { let mut accum = init; while let Some(x) = self.next_back() { diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 055ead117ea24..644a169294396 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -1,5 +1,4 @@ use crate::ascii::Char as AsciiChar; -use crate::convert::TryFrom; use crate::mem; use crate::net::{Ipv4Addr, Ipv6Addr}; use crate::num::NonZero; @@ -1157,11 +1156,11 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { let is_iterating = self.start < self.end; Some(if is_iterating { // SAFETY: just checked precondition - let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; + let n = unsafe { Step::forward_unchecked(self.start, 1) }; mem::replace(&mut self.start, n) } else { self.exhausted = true; - self.start.clone() + self.start }) } @@ -1180,7 +1179,7 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { while self.start < self.end { // SAFETY: just checked precondition - let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; + let n = unsafe { Step::forward_unchecked(self.start, 1) }; let n = mem::replace(&mut self.start, n); accum = f(accum, n)?; } @@ -1188,7 +1187,7 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { self.exhausted = true; if self.start == self.end { - accum = f(accum, self.start.clone())?; + accum = f(accum, self.start)?; } try { accum } @@ -1202,11 +1201,11 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { let is_iterating = self.start < self.end; Some(if is_iterating { // SAFETY: just checked precondition - let n = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; + let n = unsafe { Step::backward_unchecked(self.end, 1) }; mem::replace(&mut self.end, n) } else { self.exhausted = true; - self.end.clone() + self.end }) } @@ -1225,7 +1224,7 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { while self.start < self.end { // SAFETY: just checked precondition - let n = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; + let n = unsafe { Step::backward_unchecked(self.end, 1) }; let n = mem::replace(&mut self.end, n); accum = f(accum, n)?; } @@ -1233,7 +1232,7 @@ impl RangeInclusiveIteratorImpl for ops::RangeInclusive { self.exhausted = true; if self.start == self.end { - accum = f(accum, self.start.clone())?; + accum = f(accum, self.start)?; } try { accum } diff --git a/library/core/src/iter/sources/from_coroutine.rs b/library/core/src/iter/sources/from_coroutine.rs index bf413b24d4177..710ba504ded64 100644 --- a/library/core/src/iter/sources/from_coroutine.rs +++ b/library/core/src/iter/sources/from_coroutine.rs @@ -14,7 +14,7 @@ use crate::pin::Pin; /// #![feature(coroutines)] /// #![feature(iter_from_coroutine)] /// -/// let it = std::iter::from_coroutine(|| { +/// let it = std::iter::from_coroutine(#[coroutine] || { /// yield 1; /// yield 2; /// yield 3; diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index d89801bce2b6d..563781230c023 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -150,6 +150,39 @@ pub trait FromIterator: Sized { fn from_iter>(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::>()?; +/// +/// assert_eq!(numbers, [1, 2, 123, 4]); +/// assert_eq!(lengths, [1, 1, 3, 1]); +/// # Ok(()) } +/// ``` +#[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] +impl FromIterator<(AE, BE)> for (A, B) +where + A: Default + Extend, + B: Default + Extend, +{ + fn from_iter>(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/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 35092ec51cdd9..3b12678572841 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -118,7 +118,8 @@ pub trait DoubleEndedIterator: Iterator { /// Basic usage: /// /// ``` - /// #![feature(generic_nonzero, iter_advance_by)] + /// #![feature(iter_advance_by)] + /// /// use std::num::NonZero; /// /// let a = [3, 4, 5, 6]; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index e1904ed220cb4..cee99e28b5a97 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -8,11 +8,11 @@ use super::super::ByRefSized; use super::super::TrustedRandomAccessNoCoerce; use super::super::{ArrayChunks, Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; -use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; use super::super::{ Inspect, Map, MapWhile, MapWindows, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, }; +use super::super::{Intersperse, IntersperseWith, Product, Sum, Zip}; fn _assert_is_object_safe(_: &dyn Iterator) {} @@ -288,7 +288,8 @@ pub trait Iterator { /// # Examples /// /// ``` - /// #![feature(generic_nonzero, iter_advance_by)] + /// #![feature(iter_advance_by)] + /// /// use std::num::NonZero; /// /// let a = [1, 2, 3, 4]; @@ -973,6 +974,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_do_not_const_check] + #[cfg_attr(not(test), rustc_diagnostic_item = "enumerate_method")] fn enumerate(self) -> Enumerate where Self: Sized, @@ -1572,7 +1574,7 @@ pub trait Iterator { /// /// The returned iterator implements [`FusedIterator`], because once `self` /// returns `None`, even if it returns a `Some(T)` again in the next iterations, - /// we cannot put it into a contigious array buffer, and thus the returned iterator + /// we cannot put it into a contiguous array buffer, and thus the returned iterator /// should be fused. /// /// [`slice::windows()`]: slice::windows @@ -2939,7 +2941,8 @@ pub trait Iterator { /// This also supports other types which implement [`Try`], not just [`Result`]. /// /// ``` - /// #![feature(generic_nonzero, try_find)] + /// #![feature(try_find)] + /// /// use std::num::NonZero; /// /// let a = [3, 5, 7, 4, 9, 0, 11u32]; diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs index 8bdbca120d7f9..2e756a6dd67c4 100644 --- a/library/core/src/iter/traits/marker.rs +++ b/library/core/src/iter/traits/marker.rs @@ -28,6 +28,7 @@ pub unsafe trait TrustedFused {} #[rustc_unsafe_specialization_marker] // FIXME: this should be a #[marker] and have another blanket impl for T: TrustedFused // but that ICEs iter::Fuse specializations. +#[lang = "fused_iterator"] pub trait FusedIterator: Iterator {} #[stable(feature = "fused", since = "1.26.0")] diff --git a/library/core/src/lib.miri.rs b/library/core/src/lib.miri.rs new file mode 100644 index 0000000000000..5c1027f20ba07 --- /dev/null +++ b/library/core/src/lib.miri.rs @@ -0,0 +1,4 @@ +//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about. +#![no_std] +extern crate core as realcore; +pub use realcore::*; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 9b786feba8988..5dcef63895441 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -57,10 +57,7 @@ // // This cfg won't affect doc tests. #![cfg(not(test))] -// To run core tests without x.py without ending up with two copies of core, Miri needs to be -// able to "empty" this crate. See . -// rustc itself never sets the feature, so this line has no effect there. -#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] +// #![stable(feature = "core", since = "1.6.0")] #![doc( html_playground_url = "https://play.rust-lang.org/", @@ -71,7 +68,6 @@ #![doc(rust_logo)] #![doc(cfg_hide( not(test), - any(not(feature = "miri-test-libstd"), test, doctest), no_fp_fmt_parse, target_pointer_width = "16", target_pointer_width = "32", @@ -94,6 +90,7 @@ ))] #![no_core] #![rustc_coherence_is_core] +#![rustc_preserve_ub_checks] // // Lints: #![deny(rust_2021_incompatible_or_patterns)] @@ -112,6 +109,8 @@ // // Library features: // tidy-alphabetical-start +#![feature(array_ptr_get)] +#![feature(asm_experimental_arch)] #![feature(char_indices_offset)] #![feature(const_align_of_val)] #![feature(const_align_of_val_raw)] @@ -122,7 +121,6 @@ #![feature(const_array_into_iter_constructors)] #![feature(const_bigint_helper_methods)] #![feature(const_black_box)] -#![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_char_from_u32_unchecked)] #![feature(const_eval_select)] @@ -134,7 +132,7 @@ #![feature(const_heap)] #![feature(const_hint_assert_unchecked)] #![feature(const_index_range_slice_index)] -#![feature(const_int_unchecked_arith)] +#![feature(const_int_from_str)] #![feature(const_intrinsic_copy)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] @@ -160,7 +158,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)] @@ -168,6 +165,8 @@ #![feature(const_try)] #![feature(const_type_id)] #![feature(const_type_name)] +#![feature(const_typed_swap)] +#![feature(const_ub_checks)] #![feature(const_unicode_case_lookup)] #![feature(const_unsafecell_get_mut)] #![feature(const_waker)] @@ -178,8 +177,8 @@ #![feature(ip_bits)] #![feature(is_ascii_octdigit)] #![feature(isqrt)] +#![feature(link_cfg)] #![feature(maybe_uninit_uninit_array)] -#![feature(non_null_convenience)] #![feature(offset_of_enum)] #![feature(offset_of_nested)] #![feature(panic_internals)] @@ -187,13 +186,12 @@ #![feature(ptr_metadata)] #![feature(set_ptr_value)] #![feature(slice_ptr_get)] -#![feature(slice_split_at_unchecked)] #![feature(split_at_checked)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] #![feature(str_split_remainder)] #![feature(strict_provenance)] -#![feature(unchecked_math)] +#![feature(ub_checks)] #![feature(unchecked_shifts)] #![feature(utf16_extra)] #![feature(utf16_extra_const)] @@ -202,17 +200,11 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(diagnostic_namespace))] -#![cfg_attr(bootstrap, feature(exhaustive_patterns))] -#![cfg_attr(bootstrap, feature(platform_intrinsics))] -#![cfg_attr(not(bootstrap), feature(freeze_impls))] -#![cfg_attr(not(bootstrap), feature(min_exhaustive_patterns))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_const)] -#![feature(associated_type_bounds)] #![feature(auto_traits)] #![feature(c_unwind)] #![feature(cfg_sanitize)] @@ -232,16 +224,19 @@ #![feature(doc_notable_trait)] #![feature(effects)] #![feature(extern_types)] +#![feature(f128)] +#![feature(f16)] +#![feature(freeze_impls)] #![feature(fundamental)] #![feature(generic_arg_infer)] #![feature(if_let_guard)] -#![feature(inline_const)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] #![feature(lang_items)] #![feature(let_chains)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] +#![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] #![feature(must_not_suspend)] @@ -271,6 +266,7 @@ #![feature(arm_target_feature)] #![feature(avx512_target_feature)] #![feature(hexagon_target_feature)] +#![feature(loongarch_target_feature)] #![feature(mips_target_feature)] #![feature(powerpc_target_feature)] #![feature(riscv_target_feature)] @@ -286,7 +282,7 @@ extern crate self as core; #[prelude_import] #[allow(unused)] -use prelude::v1::*; +use prelude::rust_2021::*; #[cfg(not(test))] // See #65860 #[macro_use] @@ -350,6 +346,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"] @@ -368,6 +368,8 @@ pub mod hint; pub mod intrinsics; pub mod mem; pub mod ptr; +#[unstable(feature = "ub_checks", issue = "none")] +pub mod ub_checks; /* Core language traits */ @@ -398,6 +400,8 @@ pub mod net; pub mod option; pub mod panic; pub mod panicking; +#[unstable(feature = "core_pattern_types", issue = "none")] +pub mod pat; pub mod pin; pub mod result; pub mod sync; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 0ee7e190e3d45..2ddedfa37fe27 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1053,7 +1053,8 @@ pub(crate) mod builtin { /// /// If the environment variable is not defined, then a compilation error /// will be emitted. To not emit a compile error, use the [`option_env!`] - /// macro instead. + /// macro instead. A compilation error will also be emitted if the + /// environment variable is not a vaild Unicode string. /// /// # Examples /// @@ -1704,30 +1705,50 @@ pub(crate) mod builtin { } /// Unstable placeholder for type ascription. - #[rustc_builtin_macro] + #[allow_internal_unstable(builtin_syntax)] #[unstable( feature = "type_ascription", issue = "23416", reason = "placeholder syntax for type ascription" )] + #[rustfmt::skip] pub macro type_ascribe($expr:expr, $ty:ty) { - /* compiler built-in */ + builtin # type_ascribe($expr, $ty) + } + + /// Unstable placeholder for deref patterns. + #[allow_internal_unstable(builtin_syntax)] + #[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" + )] + pub macro deref($pat:pat) { + builtin # deref($pat) } - /// Unstable implementation detail of the `rustc` compiler, do not use. + /// Derive macro for `rustc-serialize`. Should not be used in new code. #[rustc_builtin_macro] - #[stable(feature = "rust1", since = "1.0.0")] - #[allow_internal_unstable(core_intrinsics, libstd_sys_internals, rt)] + #[unstable( + feature = "rustc_encodable_decodable", + issue = "none", + soft, + reason = "derive macro for `rustc-serialize`; should not be used in new code" + )] #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. pub macro RustcDecodable($item:item) { /* compiler built-in */ } - /// Unstable implementation detail of the `rustc` compiler, do not use. + /// Derive macro for `rustc-serialize`. Should not be used in new code. #[rustc_builtin_macro] - #[stable(feature = "rust1", since = "1.0.0")] - #[allow_internal_unstable(core_intrinsics, rt)] + #[unstable( + feature = "rustc_encodable_decodable", + issue = "none", + soft, + reason = "derive macro for `rustc-serialize`; should not be used in new code" + )] #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. pub macro RustcEncodable($item:item) { diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index a56a2578c2241..1d073a6d649b8 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -422,7 +422,7 @@ 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, @@ -817,6 +817,13 @@ pub trait DiscriminantKind { /// This can be used to declare that a constant with a generic type /// will not contain interior mutability, and subsequently allow /// placing the constant behind references. +/// +/// # Safety +/// +/// This trait is a core part of the language, it is just expressed as a trait in libcore for +/// convenience. Do *not* implement it for other types. +// FIXME: Eventually this trait should become `#[rustc_deny_explicit_impl]`. +// That requires porting the impls below to native internal impls. #[lang = "freeze"] #[unstable(feature = "freeze", issue = "121675")] pub unsafe auto trait Freeze {} diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index e247b9ed83df5..9054ade2d7968 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -726,63 +726,9 @@ pub unsafe fn uninitialized() -> T { #[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[rustc_diagnostic_item = "mem_swap"] pub const fn swap(x: &mut T, y: &mut T) { - // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary - // reinterpretation of values as (chunkable) byte arrays, and the loop in the - // block optimization in `swap_slice` is hard to rewrite back - // into the (unoptimized) direct swapping implementation, so we disable it. - #[cfg(not(any(target_arch = "spirv")))] - { - // For types that are larger multiples of their alignment, the simple way - // tends to copy the whole thing to stack rather than doing it one part - // at a time, so instead treat them as one-element slices and piggy-back - // the slice optimizations that will split up the swaps. - if const { size_of::() / align_of::() > 2 } { - // SAFETY: exclusive references always point to one non-overlapping - // element and are non-null and properly aligned. - return unsafe { ptr::swap_nonoverlapping(x, y, 1) }; - } - } - - // If a scalar consists of just a small number of alignment units, let - // the codegen just swap those pieces directly, as it's likely just a - // few instructions and anything else is probably overcomplicated. - // - // Most importantly, this covers primitives and simd types that tend to - // have size=align where doing anything else can be a pessimization. - // (This will also be used for ZSTs, though any solution works for them.) - swap_simple(x, y); -} - -/// Same as [`swap`] semantically, but always uses the simple implementation. -/// -/// Used elsewhere in `mem` and `ptr` at the bottom layer of calls. -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] -#[inline] -pub(crate) const fn swap_simple(x: &mut T, y: &mut T) { - // We arrange for this to typically be called with small types, - // so this reads-and-writes approach is actually better than using - // copy_nonoverlapping as it easily puts things in LLVM registers - // directly and doesn't end up inlining allocas. - // And LLVM actually optimizes it to 3×memcpy if called with - // a type larger than it's willing to keep in a register. - // Having typed reads and writes in MIR here is also good as - // it lets Miri and CTFE understand them better, including things - // like enforcing type validity for them. - // Importantly, read+copy_nonoverlapping+write introduces confusing - // asymmetry to the behaviour where one value went through read+write - // whereas the other was copied over by the intrinsic (see #94371). - // Furthermore, using only read+write here benefits limited backends - // such as SPIR-V that work on an underlying *typed* view of memory, - // and thus have trouble with Rust's untyped memory operations. - - // SAFETY: exclusive references are always valid to read/write, - // including being aligned, and nothing here panics so it's drop-safe. - unsafe { - let a = ptr::read(x); - let b = ptr::read(y); - ptr::write(x, b); - ptr::write(y, a); - } + // SAFETY: `&mut` guarantees these are typed readable and writable + // as well as non-overlapping. + unsafe { intrinsics::typed_swap(x, y) } } /// Replaces `dest` with the default value of `T`, returning the previous `dest` value. @@ -1337,7 +1283,7 @@ impl SizedTypeProperties for T {} /// type B = Wrapper; /// /// // Not necessarily identical even though `u8` and `i8` have the same layout! -/// // assert!(mem::offset_of!(A, 1), mem::offset_of!(B, 1)); +/// // assert_eq!(mem::offset_of!(A, 1), mem::offset_of!(B, 1)); /// /// #[repr(transparent)] /// struct U8(u8); @@ -1345,12 +1291,12 @@ impl SizedTypeProperties for T {} /// type C = Wrapper; /// /// // Not necessarily identical even though `u8` and `U8` have the same layout! -/// // assert!(mem::offset_of!(A, 1), mem::offset_of!(C, 1)); +/// // assert_eq!(mem::offset_of!(A, 1), mem::offset_of!(C, 1)); /// /// struct Empty(core::marker::PhantomData); /// /// // Not necessarily identical even though `PhantomData` always has the same layout! -/// // assert!(mem::offset_of!(Empty, 0), mem::offset_of!(Empty, 0)); +/// // assert_eq!(mem::offset_of!(Empty, 0), mem::offset_of!(Empty, 0)); /// ``` /// /// [explicit `repr` attribute]: https://doc.rust-lang.org/reference/type-layout.html#representations @@ -1394,8 +1340,8 @@ impl SizedTypeProperties for T {} /// assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0); /// ``` #[stable(feature = "offset_of", since = "1.77.0")] -#[allow_internal_unstable(builtin_syntax, hint_must_use)] +#[allow_internal_unstable(builtin_syntax)] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { // The `{}` is for better error messages - crate::hint::must_use({builtin # offset_of($Container, $($fields)+)}) + {builtin # offset_of($Container, $($fields)+)} } diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 0687874a2582a..827426b235839 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -6,7 +6,7 @@ use crate::marker::ConstParamTy; /// any value of type `Self` are safely transmutable into a value of type `Dst`, in a given `Context`, /// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied. #[unstable(feature = "transmutability", issue = "99571")] -#[cfg_attr(not(bootstrap), lang = "transmute_trait")] +#[lang = "transmute_trait"] #[rustc_deny_explicit_impl(implement_via_object = false)] #[rustc_coinductive] pub unsafe trait BikeshedIntrinsicFrom diff --git a/library/core/src/net/parser.rs b/library/core/src/net/parser.rs index 835ab9d73af57..deea821244859 100644 --- a/library/core/src/net/parser.rs +++ b/library/core/src/net/parser.rs @@ -3,7 +3,6 @@ //! This module is "publicly exported" through the `FromStr` implementations //! below. -use crate::convert::{TryFrom, TryInto}; use crate::error::Error; use crate::fmt; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; diff --git a/library/core/src/net/socket_addr.rs b/library/core/src/net/socket_addr.rs index 55116285842aa..c24d8f5519504 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/error.rs b/library/core/src/num/error.rs index 14e99578a7c7d..a2d7e6f7b0754 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -113,8 +113,9 @@ pub enum IntErrorKind { impl ParseIntError { /// Outputs the detailed cause of parsing an integer failing. #[must_use] + #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] #[stable(feature = "int_error_matching", since = "1.55.0")] - pub fn kind(&self) -> &IntErrorKind { + pub const fn kind(&self) -> &IntErrorKind { &self.kind } } diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs new file mode 100644 index 0000000000000..8a94964c8c5e6 --- /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::(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::(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 0000000000000..039f5188ba4a6 --- /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::(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::(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/num/f32.rs b/library/core/src/num/f32.rs index abdcb7099cacb..2e715fb0bdde7 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1153,11 +1153,7 @@ impl f32 { // Stability concerns. unsafe { mem::transmute(x) } } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: We use internal implementations that either always work or fail at compile time. - unsafe { - intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) - } + intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) } /// Raw transmutation from `u32`. @@ -1248,11 +1244,7 @@ impl f32 { // Stability concerns. unsafe { mem::transmute(x) } } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: We use internal implementations that either always work or fail at compile time. - unsafe { - intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) - } + intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) } /// Return the memory representation of this floating point number as a byte array in diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index f4d2a4f216734..d56f346d95e47 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1146,11 +1146,7 @@ impl f64 { // Stability concerns. unsafe { mem::transmute::(rt) } } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: We use internal implementations that either always work or fail at compile time. - unsafe { - intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) - } + intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) } /// Raw transmutation from `u64`. @@ -1246,11 +1242,7 @@ impl f64 { // Stability concerns. unsafe { mem::transmute::(rt) } } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: We use internal implementations that either always work or fail at compile time. - unsafe { - intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) - } + intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) } /// Return the memory representation of this floating point number as a byte array in diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index fa37ee4ffb204..77b1039039b1d 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -60,32 +60,6 @@ macro_rules! int_impl { #[stable(feature = "int_bits_const", since = "1.53.0")] pub const BITS: u32 = <$UnsignedT>::BITS; - /// Converts a string slice in a given base to an integer. - /// - /// The string is expected to be an optional `+` or `-` sign followed by digits. - /// Leading and trailing whitespace represent an error. Digits are a subset of these characters, - /// depending on `radix`: - /// - /// * `0-9` - /// * `a-z` - /// * `A-Z` - /// - /// # Panics - /// - /// This function panics if `radix` is not in the range from 2 to 36. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")] - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) - } - /// Returns the number of ones in the binary representation of `self`. /// /// # Examples @@ -492,21 +466,25 @@ macro_rules! int_impl { /// Unchecked integer addition. Computes `self + rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_add(y)` is semantically equivalent to calling + /// `x.`[`checked_add`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_add`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_add`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_add`]: ", stringify!($SelfT), "::checked_add")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_add`]: ", stringify!($SelfT), "::wrapping_add")] + #[stable(feature = "unchecked_math", since = "1.79.0")] + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { @@ -630,21 +608,25 @@ macro_rules! int_impl { /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_sub(y)` is semantically equivalent to calling + /// `x.`[`checked_sub`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_sub`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_sub`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_sub`]: ", stringify!($SelfT), "::checked_sub")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_sub`]: ", stringify!($SelfT), "::wrapping_sub")] + #[stable(feature = "unchecked_math", since = "1.79.0")] + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { @@ -768,21 +750,25 @@ macro_rules! int_impl { /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_mul(y)` is semantically equivalent to calling + /// `x.`[`checked_mul`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_mul`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_mul`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_mul`]: ", stringify!($SelfT), "::checked_mul")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_mul`]: ", stringify!($SelfT), "::wrapping_mul")] + #[stable(feature = "unchecked_math", since = "1.79.0")] + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { @@ -1177,12 +1163,19 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + // We could always go back to wrapping + #[rustc_allow_const_fn_unstable(unchecked_shifts)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) { None } else { Some(a) } + // Not using overflowing_shl as that's a wrapping shift + if rhs < Self::BITS { + // SAFETY: just checked the RHS is in-range + Some(unsafe { self.unchecked_shl(rhs) }) + } else { + None + } } /// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger @@ -1241,10 +1234,9 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shl`. + unsafe { intrinsics::unchecked_shl(self, rhs) } } /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is @@ -1260,12 +1252,19 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + // We could always go back to wrapping + #[rustc_allow_const_fn_unstable(unchecked_shifts)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) { None } else { Some(a) } + // Not using overflowing_shr as that's a wrapping shift + if rhs < Self::BITS { + // SAFETY: just checked the RHS is in-range + Some(unsafe { self.unchecked_shr(rhs) }) + } else { + None + } } /// Strict shift right. Computes `self >> rhs`, panicking `rhs` is @@ -1326,8 +1325,7 @@ macro_rules! int_impl { pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_shr`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + unsafe { intrinsics::unchecked_shr(self, rhs) } } /// Checked absolute value. Computes `self.abs()`, returning `None` if @@ -3107,21 +3105,9 @@ macro_rules! int_impl { if self <= 0 || base <= 1 { None } else { - let mut n = 0; - let mut r = self; - - // Optimization for 128 bit wide integers. - if Self::BITS == 128 { - let b = Self::ilog2(self) / (Self::ilog2(base) + 1); - n += b; - r /= base.pow(b as u32); - } - - while r >= base { - r /= base; - n += 1; - } - Some(n) + // Delegate to the unsigned implementation. + // The condition makes sure that both casts are exact. + (self as $UnsignedT).checked_ilog(base as $UnsignedT) } } @@ -3183,7 +3169,8 @@ macro_rules! int_impl { /// that code in debug mode will trigger a panic on this case and /// optimized code will return #[doc = concat!("`", stringify!($SelfT), "::MIN`")] - /// without a panic. + /// without a panic. If you do not want this behavior, consider + /// using [`unsigned_abs`](Self::unsigned_abs) instead. /// /// # Examples /// diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 03c977abbbb42..c02f73fdf0364 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -6,7 +6,6 @@ use crate::ascii; use crate::hint; use crate::intrinsics; use crate::mem; -use crate::ops::{Add, Mul, Sub}; use crate::str::FromStr; // Used because the `?` operator is not allowed in a const context. @@ -68,15 +67,15 @@ pub use error::ParseIntError; )] pub use nonzero::ZeroablePrimitive; -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "1.79.0")] pub use nonzero::NonZero; -#[stable(feature = "nonzero", since = "1.28.0")] -pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; - #[stable(feature = "signed_nonzero", since = "1.34.0")] pub use nonzero::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; +#[stable(feature = "nonzero", since = "1.28.0")] +pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; + #[stable(feature = "try_from", since = "1.34.0")] pub use error::TryFromIntError; @@ -286,17 +285,6 @@ macro_rules! widening_impl { }; } -macro_rules! conv_rhs_for_unchecked_shift { - ($SelfT:ty, $x:expr) => {{ - // If the `as` cast will truncate, ensure we still tell the backend - // that the pre-truncation value was also small. - if <$SelfT>::BITS < 32 { - intrinsics::assume($x <= (<$SelfT>::MAX as u32)); - } - $x as $SelfT - }}; -} - impl i8 { int_impl! { Self = i8, @@ -1386,51 +1374,19 @@ pub enum FpCategory { Normal, } -#[doc(hidden)] -trait FromStrRadixHelper: - PartialOrd + Copy + Add + Sub + Mul -{ - const MIN: Self; - fn from_u32(u: u32) -> Self; - fn checked_mul(&self, other: u32) -> Option; - fn checked_sub(&self, other: u32) -> Option; - fn checked_add(&self, other: u32) -> Option; -} - macro_rules! from_str_radix_int_impl { ($($t:ty)*) => {$( #[stable(feature = "rust1", since = "1.0.0")] impl FromStr for $t { type Err = ParseIntError; fn from_str(src: &str) -> Result { - from_str_radix(src, 10) + <$t>::from_str_radix(src, 10) } } )*} } from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } -macro_rules! impl_helper_for { - ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { - const MIN: Self = Self::MIN; - #[inline] - fn from_u32(u: u32) -> Self { u as Self } - #[inline] - fn checked_mul(&self, other: u32) -> Option { - Self::checked_mul(*self, other as Self) - } - #[inline] - fn checked_sub(&self, other: u32) -> Option { - Self::checked_sub(*self, other as Self) - } - #[inline] - fn checked_add(&self, other: u32) -> Option { - Self::checked_add(*self, other as Self) - } - })*) -} -impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } - /// Determines if a string of text of that length of that radix could be guaranteed to be /// stored in the given type T. /// Note that if the radix is known to the compiler, it is just the check of digits.len that @@ -1438,92 +1394,198 @@ impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] -pub fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { +pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } -fn from_str_radix(src: &str, radix: u32) -> Result { - use self::IntErrorKind::*; - use self::ParseIntError as PIE; +#[track_caller] +const fn from_str_radix_panic_ct(_radix: u32) -> ! { + panic!("from_str_radix_int: must lie in the range `[2, 36]`"); +} - assert!( - (2..=36).contains(&radix), - "from_str_radix_int: must lie in the range `[2, 36]` - found {}", - radix - ); +#[track_caller] +fn from_str_radix_panic_rt(radix: u32) -> ! { + panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix); +} - if src.is_empty() { - return Err(PIE { kind: Empty }); +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[cold] +#[track_caller] +const fn from_str_radix_assert(radix: u32) { + if 2 > radix || radix > 36 { + // The only difference between these two functions is their panic message. + intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt); } +} - let is_signed_ty = T::from_u32(0) > T::MIN; - - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); +macro_rules! from_str_radix { + ($($int_ty:ty)+) => {$( + impl $int_ty { + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` sign + /// followed by digits. + /// Leading and trailing whitespace represent an error. + /// Digits are a subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { + use self::IntErrorKind::*; + use self::ParseIntError as PIE; + + from_str_radix_assert(radix); + + if src.is_empty() { + return Err(PIE { kind: Empty }); + } - let (is_positive, digits) = match src[0] { - b'+' | b'-' if src[1..].is_empty() => { - return Err(PIE { kind: InvalidDigit }); - } - b'+' => (true, &src[1..]), - b'-' if is_signed_ty => (false, &src[1..]), - _ => (true, src), - }; + #[allow(unused_comparisons)] + let is_signed_ty = 0 > <$int_ty>::MIN; + + // all valid digits are ascii, so we will just iterate over the utf8 bytes + // and cast them to chars. .to_digit() will safely return None for anything + // other than a valid ascii digit for the given radix, including the first-byte + // of multi-byte sequences + let src = src.as_bytes(); + + let (is_positive, mut digits) = match src { + [b'+' | b'-'] => { + return Err(PIE { kind: InvalidDigit }); + } + [b'+', rest @ ..] => (true, rest), + [b'-', rest @ ..] if is_signed_ty => (false, rest), + _ => (true, src), + }; + + let mut result = 0; + + macro_rules! unwrap_or_PIE { + ($option:expr, $kind:ident) => { + match $option { + Some(value) => value, + None => return Err(PIE { kind: $kind }), + } + }; + } - let mut result = T::from_u32(0); - - if can_not_overflow::(radix, is_signed_ty, digits) { - // If the len of the str is short compared to the range of the type - // we are parsing into, then we can be certain that an overflow will not occur. - // This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition - // above is a faster (conservative) approximation of this. - // - // Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest: - // `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow. - // `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow. - macro_rules! run_unchecked_loop { - ($unchecked_additive_op:expr) => { - for &c in digits { - result = result * T::from_u32(radix); - let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?; - result = $unchecked_additive_op(result, T::from_u32(x)); + if can_not_overflow::<$int_ty>(radix, is_signed_ty, digits) { + // If the len of the str is short compared to the range of the type + // we are parsing into, then we can be certain that an overflow will not occur. + // This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition + // above is a faster (conservative) approximation of this. + // + // Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest: + // `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow. + // `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow. + macro_rules! run_unchecked_loop { + ($unchecked_additive_op:tt) => {{ + while let [c, rest @ ..] = digits { + result = result * (radix as $int_ty); + let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit); + result = result $unchecked_additive_op (x as $int_ty); + digits = rest; + } + }}; + } + if is_positive { + run_unchecked_loop!(+) + } else { + run_unchecked_loop!(-) + }; + } else { + macro_rules! run_checked_loop { + ($checked_additive_op:ident, $overflow_err:ident) => {{ + while let [c, rest @ ..] = digits { + // When `radix` is passed in as a literal, rather than doing a slow `imul` + // the compiler can use shifts if `radix` can be expressed as a + // sum of powers of 2 (x*10 can be written as x*8 + x*2). + // When the compiler can't use these optimisations, + // the latency of the multiplication can be hidden by issuing it + // before the result is needed to improve performance on + // modern out-of-order CPU as multiplication here is slower + // than the other instructions, we can get the end result faster + // doing multiplication first and let the CPU spends other cycles + // doing other computation and get multiplication result later. + let mul = result.checked_mul(radix as $int_ty); + let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit) as $int_ty; + result = unwrap_or_PIE!(mul, $overflow_err); + result = unwrap_or_PIE!(<$int_ty>::$checked_additive_op(result, x), $overflow_err); + digits = rest; + } + }}; + } + if is_positive { + run_checked_loop!(checked_add, PosOverflow) + } else { + run_checked_loop!(checked_sub, NegOverflow) + }; } - }; + Ok(result) + } } - if is_positive { - run_unchecked_loop!(::add) - } else { - run_unchecked_loop!(::sub) - }; - } else { - macro_rules! run_checked_loop { - ($checked_additive_op:ident, $overflow_err:expr) => { - for &c in digits { - // When `radix` is passed in as a literal, rather than doing a slow `imul` - // the compiler can use shifts if `radix` can be expressed as a - // sum of powers of 2 (x*10 can be written as x*8 + x*2). - // When the compiler can't use these optimisations, - // the latency of the multiplication can be hidden by issuing it - // before the result is needed to improve performance on - // modern out-of-order CPU as multiplication here is slower - // than the other instructions, we can get the end result faster - // doing multiplication first and let the CPU spends other cycles - // doing other computation and get multiplication result later. - let mul = result.checked_mul(radix); - let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?; - result = mul.ok_or_else($overflow_err)?; - result = T::$checked_additive_op(&result, x).ok_or_else($overflow_err)?; - } - }; + )+} +} + +from_str_radix! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 } + +// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two +// identical functions. +macro_rules! from_str_radix_size_impl { + ($($t:ident $size:ty),*) => {$( + impl $size { + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` sign + /// followed by digits. + /// Leading and trailing whitespace represent an error. + /// Digits are a subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { + match <$t>::from_str_radix(src, radix) { + Ok(x) => Ok(x as $size), + Err(e) => Err(e), + } } - if is_positive { - run_checked_loop!(checked_add, || PIE { kind: PosOverflow }) - } else { - run_checked_loop!(checked_sub, || PIE { kind: NegOverflow }) - }; - } - Ok(result) + })*} } + +#[cfg(target_pointer_width = "16")] +from_str_radix_size_impl! { i16 isize, u16 usize } +#[cfg(target_pointer_width = "32")] +from_str_radix_size_impl! { i32 isize, u32 usize } +#[cfg(target_pointer_width = "64")] +from_str_radix_size_impl! { i64 isize, u64 usize } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 6cbcac20ea1b4..5d3ae7316b124 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -4,68 +4,100 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::intrinsics; -use crate::marker::StructuralPartialEq; -use crate::ops::{BitOr, BitOrAssign, Div, Neg, Rem}; +use crate::marker::{Freeze, StructuralPartialEq}; +use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; +use crate::panic::{RefUnwindSafe, UnwindSafe}; +use crate::ptr; use crate::str::FromStr; +use crate::ub_checks; -use super::from_str_radix; use super::{IntErrorKind, ParseIntError}; -mod private { - #[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" - )] - #[const_trait] - pub trait Sealed {} -} - /// A marker trait for primitive types which can be zero. /// /// This is an implementation detail for [NonZero]\ which may disappear or be replaced at any time. /// /// # Safety /// -/// Types implementing this trait must be primitves that are valid when zeroed. +/// Types implementing this trait must be primitives that are valid when zeroed. +/// +/// The associated `Self::NonZeroInner` type must have the same size+align as `Self`, +/// but with a niche and bit validity making it so the following `transmutes` are sound: +/// +/// - `Self::NonZeroInner` to `Option` +/// - `Option` to `Self` +/// +/// (And, consequently, `Self::NonZeroInner` to `Self`.) #[unstable( feature = "nonzero_internals", reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] #[const_trait] -pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed {} +pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed { + #[doc(hidden)] + type NonZeroInner: Sized + Copy; +} macro_rules! impl_zeroable_primitive { - ($primitive:ty) => { - #[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" - )] - impl const private::Sealed for $primitive {} - - #[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" - )] - unsafe impl const ZeroablePrimitive for $primitive {} + ($($NonZeroInner:ident ( $primitive:ty )),+ $(,)?) => { + mod private { + #[unstable( + feature = "nonzero_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" + )] + #[const_trait] + pub trait Sealed {} + + $( + #[derive(Debug, Clone, Copy, PartialEq)] + #[repr(transparent)] + #[rustc_layout_scalar_valid_range_start(1)] + #[rustc_nonnull_optimization_guaranteed] + #[unstable( + feature = "nonzero_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" + )] + pub struct $NonZeroInner($primitive); + )+ + } + + $( + #[unstable( + feature = "nonzero_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" + )] + impl const private::Sealed for $primitive {} + + #[unstable( + feature = "nonzero_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" + )] + unsafe impl const ZeroablePrimitive for $primitive { + type NonZeroInner = private::$NonZeroInner; + } + )+ }; } -impl_zeroable_primitive!(u8); -impl_zeroable_primitive!(u16); -impl_zeroable_primitive!(u32); -impl_zeroable_primitive!(u64); -impl_zeroable_primitive!(u128); -impl_zeroable_primitive!(usize); -impl_zeroable_primitive!(i8); -impl_zeroable_primitive!(i16); -impl_zeroable_primitive!(i32); -impl_zeroable_primitive!(i64); -impl_zeroable_primitive!(i128); -impl_zeroable_primitive!(isize); +impl_zeroable_primitive!( + NonZeroU8Inner(u8), + NonZeroU16Inner(u16), + NonZeroU32Inner(u32), + NonZeroU64Inner(u64), + NonZeroU128Inner(u128), + NonZeroUsizeInner(usize), + NonZeroI8Inner(i8), + NonZeroI16Inner(i16), + NonZeroI32Inner(i32), + NonZeroI64Inner(i64), + NonZeroI128Inner(i128), + NonZeroIsizeInner(isize), +); /// A value that is known not to equal zero. /// @@ -73,17 +105,15 @@ impl_zeroable_primitive!(isize); /// For example, `Option>` is the same size as `u32`: /// /// ``` -/// #![feature(generic_nonzero)] -/// use core::mem::size_of; +/// use core::{mem::size_of, num::NonZero}; /// -/// assert_eq!(size_of::>>(), size_of::()); +/// assert_eq!(size_of::>>(), size_of::()); /// ``` -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "1.79.0")] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] #[rustc_diagnostic_item = "NonZero"] -pub struct NonZero(T); +pub struct NonZero(T::NonZeroInner); macro_rules! impl_nonzero_fmt { ($Trait:ident) => { @@ -107,6 +137,26 @@ impl_nonzero_fmt!(Octal); impl_nonzero_fmt!(LowerHex); impl_nonzero_fmt!(UpperHex); +macro_rules! impl_nonzero_auto_trait { + (unsafe $Trait:ident) => { + #[stable(feature = "nonzero", since = "1.28.0")] + unsafe impl $Trait for NonZero where T: ZeroablePrimitive + $Trait {} + }; + ($Trait:ident) => { + #[stable(feature = "nonzero", since = "1.28.0")] + impl $Trait for NonZero where T: ZeroablePrimitive + $Trait {} + }; +} + +// Implement auto-traits manually based on `T` to avoid docs exposing +// the `ZeroablePrimitive::NonZeroInner` implementation detail. +impl_nonzero_auto_trait!(unsafe Freeze); +impl_nonzero_auto_trait!(RefUnwindSafe); +impl_nonzero_auto_trait!(unsafe Send); +impl_nonzero_auto_trait!(unsafe Sync); +impl_nonzero_auto_trait!(Unpin); +impl_nonzero_auto_trait!(UnwindSafe); + #[stable(feature = "nonzero", since = "1.28.0")] impl Clone for NonZero where @@ -114,8 +164,7 @@ where { #[inline] fn clone(&self) -> Self { - // SAFETY: The contained value is non-zero. - unsafe { Self(self.0) } + Self(self.0) } } @@ -188,19 +237,19 @@ where #[inline] fn max(self, other: Self) -> Self { // SAFETY: The maximum of two non-zero values is still non-zero. - unsafe { Self(self.get().max(other.get())) } + unsafe { Self::new_unchecked(self.get().max(other.get())) } } #[inline] fn min(self, other: Self) -> Self { // SAFETY: The minimum of two non-zero values is still non-zero. - unsafe { Self(self.get().min(other.get())) } + unsafe { Self::new_unchecked(self.get().min(other.get())) } } #[inline] fn clamp(self, min: Self, max: Self) -> Self { // SAFETY: A non-zero value clamped between two non-zero values is still non-zero. - unsafe { Self(self.get().clamp(min.get(), max.get())) } + unsafe { Self::new_unchecked(self.get().clamp(min.get(), max.get())) } } } @@ -240,7 +289,7 @@ where #[inline] fn bitor(self, rhs: Self) -> Self::Output { // SAFETY: Bitwise OR of two non-zero values is still non-zero. - unsafe { Self(self.get() | rhs.get()) } + unsafe { Self::new_unchecked(self.get() | rhs.get()) } } } @@ -254,7 +303,7 @@ where #[inline] fn bitor(self, rhs: T) -> Self::Output { // SAFETY: Bitwise OR of a non-zero value with anything is still non-zero. - unsafe { Self(self.get() | rhs) } + unsafe { Self::new_unchecked(self.get() | rhs) } } } @@ -268,7 +317,7 @@ where #[inline] fn bitor(self, rhs: NonZero) -> Self::Output { // SAFETY: Bitwise OR of anything with a non-zero value is still non-zero. - unsafe { NonZero(self | rhs.get()) } + unsafe { NonZero::new_unchecked(self | rhs.get()) } } } @@ -327,7 +376,7 @@ where None => { // SAFETY: The caller guarantees that `n` is non-zero, so this is unreachable. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "NonZero::new_unchecked requires the argument to be non-zero", () => false, @@ -346,7 +395,7 @@ where pub fn from_mut(n: &mut T) -> Option<&mut Self> { // SAFETY: Memory layout optimization guarantees that `Option>` has // the same layout and size as `T`, with `0` representing `None`. - let opt_n = unsafe { &mut *(n as *mut T as *mut Option) }; + let opt_n = unsafe { &mut *(ptr::from_mut(n).cast::>()) }; opt_n.as_mut() } @@ -367,7 +416,7 @@ where None => { // SAFETY: The caller guarantees that `n` references a value that is non-zero, so this is unreachable. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_library_ub, "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", () => false, @@ -390,13 +439,12 @@ where // memory somewhere. If the value of `self` was from by-value argument // of some not-inlined function, LLVM don't have range metadata // to understand that the value cannot be zero. - match Self::new(self.0) { - Some(Self(n)) => n, - None => { - // SAFETY: `NonZero` is guaranteed to only contain non-zero values, so this is unreachable. - unsafe { intrinsics::unreachable() } - } - } + // + // For now, using the transmute `assume`s the range at runtime. + // + // SAFETY: `ZeroablePrimitive` guarantees that the size and bit validity + // of `.0` is such that this transmute is sound. + unsafe { intrinsics::transmute_unchecked(self) } } } @@ -479,7 +527,9 @@ macro_rules! nonzero_integer { #[inline] pub const fn leading_zeros(self) -> u32 { // SAFETY: since `self` cannot be zero, it is safe to call `ctlz_nonzero`. - unsafe { intrinsics::ctlz_nonzero(self.get() as $UnsignedPrimitive) as u32 } + unsafe { + intrinsics::ctlz_nonzero(self.get() as $UnsignedPrimitive) + } } /// Returns the number of trailing zeros in the binary representation @@ -503,7 +553,9 @@ macro_rules! nonzero_integer { #[inline] pub const fn trailing_zeros(self) -> u32 { // SAFETY: since `self` cannot be zero, it is safe to call `cttz_nonzero`. - unsafe { intrinsics::cttz_nonzero(self.get() as $UnsignedPrimitive) as u32 } + unsafe { + intrinsics::cttz_nonzero(self.get() as $UnsignedPrimitive) + } } /// Returns the number of ones in the binary representation of `self`. @@ -513,7 +565,8 @@ macro_rules! nonzero_integer { /// Basic usage: /// /// ``` - /// #![feature(generic_nonzero, non_zero_count_ones)] + /// #![feature(non_zero_count_ones)] + /// /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { /// # use std::num::*; @@ -754,7 +807,7 @@ macro_rules! nonzero_integer { impl FromStr for $Ty { type Err = ParseIntError; fn from_str(src: &str) -> Result { - Self::new(from_str_radix(src, 10)?) + Self::new(<$Int>::from_str_radix(src, 10)?) .ok_or(ParseIntError { kind: IntErrorKind::Zero }) @@ -802,6 +855,16 @@ macro_rules! nonzero_integer_signedness_dependent_impls { } } + #[stable(feature = "nonzero_div_assign", since = "1.79.0")] + impl DivAssign<$Ty> for $Int { + /// This operation rounds towards zero, + /// truncating any fractional part of the exact result, and cannot panic. + #[inline] + fn div_assign(&mut self, other: $Ty) { + *self = *self / other; + } + } + #[stable(feature = "nonzero_div", since = "1.51.0")] impl Rem<$Ty> for $Int { type Output = $Int; @@ -814,6 +877,15 @@ macro_rules! nonzero_integer_signedness_dependent_impls { unsafe { intrinsics::unchecked_rem(self, other.get()) } } } + + #[stable(feature = "nonzero_div_assign", since = "1.79.0")] + impl RemAssign<$Ty> for $Int { + /// This operation satisfies `n % d == n - (n / d) * d`, and cannot panic. + #[inline] + fn rem_assign(&mut self, other: $Ty) { + *self = *self % other; + } + } }; // Impls for signed nonzero types only. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 081a3c0b118ea..9effa279b48e1 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -58,33 +58,6 @@ macro_rules! uint_impl { #[stable(feature = "int_bits_const", since = "1.53.0")] pub const BITS: u32 = Self::MAX.count_ones(); - /// Converts a string slice in a given base to an integer. - /// - /// The string is expected to be an optional `+` sign - /// followed by digits. - /// Leading and trailing whitespace represent an error. - /// Digits are a subset of these characters, depending on `radix`: - /// - /// * `0-9` - /// * `a-z` - /// * `A-Z` - /// - /// # Panics - /// - /// This function panics if `radix` is not in the range from 2 to 36. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")] - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) - } - /// Returns the number of ones in the binary representation of `self`. /// /// # Examples @@ -104,7 +77,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] pub const fn count_ones(self) -> u32 { - intrinsics::ctpop(self as $ActualT) as u32 + return intrinsics::ctpop(self as $ActualT); } /// Returns the number of zeros in the binary representation of `self`. @@ -146,7 +119,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] pub const fn leading_zeros(self) -> u32 { - intrinsics::ctlz(self as $ActualT) as u32 + return intrinsics::ctlz(self as $ActualT); } /// Returns the number of trailing zeros in the binary representation @@ -167,7 +140,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] pub const fn trailing_zeros(self) -> u32 { - intrinsics::cttz(self) as u32 + return intrinsics::cttz(self); } /// Returns the number of leading ones in the binary representation of `self`. @@ -232,7 +205,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] pub const fn rotate_left(self, n: u32) -> Self { - intrinsics::rotate_left(self, n as $SelfT) + return intrinsics::rotate_left(self, n); } /// Shifts the bits to the right by a specified amount, `n`, @@ -257,7 +230,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] pub const fn rotate_right(self, n: u32) -> Self { - intrinsics::rotate_right(self, n as $SelfT) + return intrinsics::rotate_right(self, n); } /// Reverses the byte order of the integer. @@ -500,21 +473,25 @@ macro_rules! uint_impl { /// Unchecked integer addition. Computes `self + rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_add(y)` is semantically equivalent to calling + /// `x.`[`checked_add`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_add`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_add`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_add`]: ", stringify!($SelfT), "::checked_add")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_add`]: ", stringify!($SelfT), "::wrapping_add")] + #[stable(feature = "unchecked_math", since = "1.79.0")] + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { @@ -602,8 +579,17 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_sub(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) { None } else { Some(a) } + // Per PR#103299, there's no advantage to the `overflowing` intrinsic + // for *unsigned* subtraction and we just emit the manual check anyway. + // Thus, rather than using `overflowing_sub` that produces a wrapping + // subtraction, check it ourself so we can use an unchecked one. + + if self >= rhs { + // SAFETY: just checked this can't overflow + Some(unsafe { intrinsics::unchecked_sub(self, rhs) }) + } else { + None + } } /// Strict integer subtraction. Computes `self - rhs`, panicking if @@ -644,21 +630,25 @@ macro_rules! uint_impl { /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_sub(y)` is semantically equivalent to calling + /// `x.`[`checked_sub`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_sub`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_sub`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_sub`]: ", stringify!($SelfT), "::checked_sub")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_sub`]: ", stringify!($SelfT), "::wrapping_sub")] + #[stable(feature = "unchecked_math", since = "1.79.0")] + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { @@ -726,21 +716,25 @@ macro_rules! uint_impl { /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow /// cannot occur. /// + /// Calling `x.unchecked_mul(y)` is semantically equivalent to calling + /// `x.`[`checked_mul`]`(y).`[`unwrap_unchecked`]`()`. + /// + /// If you're just trying to avoid the panic in debug mode, then **do not** + /// use this. Instead, you're looking for [`wrapping_mul`]. + /// /// # Safety /// /// This results in undefined behavior when #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`,")] /// i.e. when [`checked_mul`] would return `None`. /// + /// [`unwrap_unchecked`]: option/enum.Option.html#method.unwrap_unchecked #[doc = concat!("[`checked_mul`]: ", stringify!($SelfT), "::checked_mul")] - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "85122", - )] + #[doc = concat!("[`wrapping_mul`]: ", stringify!($SelfT), "::wrapping_mul")] + #[stable(feature = "unchecked_math", since = "1.79.0")] + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { @@ -1101,18 +1095,25 @@ macro_rules! uint_impl { None } else { let mut n = 0; - let mut r = self; + let mut r = 1; // Optimization for 128 bit wide integers. if Self::BITS == 128 { - let b = Self::ilog2(self) / (Self::ilog2(base) + 1); - n += b; - r /= base.pow(b as u32); + // The following is a correct lower bound for ⌊log(base,self)⌋ because + // + // log(base,self) = log(2,self) / log(2,base) + // ≥ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) + // + // hence + // + // ⌊log(base,self)⌋ ≥ ⌊ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) ⌋ . + n = self.ilog2() / (base.ilog2() + 1); + r = base.pow(n); } - while r >= base { - r /= base; + while r <= self / base { n += 1; + r *= base; } Some(n) } @@ -1237,12 +1238,19 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + // We could always go back to wrapping + #[rustc_allow_const_fn_unstable(unchecked_shifts)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) { None } else { Some(a) } + // Not using overflowing_shl as that's a wrapping shift + if rhs < Self::BITS { + // SAFETY: just checked the RHS is in-range + Some(unsafe { self.unchecked_shl(rhs) }) + } else { + None + } } /// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger @@ -1303,8 +1311,7 @@ macro_rules! uint_impl { pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_shl`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + unsafe { intrinsics::unchecked_shl(self, rhs) } } /// Checked shift right. Computes `self >> rhs`, returning `None` @@ -1320,12 +1327,19 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + // We could always go back to wrapping + #[rustc_allow_const_fn_unstable(unchecked_shifts)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) { None } else { Some(a) } + // Not using overflowing_shr as that's a wrapping shift + if rhs < Self::BITS { + // SAFETY: just checked the RHS is in-range + Some(unsafe { self.unchecked_shr(rhs) }) + } else { + None + } } /// Strict shift right. Computes `self >> rhs`, panicking `rhs` is @@ -1384,10 +1398,9 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + // SAFETY: the caller must uphold the safety contract for + // `unchecked_shr`. + unsafe { intrinsics::unchecked_shr(self, rhs) } } /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index fd50f80474833..5e77788d8ea36 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/ops/async_function.rs b/library/core/src/ops/async_function.rs index d6b06ffb7fc0f..18bcee5a1c7e0 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -10,15 +10,9 @@ use crate::marker::Tuple; #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn"] pub trait AsyncFn: AsyncFnMut { - /// Future returned by [`AsyncFn::async_call`]. - #[unstable(feature = "async_fn_traits", issue = "none")] - type CallFuture<'a>: Future - where - Self: 'a; - /// Call the [`AsyncFn`], returning a future which may borrow from the called closure. #[unstable(feature = "async_fn_traits", issue = "none")] - extern "rust-call" fn async_call(&self, args: Args) -> Self::CallFuture<'_>; + extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_>; } /// An async-aware version of the [`FnMut`](crate::ops::FnMut) trait. @@ -30,15 +24,15 @@ pub trait AsyncFn: AsyncFnMut { #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_mut"] pub trait AsyncFnMut: AsyncFnOnce { - /// Future returned by [`AsyncFnMut::async_call_mut`]. + /// Future returned by [`AsyncFnMut::async_call_mut`] and [`AsyncFn::async_call`]. #[unstable(feature = "async_fn_traits", issue = "none")] - type CallMutFuture<'a>: Future + type CallRefFuture<'a>: Future where Self: 'a; /// Call the [`AsyncFnMut`], returning a future which may borrow from the called closure. #[unstable(feature = "async_fn_traits", issue = "none")] - extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallMutFuture<'_>; + extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallRefFuture<'_>; } /// An async-aware version of the [`FnOnce`](crate::ops::FnOnce) trait. @@ -72,9 +66,7 @@ mod impls { where F: AsyncFn, { - type CallFuture<'a> = F::CallFuture<'a> where Self: 'a; - - extern "rust-call" fn async_call(&self, args: A) -> Self::CallFuture<'_> { + extern "rust-call" fn async_call(&self, args: A) -> Self::CallRefFuture<'_> { F::async_call(*self, args) } } @@ -84,9 +76,9 @@ mod impls { where F: AsyncFn, { - type CallMutFuture<'a> = F::CallFuture<'a> where Self: 'a; + type CallRefFuture<'a> = F::CallRefFuture<'a> where Self: 'a; - extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> { + extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallRefFuture<'_> { F::async_call(*self, args) } } @@ -97,7 +89,7 @@ mod impls { F: AsyncFn, { type Output = F::Output; - type CallOnceFuture = F::CallFuture<'a>; + type CallOnceFuture = F::CallRefFuture<'a>; extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture { F::async_call(self, args) @@ -109,9 +101,9 @@ mod impls { where F: AsyncFnMut, { - type CallMutFuture<'a> = F::CallMutFuture<'a> where Self: 'a; + type CallRefFuture<'a> = F::CallRefFuture<'a> where Self: 'a; - extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallMutFuture<'_> { + extern "rust-call" fn async_call_mut(&mut self, args: A) -> Self::CallRefFuture<'_> { F::async_call_mut(*self, args) } } @@ -122,7 +114,7 @@ mod impls { F: AsyncFnMut, { type Output = F::Output; - type CallOnceFuture = F::CallMutFuture<'a>; + type CallOnceFuture = F::CallRefFuture<'a>; extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture { F::async_call_mut(self, args) @@ -140,7 +132,7 @@ mod internal_implementation_detail { /// and thus either `?0` or `i8`/`i16`/`i32` (see docs for `ClosureKind` /// for an explanation of that). The `GoalKind` is also the same type, but /// representing the kind of the trait that the closure is being called with. - #[cfg_attr(not(bootstrap), lang = "async_fn_kind_helper")] + #[lang = "async_fn_kind_helper"] trait AsyncFnKindHelper { // Projects a set of closure inputs (arguments), a region, and a set of upvars // (by move and by ref) to the upvars that we expect the coroutine to have diff --git a/library/core/src/ops/coroutine.rs b/library/core/src/ops/coroutine.rs index 6e067f95da92d..6a6c5db1ab115 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -40,12 +40,13 @@ pub enum CoroutineState { /// ```rust /// #![feature(coroutines)] /// #![feature(coroutine_trait)] +/// #![feature(stmt_expr_attributes)] /// /// use std::ops::{Coroutine, CoroutineState}; /// use std::pin::Pin; /// /// fn main() { -/// let mut coroutine = || { +/// let mut coroutine = #[coroutine] || { /// yield 1; /// "foo" /// }; diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 2c7845d4304fd..9849410d484ca 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -275,6 +275,25 @@ impl DerefMut for &mut T { } } +/// Perma-unstable marker trait. Indicates that the type has a well-behaved [`Deref`] +/// (and, if applicable, [`DerefMut`]) implementation. This is relied on for soundness +/// of deref patterns. +/// +/// FIXME(deref_patterns): The precise semantics are undecided; the rough idea is that +/// successive calls to `deref`/`deref_mut` without intermediate mutation should be +/// idempotent, in the sense that they return the same value as far as pattern-matching +/// is concerned. Calls to `deref`/`deref_mut`` must leave the pointer itself likewise +/// unchanged. +#[unstable(feature = "deref_pure_trait", issue = "87121")] +#[lang = "deref_pure"] +pub unsafe trait DerefPure {} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for &T {} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for &mut T {} + /// Indicates that a struct can be used as a method receiver, without the /// `arbitrary_self_types` feature. This is implemented by stdlib pointer types like `Box`, /// `Rc`, `&T`, and `Pin

`. diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index 34dfa9e4c51fc..36ae581e3f723 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -238,3 +238,10 @@ pub trait Drop { #[stable(feature = "rust1", since = "1.0.0")] fn drop(&mut self); } + +/// Fallback function to call surface level `Drop::drop` function +#[allow(drop_bounds)] +#[lang = "fallback_surface_drop"] +pub(crate) fn fallback_surface_drop(x: &mut T) { + ::drop(x) +} diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index b28d88fa5bfc2..65bda9177c7be 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -1,6 +1,7 @@ -use crate::intrinsics::{assert_unsafe_precondition, unchecked_add, unchecked_sub}; +use crate::intrinsics::{unchecked_add, unchecked_sub}; use crate::iter::{FusedIterator, TrustedLen}; use crate::num::NonZero; +use crate::ub_checks; /// Like a `Range`, but with a safety invariant that `start <= end`. /// @@ -19,7 +20,7 @@ impl IndexRange { /// - `start <= end` #[inline] pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_library_ub, "IndexRange::new_unchecked requires `start <= end`", (start: usize = start, end: usize = end) => start <= end, diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 4289a86f89b06..7bcfaadbe372b 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -165,12 +165,17 @@ pub use self::bit::{BitAndAssign, BitOrAssign, BitXorAssign, ShlAssign, ShrAssig #[stable(feature = "rust1", since = "1.0.0")] pub use self::deref::{Deref, DerefMut}; +#[unstable(feature = "deref_pure_trait", issue = "87121")] +pub use self::deref::DerefPure; + #[unstable(feature = "receiver_trait", issue = "none")] pub use self::deref::Receiver; #[stable(feature = "rust1", since = "1.0.0")] pub use self::drop::Drop; +pub(crate) use self::drop::fallback_surface_drop; + #[stable(feature = "rust1", since = "1.0.0")] pub use self::function::{Fn, FnMut, FnOnce}; diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 3f8c8efd416f3..483f55b207093 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -310,7 +310,7 @@ pub trait FromResidual::Residual> { /// This should be implemented consistently with the `branch` method such /// that applying the `?` operator will get back an equivalent residual: /// `FromResidual::from_residual(r).branch() --> ControlFlow::Break(r)`. - /// (It must not be an *identical* residual when interconversion is involved.) + /// (The residual is not mandated to be *identical* when interconversion is involved.) /// /// # Examples /// diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 5027e326a9d7a..1e3ed0f7c49f1 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -553,22 +553,21 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; -use crate::panicking::{panic, panic_str}; +use crate::iter::{self, FusedIterator, TrustedLen}; +use crate::panicking::{panic, panic_display}; use crate::pin::Pin; use crate::{ cmp, convert, hint, mem, - num::NonZero, ops::{self, ControlFlow, Deref, DerefMut}, slice, }; /// The `Option` type. See [the module level documentation](self) for more. -#[derive(Copy, PartialOrd, Eq, Ord, Debug, Hash)] +#[derive(Copy, Eq, Debug, Hash)] #[rustc_diagnostic_item = "Option"] #[lang = "Option"] #[stable(feature = "rust1", since = "1.0.0")] -#[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is specialized +#[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is manually implemented equivalently pub enum Option { /// No value. #[lang = "None"] @@ -1992,7 +1991,7 @@ const fn unwrap_failed() -> ! { #[track_caller] #[rustc_const_unstable(feature = "const_option", issue = "67441")] const fn expect_failed(msg: &str) -> ! { - panic_str(msg) + panic_display(&msg) } ///////////////////////////////////////////////////////////////////////////// @@ -2146,83 +2145,52 @@ impl<'a, T> From<&'a mut Option> for Option<&'a mut T> { } } +// Ideally, LLVM should be able to optimize our derive code to this. +// Once https://github.com/llvm/llvm-project/issues/52622 is fixed, we can +// go back to deriving `PartialEq`. #[stable(feature = "rust1", since = "1.0.0")] impl crate::marker::StructuralPartialEq for Option {} #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for Option { #[inline] fn eq(&self, other: &Self) -> bool { - SpecOptionPartialEq::eq(self, other) - } -} - -/// This specialization trait is a workaround for LLVM not currently (2023-01) -/// being able to optimize this itself, even though Alive confirms that it would -/// be legal to do so: -/// -/// Once that's fixed, `Option` should go back to deriving `PartialEq`, as -/// it used to do before . -#[unstable(feature = "spec_option_partial_eq", issue = "none", reason = "exposed only for rustc")] -#[doc(hidden)] -pub trait SpecOptionPartialEq: Sized { - fn eq(l: &Option, other: &Option) -> bool; -} - -#[unstable(feature = "spec_option_partial_eq", issue = "none", reason = "exposed only for rustc")] -impl SpecOptionPartialEq for T { - #[inline] - default fn eq(l: &Option, r: &Option) -> bool { - match (l, r) { + // Spelling out the cases explicitly optimizes better than + // `_ => false` + match (self, other) { (Some(l), Some(r)) => *l == *r, + (Some(_), None) => false, + (None, Some(_)) => false, (None, None) => true, - _ => false, } } } -macro_rules! non_zero_option { - ( $( #[$stability: meta] $NZ:ty; )+ ) => { - $( - #[$stability] - impl SpecOptionPartialEq for $NZ { - #[inline] - fn eq(l: &Option, r: &Option) -> bool { - l.map(Self::get).unwrap_or(0) == r.map(Self::get).unwrap_or(0) - } - } - )+ - }; -} - -non_zero_option! { - #[stable(feature = "nonzero", since = "1.28.0")] NonZero; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero; -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl SpecOptionPartialEq for crate::ptr::NonNull { +// Manually implementing here somewhat improves codegen for +// https://github.com/rust-lang/rust/issues/49892, although still +// not optimal. +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Option { #[inline] - fn eq(l: &Option, r: &Option) -> bool { - l.map(Self::as_ptr).unwrap_or_else(|| crate::ptr::null_mut()) - == r.map(Self::as_ptr).unwrap_or_else(|| crate::ptr::null_mut()) + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Some(l), Some(r)) => l.partial_cmp(r), + (Some(_), None) => Some(cmp::Ordering::Greater), + (None, Some(_)) => Some(cmp::Ordering::Less), + (None, None) => Some(cmp::Ordering::Equal), + } } } #[stable(feature = "rust1", since = "1.0.0")] -impl SpecOptionPartialEq for cmp::Ordering { +impl Ord for Option { #[inline] - fn eq(l: &Option, r: &Option) -> bool { - l.map_or(2, |x| x as i8) == r.map_or(2, |x| x as i8) + fn cmp(&self, other: &Self) -> cmp::Ordering { + match (self, other) { + (Some(l), Some(r)) => l.cmp(r), + (Some(_), None) => cmp::Ordering::Greater, + (None, Some(_)) => cmp::Ordering::Less, + (None, None) => cmp::Ordering::Equal, + } } } diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index b520efe93f90d..8771f40f9b42b 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -27,9 +27,9 @@ pub macro panic_2015 { ($msg:literal $(,)?) => ( $crate::panicking::panic($msg) ), - // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint. + // Use `panic_str_2015` instead of `panic_display::<&str>` for non_fmt_panic lint. ($msg:expr $(,)?) => ({ - $crate::panicking::panic_str($msg); + $crate::panicking::panic_str_2015($msg); }), // Special-case the single-argument case for const_panic. ("{}", $arg:expr $(,)?) => ({ diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 6dcf23dde87c7..eb27da1724ec9 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -81,7 +81,7 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "track_caller", since = "1.46.0")] - #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] + #[rustc_const_stable(feature = "const_caller_location", since = "1.79.0")] #[track_caller] #[inline] pub const fn caller() -> &'static Location<'static> { @@ -123,7 +123,7 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - #[rustc_const_unstable(feature = "const_location_fields", issue = "102911")] + #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")] #[inline] pub const fn file(&self) -> &str { self.file @@ -148,7 +148,7 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - #[rustc_const_unstable(feature = "const_location_fields", issue = "102911")] + #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")] #[inline] pub const fn line(&self) -> u32 { self.line @@ -173,7 +173,7 @@ impl<'a> Location<'a> { /// ``` #[must_use] #[stable(feature = "panic_col", since = "1.25.0")] - #[rustc_const_unstable(feature = "const_location_fields", issue = "102911")] + #[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")] #[inline] pub const fn column(&self) -> u32 { self.col diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index c77e9675a6ac5..403262212580c 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -161,11 +161,12 @@ impl fmt::Display for PanicInfo<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("panicked at ")?; self.location.fmt(formatter)?; + formatter.write_str(":")?; if let Some(message) = self.message { - formatter.write_str(":\n")?; + formatter.write_str("\n")?; formatter.write_fmt(*message)?; } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() { - formatter.write_str(":\n")?; + formatter.write_str("\n")?; formatter.write_str(payload)?; } // NOTE: we cannot use downcast_ref::() here diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 9e1184c8f5b88..ca06e059b75ac 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -117,34 +117,97 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo panic_fmt(fmt); } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: const panic does not care about unwinding - unsafe { - super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime); - } + super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime); } // Next we define a bunch of higher-level wrappers that all bottom out in the two core functions // above. /// The underlying implementation of core's `panic!` macro when no formatting is used. -// never inline unless panic_immediate_abort to avoid code -// bloat at the call sites as much as possible +// Never inline unless panic_immediate_abort to avoid code +// bloat at the call sites as much as possible. #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] #[rustc_const_unstable(feature = "panic_internals", issue = "none")] -#[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators +#[lang = "panic"] // used by lints and miri for panics pub const fn panic(expr: &'static str) -> ! { - // Use Arguments::new_v1 instead of format_args!("{expr}") to potentially + // Use Arguments::new_const instead of format_args!("{expr}") to potentially // reduce size overhead. The format_args! macro uses str's Display trait to // write expr, which calls Formatter::pad, which must accommodate string // truncation and padding (even though none is used here). Using - // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the + // Arguments::new_const may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. + // However, this optimization only works for `'static` strings: `new_const` also makes this + // message return `Some` from `Arguments::as_str`, which means it can become part of the panic + // payload without any allocation or copying. Shorter-lived strings would become invalid as + // stack frames get popped during unwinding, and couldn't be directly referenced from the + // payload. panic_fmt(fmt::Arguments::new_const(&[expr])); } +// We generate functions for usage by compiler-generated assertions. +// +// Placing these functions in libcore means that all Rust programs can generate a jump into this +// code rather than expanding to panic("...") above, which adds extra bloat to call sites (for the +// constant string argument's pointer and length). +// +// This is especially important when this code is called often (e.g., with -Coverflow-checks) for +// reducing binary size impact. +macro_rules! panic_const { + ($($lang:ident = $message:expr,)+) => { + pub mod panic_const { + use super::*; + + $( + /// This is a panic called with a message that's a result of a MIR-produced Assert. + // + // never inline unless panic_immediate_abort to avoid code + // bloat at the call sites as much as possible + #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] + #[cfg_attr(feature = "panic_immediate_abort", inline)] + #[track_caller] + #[rustc_const_unstable(feature = "panic_internals", issue = "none")] + #[lang = stringify!($lang)] + pub const fn $lang() -> ! { + // Use Arguments::new_const instead of format_args!("{expr}") to potentially + // reduce size overhead. The format_args! macro uses str's Display trait to + // write expr, which calls Formatter::pad, which must accommodate string + // truncation and padding (even though none is used here). Using + // Arguments::new_const may allow the compiler to omit Formatter::pad from the + // output binary, saving up to a few kilobytes. + panic_fmt(fmt::Arguments::new_const(&[$message])); + } + )+ + } + } +} + +// Unfortunately this set of strings is replicated here and in a few places in the compiler in +// slightly different forms. It's not clear if there's a good way to deduplicate without adding +// special cases to the compiler (e.g., a const generic function wouldn't have a single definition +// shared across crates, which is exactly what we want here). +panic_const! { + panic_const_add_overflow = "attempt to add with overflow", + panic_const_sub_overflow = "attempt to subtract with overflow", + panic_const_mul_overflow = "attempt to multiply with overflow", + panic_const_div_overflow = "attempt to divide with overflow", + panic_const_rem_overflow = "attempt to calculate the remainder with overflow", + panic_const_neg_overflow = "attempt to negate with overflow", + panic_const_shr_overflow = "attempt to shift right with overflow", + panic_const_shl_overflow = "attempt to shift left with overflow", + panic_const_div_by_zero = "attempt to divide by zero", + panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", + panic_const_coroutine_resumed = "coroutine resumed after completion", + panic_const_async_fn_resumed = "`async fn` resumed after completion", + panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion", + panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion", + panic_const_coroutine_resumed_panic = "coroutine resumed after panicking", + panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking", + panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking", + panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking", +} + /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. /// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly. #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] @@ -164,14 +227,6 @@ pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ true); } -#[inline] -#[track_caller] -#[rustc_diagnostic_item = "panic_str"] -#[rustc_const_unstable(feature = "panic_internals", issue = "none")] -pub const fn panic_str(expr: &str) -> ! { - panic_display(&expr); -} - #[track_caller] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] @@ -187,6 +242,16 @@ pub fn unreachable_display(x: &T) -> ! { panic_fmt(format_args!("internal error: entered unreachable code: {}", *x)); } +/// This exists solely for the 2015 edition `panic!` macro to trigger +/// a lint on `panic!(my_str_variable);`. +#[inline] +#[track_caller] +#[rustc_diagnostic_item = "panic_str_2015"] +#[rustc_const_unstable(feature = "panic_internals", issue = "none")] +pub const fn panic_str_2015(expr: &str) -> ! { + panic_display(&expr); +} + #[inline] #[track_caller] #[rustc_do_not_const_check] // hooked by const-eval diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs new file mode 100644 index 0000000000000..a10c45933428d --- /dev/null +++ b/library/core/src/pat.rs @@ -0,0 +1,14 @@ +//! Helper module for exporting the `pattern_type` macro + +/// Creates a pattern type. +/// ```ignore (cannot test this from within core yet) +/// type Positive = std::pat::pattern_type!(i32 is 1..); +/// ``` +#[macro_export] +#[rustc_builtin_macro(pattern_type)] +#[unstable(feature = "core_pattern_type", issue = "none")] +macro_rules! pattern_type { + ($($arg:tt)*) => { + /* compiler built-in */ + }; +} diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index a0227d9130bb7..f7463d170fd8e 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -144,7 +144,7 @@ //! * e.g. [`drop`]ping the [`Future`] [^pin-drop-future] //! //! There are two possible ways to ensure the invariants required for 2. and 3. above (which -//! apply to any address-sensitive type, not just self-referrential types) do not get broken. +//! apply to any address-sensitive type, not just self-referential types) do not get broken. //! //! 1. Have the value detect when it is moved and update all the pointers that point to itself. //! 2. Guarantee that the address of the value does not change (and that memory is not re-used @@ -170,7 +170,7 @@ //! become viral throughout all code that interacts with the object. //! //! The second option is a viable solution to the problem for some use cases, in particular -//! for self-referrential types. Under this model, any type that has an address sensitive state +//! for self-referential types. Under this model, any type that has an address sensitive state //! would ultimately store its data in something like a [`Box`], carefully manage internal //! access to that data to ensure no *moves* or other invalidation occurs, and finally //! provide a safe interface on top. @@ -186,8 +186,8 @@ //! //! Although there were other reason as well, this issue of expensive composition is the key thing //! that drove Rust towards adopting a different model. It is particularly a problem -//! when one considers, for exapmle, the implications of composing together the [`Future`]s which -//! will eventaully make up an asynchronous task (including address-sensitive `async fn` state +//! when one considers, for example, the implications of composing together the [`Future`]s which +//! will eventually make up an asynchronous task (including address-sensitive `async fn` state //! machines). It is plausible that there could be many layers of [`Future`]s composed together, //! including multiple layers of `async fn`s handling different parts of a task. It was deemed //! unacceptable to force indirection and allocation for each layer of composition in this case. @@ -359,7 +359,7 @@ //! Builtin types that are [`Unpin`] include all of the primitive types, like [`bool`], [`i32`], //! and [`f32`], references ([&]T and [&mut] T), etc., as well as many //! core and standard library types like [`Box`], [`String`], and more. -//! These types are marked [`Unpin`] because they do not have an ddress-sensitive state like the +//! These types are marked [`Unpin`] because they do not have an address-sensitive state like the //! ones we discussed above. If they did have such a state, those parts of their interface would be //! unsound without being expressed through pinning, and they would then need to not //! implement [`Unpin`]. @@ -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 -//! [Pin]<[&mut] InnerT>> 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 +//! [Pin]<[&mut] InnerT>> 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 //! [below][structural-pinning]. //! //! # Examples of address-sensitive types @@ -806,7 +806,7 @@ //! //! As a consequence, the struct *must not* be [`#[repr(packed)]`][packed]. //! -//! 3. *Structural Notice of Destruction.* You must uphold the the +//! 3. *Structural Notice of Destruction.* You must uphold the //! [`Drop` guarantee][drop-guarantee]: once your struct is pinned, the struct's storage cannot //! be re-used without calling the structurally-pinned fields' destructors, as well. //! @@ -953,7 +953,7 @@ use crate::{ /// discussed below. /// /// We call such a [`Pin`]-wrapped pointer a **pinning pointer** (or pinning ref, or pinning -/// [`Box`], etc.) because its existince is the thing that is pinning the underlying pointee in +/// [`Box`], etc.) because its existence is the thing that is pinning the underlying pointee in /// place: it is the metaphorical "pin" securing the data in place on the pinboard (in memory). /// /// It is important to stress that the thing in the [`Pin`] is not the value which we want to pin @@ -962,7 +962,7 @@ use crate::{ /// /// The most common set of types which require pinning related guarantees for soundness are the /// compiler-generated state machines that implement [`Future`] for the return value of -/// `async fn`s. These compiler-generated [`Future`]s may contain self-referrential pointers, one +/// `async fn`s. These compiler-generated [`Future`]s may contain self-referential pointers, one /// of the most common use cases for [`Pin`]. More details on this point are provided in the /// [`pin` module] docs, but suffice it to say they require the guarantees provided by pinning to /// be implemented soundly. @@ -1198,7 +1198,7 @@ impl> Pin { /// Unwraps this `Pin`, 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 /// @@ -1809,7 +1809,7 @@ impl DispatchFromDyn> for Pin where Ptr: DispatchFromDyn /// fn coroutine_fn() -> impl Coroutine /* not Unpin */ { /// // Allow coroutine to be self-referential (not `Unpin`) /// // vvvvvv so that locals can cross yield points. -/// static || { +/// #[coroutine] static || { /// let foo = String::from("foo"); /// let foo_ref = &foo; // ------+ /// yield 0; // | <- crosses yield point! diff --git a/library/core/src/prelude/common.rs b/library/core/src/prelude/common.rs new file mode 100644 index 0000000000000..afc6817aa1d24 --- /dev/null +++ b/library/core/src/prelude/common.rs @@ -0,0 +1,105 @@ +//! Items common to the prelude of all editions. +//! +//! See the [module-level documentation](super) for more. + +// Re-exported core operators +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::marker::{Copy, Send, Sized, Sync, Unpin}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; + +// Re-exported functions +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::mem::drop; + +// Re-exported types and traits +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::clone::Clone; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::cmp::{Eq, Ord, PartialEq, PartialOrd}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::convert::{AsMut, AsRef, From, Into}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::default::Default; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::iter::{Extend, IntoIterator, Iterator}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::option::Option::{self, None, Some}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::result::Result::{self, Err, Ok}; + +// Re-exported built-in macros +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use crate::fmt::macros::Debug; +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use crate::hash::macros::Hash; + +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] +#[doc(no_inline)] +pub use crate::{ + assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, +}; + +#[unstable( + feature = "concat_bytes", + issue = "87555", + reason = "`concat_bytes` is not stable enough for use and is subject to change" +)] +#[doc(no_inline)] +pub use crate::concat_bytes; + +// Do not `doc(no_inline)` so that they become doc items on their own +// (no public module for them to be re-exported from). +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +pub use crate::macros::builtin::{ + alloc_error_handler, bench, derive, global_allocator, test, test_case, +}; + +#[unstable(feature = "derive_const", issue = "none")] +pub use crate::macros::builtin::derive_const; + +#[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" +)] +pub use crate::macros::builtin::cfg_accessible; + +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +pub use crate::macros::builtin::cfg_eval; + +#[unstable( + feature = "type_ascription", + issue = "23416", + reason = "placeholder syntax for type ascription" +)] +pub use crate::macros::builtin::type_ascribe; + +#[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" +)] +pub use crate::macros::builtin::deref; diff --git a/library/core/src/prelude/mod.rs b/library/core/src/prelude/mod.rs index b4791c2c022ec..ca33ef160e88b 100644 --- a/library/core/src/prelude/mod.rs +++ b/library/core/src/prelude/mod.rs @@ -6,7 +6,26 @@ #![stable(feature = "core_prelude", since = "1.4.0")] -pub mod v1; +mod common; + +/// The first version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod v1 { + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::common::*; + + // Do not `doc(inline)` these `doc(hidden)` items. + #[unstable( + feature = "rustc_encodable_decodable", + issue = "none", + soft, + reason = "derive macro for `rustc-serialize`; should not be used in new code" + )] + #[allow(deprecated)] + pub use crate::macros::builtin::{RustcDecodable, RustcEncodable}; +} /// The 2015 version of the core prelude. /// @@ -46,14 +65,21 @@ pub mod rust_2021 { pub use crate::convert::{TryFrom, TryInto}; } -/// The 2024 edition of the core prelude. +/// The 2024 version of the core prelude. /// /// See the [module-level documentation](self) for more. #[unstable(feature = "prelude_2024", issue = "121042")] pub mod rust_2024 { - #[unstable(feature = "prelude_2024", issue = "121042")] + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::common::*; + + #[stable(feature = "prelude_2021", since = "1.55.0")] + #[doc(no_inline)] + pub use crate::iter::FromIterator; + + #[stable(feature = "prelude_2021", since = "1.55.0")] #[doc(no_inline)] - pub use super::rust_2021::*; + pub use crate::convert::{TryFrom, TryInto}; #[unstable(feature = "prelude_2024", issue = "121042")] #[doc(no_inline)] diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs deleted file mode 100644 index 10525a16f3a66..0000000000000 --- a/library/core/src/prelude/v1.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! The first version of the core prelude. -//! -//! See the [module-level documentation](super) for more. - -#![stable(feature = "core_prelude", since = "1.4.0")] - -// Re-exported core operators -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::marker::{Copy, Send, Sized, Sync, Unpin}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; - -// Re-exported functions -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::mem::drop; - -// Re-exported types and traits -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::clone::Clone; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::cmp::{Eq, Ord, PartialEq, PartialOrd}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::convert::{AsMut, AsRef, From, Into}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::default::Default; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::iter::{Extend, IntoIterator, Iterator}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::option::Option::{self, None, Some}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::result::Result::{self, Err, Ok}; - -// Re-exported built-in macros -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -pub use crate::fmt::macros::Debug; -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -pub use crate::hash::macros::Hash; - -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -#[doc(no_inline)] -pub use crate::{ - assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, - stringify, trace_macros, -}; - -#[unstable( - feature = "concat_bytes", - issue = "87555", - reason = "`concat_bytes` is not stable enough for use and is subject to change" -)] -#[doc(no_inline)] -pub use crate::concat_bytes; - -// Do not `doc(inline)` these `doc(hidden)` items. -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -pub use crate::macros::builtin::{RustcDecodable, RustcEncodable}; - -// Do not `doc(no_inline)` so that they become doc items on their own -// (no public module for them to be re-exported from). -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -pub use crate::macros::builtin::{ - alloc_error_handler, bench, derive, global_allocator, test, test_case, -}; - -#[unstable(feature = "derive_const", issue = "none")] -pub use crate::macros::builtin::derive_const; - -#[unstable( - feature = "cfg_accessible", - issue = "64797", - reason = "`cfg_accessible` is not fully implemented" -)] -pub use crate::macros::builtin::cfg_accessible; - -#[unstable( - feature = "cfg_eval", - issue = "82679", - reason = "`cfg_eval` is a recently implemented feature" -)] -pub use crate::macros::builtin::cfg_eval; - -#[unstable( - feature = "type_ascription", - issue = "23416", - reason = "placeholder syntax for type ascription" -)] -pub use crate::macros::builtin::type_ascribe; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 15da4171bda21..18a9503cfd2f5 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -268,6 +268,53 @@ mod prim_bool {} /// [`Debug`]: fmt::Debug /// [`default()`]: Default::default /// +/// # Never type fallback +/// +/// When the compiler sees a value of type `!` in a [coercion site], it implicitly inserts a +/// coercion to allow the type checker to infer any type: +/// +/// ```rust,ignore (illustrative-and-has-placeholders) +/// // this +/// let x: u8 = panic!(); +/// +/// // is (essentially) turned by the compiler into +/// let x: u8 = absurd(panic!()); +/// +/// // where absurd is a function with the following signature +/// // (it's sound, because `!` always marks unreachable code): +/// fn absurd(_: !) -> T { ... } +// FIXME: use `core::convert::absurd` here instead, once it's merged +/// ``` +/// +/// This can lead to compilation errors if the type cannot be inferred: +/// +/// ```compile_fail +/// // this +/// { panic!() }; +/// +/// // gets turned into this +/// { absurd(panic!()) }; // error: can't infer the type of `absurd` +/// ``` +/// +/// To prevent such errors, the compiler remembers where it inserted `absurd` calls, and +/// if it can't infer the type, it uses the fallback type instead: +/// ```rust, ignore +/// type Fallback = /* An arbitrarily selected type! */; +/// { absurd::(panic!()) } +/// ``` +/// +/// This is what is known as "never type fallback". +/// +/// Historically, the fallback type was [`()`], causing confusing behavior where `!` spontaneously +/// coerced to `()`, even when it would not infer `()` without the fallback. There are plans to +/// change it in the [2024 edition] (and possibly in all editions on a later date); see +/// [Tracking Issue for making `!` fall back to `!`][fallback-ti]. +/// +/// [coercion site]: +/// [`()`]: prim@unit +/// [fallback-ti]: +/// [2024 edition]: +/// #[unstable(feature = "never_type", issue = "35121")] mod prim_never {} @@ -537,7 +584,11 @@ impl () {} /// ## 4. Get it from C. /// /// ``` -/// # #![feature(rustc_private)] +/// # mod libc { +/// # pub unsafe fn malloc(_size: usize) -> *mut core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() } +/// # pub unsafe fn free(_ptr: *mut core::ffi::c_void) {} +/// # } +/// # #[cfg(any())] /// #[allow(unused_extern_crates)] /// extern crate libc; /// @@ -548,7 +599,7 @@ impl () {} /// if my_num.is_null() { /// panic!("failed to allocate memory"); /// } -/// libc::free(my_num as *mut libc::c_void); +/// libc::free(my_num as *mut core::ffi::c_void); /// } /// ``` /// @@ -1074,7 +1125,22 @@ mod prim_tuple {} #[doc(hidden)] impl (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 +1209,7 @@ impl (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 +1224,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/alignment.rs b/library/core/src/ptr/alignment.rs index 8f44b7eb7c28d..68fce3960c78c 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -1,7 +1,6 @@ -use crate::convert::{TryFrom, TryInto}; -#[cfg(debug_assertions)] -use crate::intrinsics::assert_unsafe_precondition; use crate::num::NonZero; +#[cfg(debug_assertions)] +use crate::ub_checks::assert_unsafe_precondition; use crate::{cmp, fmt, hash, mem, num}; /// A type storing a `usize` which is a power of two, and thus diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index a0c04d3f65dfd..e9eeb3153307f 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -48,12 +48,8 @@ impl *const T { } } - // on bootstrap bump, remove unsafe block - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] - // SAFETY: The two versions are equivalent at runtime. - unsafe { - const_eval_select((self as *const u8,), const_impl, runtime_impl) - } + #[allow(unused_unsafe)] + const_eval_select((self as *const u8,), const_impl, runtime_impl) } /// Casts to a pointer of another type. @@ -140,7 +136,7 @@ impl *const T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `expose_addr` method, or update your code \ + note = "replaced by the `expose_provenance` method, or update your code \ to follow the strict provenance rules using its APIs" )] #[inline(always)] @@ -169,7 +165,7 @@ impl *const T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `ptr::from_exposed_addr` function, or update \ + note = "replaced by the `ptr::with_exposed_provenance` function, or update \ your code to follow the strict provenance rules using its APIs" )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function @@ -191,7 +187,7 @@ impl *const T { /// /// If using those APIs is not possible because there is no way to preserve a pointer with the /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_addr`][pointer::expose_addr] and [`from_exposed_addr`][from_exposed_addr] + /// or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] /// instead. However, note that this makes your code less portable and less amenable to tools /// that check for compliance with the Rust memory model. /// @@ -214,35 +210,35 @@ impl *const T { unsafe { mem::transmute(self.cast::<()>()) } } - /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future - /// use in [`from_exposed_addr`][]. + /// Exposes the "provenance" part of the pointer for future use in + /// [`with_exposed_provenance`][] and returns the "address" portion. /// /// This is equivalent to `self as usize`, which semantically discards *provenance* and /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can - /// later call [`from_exposed_addr`][] to reconstitute the original pointer including its + /// later call [`with_exposed_provenance`][] to reconstitute the original pointer including its /// provenance. (Reconstructing address space information, if required, is your responsibility.) /// /// Using this method means that code is *not* following [Strict /// Provenance][super#strict-provenance] rules. Supporting - /// [`from_exposed_addr`][] complicates specification and reasoning and may not be supported by + /// [`with_exposed_provenance`][] complicates specification and reasoning and may not be supported by /// tools that help you to stay conformant with the Rust memory model, so it is recommended to /// use [`addr`][pointer::addr] wherever possible. /// /// On most platforms this will produce a value with the same bytes as the original pointer, /// because all the bytes are dedicated to describing the address. Platforms which need to store /// additional information in the pointer may not support this operation, since the 'expose' - /// side-effect which is required for [`from_exposed_addr`][] to work is typically not + /// side-effect which is required for [`with_exposed_provenance`][] to work is typically not /// available. /// /// It is unclear whether this method can be given a satisfying unambiguous specification. This /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance]. /// - /// [`from_exposed_addr`]: from_exposed_addr + /// [`with_exposed_provenance`]: with_exposed_provenance #[must_use] #[inline(always)] #[unstable(feature = "exposed_provenance", issue = "95228")] - pub fn expose_addr(self) -> usize { + pub fn expose_provenance(self) -> usize { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. self.cast::<()>() as usize } @@ -818,16 +814,11 @@ impl *const T { true } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] - // on bootstrap bump, remove unsafe block - // SAFETY: This function is only used to provide the same check that the const eval - // interpreter does at runtime. - unsafe { - intrinsics::const_eval_select((this, origin), comptime, runtime) - } + #[allow(unused_unsafe)] + intrinsics::const_eval_select((this, origin), comptime, runtime) } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::sub_ptr requires `self >= origin`", ( @@ -1038,8 +1029,6 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] - // We could always go back to wrapping if unchecked becomes unacceptable - #[rustc_allow_const_fn_unstable(const_int_unchecked_arith)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1412,8 +1401,6 @@ impl *const T { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] - /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] /// struct AlignedI32(i32); @@ -1436,7 +1423,6 @@ impl *const T { /// underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1462,7 +1448,6 @@ impl *const T { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1488,7 +1473,6 @@ impl *const T { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1512,7 +1496,7 @@ impl *const T { /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 #[must_use] #[inline] - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[stable(feature = "pointer_is_aligned", since = "1.79.0")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned(self) -> bool where @@ -1533,7 +1517,7 @@ impl *const T { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] @@ -1562,7 +1546,7 @@ impl *const T { /// cannot be stricter aligned than the reference's underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1587,7 +1571,7 @@ impl *const T { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1611,7 +1595,7 @@ impl *const T { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// const _: () = { @@ -1627,7 +1611,7 @@ impl *const T { /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 #[must_use] #[inline] - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[unstable(feature = "pointer_is_aligned_to", issue = "96284")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned_to(self, align: usize) -> bool { if !align.is_power_of_two() { @@ -1648,11 +1632,7 @@ impl *const T { // The cast to `()` is used to // 1. deal with fat pointers; and // 2. ensure that `align_offset` (in `const_impl`) doesn't actually try to compute an offset. - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: The two versions are equivalent at runtime. - unsafe { - const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl) - } + const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl) } } @@ -1667,16 +1647,15 @@ impl *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 = "1.79.0")] + #[rustc_const_stable(feature = "const_slice_ptr_len", since = "1.79.0")] + #[rustc_allow_const_fn_unstable(ptr_metadata)] pub const fn len(self) -> usize { metadata(self) } @@ -1686,15 +1665,14 @@ impl *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 = "1.79.0")] + #[rustc_const_stable(feature = "const_slice_ptr_len", since = "1.79.0")] pub const fn is_empty(self) -> bool { self.len() == 0 } @@ -1798,6 +1776,46 @@ impl *const [T] { } } +impl *const [T; N] { + /// Returns a raw pointer to the array's buffer. + /// + /// This is equivalent to casting `self` to `*const T`, but more type-safe. + /// + /// # Examples + /// + /// ```rust + /// #![feature(array_ptr_get)] + /// use std::ptr; + /// + /// let arr: *const [i8; 3] = ptr::null(); + /// assert_eq!(arr.as_ptr(), ptr::null()); + /// ``` + #[inline] + #[unstable(feature = "array_ptr_get", issue = "119834")] + #[rustc_const_unstable(feature = "array_ptr_get", issue = "119834")] + pub const fn as_ptr(self) -> *const T { + self as *const T + } + + /// Returns a raw pointer to a slice containing the entire array. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_ptr_get)] + /// + /// let arr: *const [i32; 3] = &[1, 2, 4] as *const [i32; 3]; + /// let slice: *const [i32] = arr.as_slice(); + /// assert_eq!(slice.len(), 3); + /// ``` + #[inline] + #[unstable(feature = "array_ptr_get", issue = "119834")] + #[rustc_const_unstable(feature = "array_ptr_get", issue = "119834")] + pub const fn as_slice(self) -> *const [T] { + self + } +} + // Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *const T { @@ -1830,6 +1848,7 @@ impl Ord for *const T { #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *const T { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn partial_cmp(&self, other: &*const T) -> Option { Some(self.cmp(other)) } diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index fe19f66a31ac4..eb815b6d822c5 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -2,6 +2,8 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; +use crate::intrinsics::aggregate_raw_ptr; +use crate::marker::Freeze; /// Provides the pointer metadata type of any pointed-to type. /// @@ -57,7 +59,7 @@ pub trait Pointee { // NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata` // in `library/core/src/ptr/metadata.rs` // in sync with those here: - type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; + type Metadata: fmt::Debug + Copy + Send + Sync + Ord + Hash + Unpin + Freeze; } /// Pointers to types implementing this trait alias are “thin”. @@ -112,10 +114,7 @@ pub const fn from_raw_parts( data_pointer: *const (), metadata: ::Metadata, ) -> *const T { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr } + aggregate_raw_ptr(data_pointer, metadata) } /// Performs the same functionality as [`from_raw_parts`], except that a @@ -129,10 +128,7 @@ pub const fn from_raw_parts_mut( data_pointer: *mut (), metadata: ::Metadata, ) -> *mut T { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr } + aggregate_raw_ptr(data_pointer, metadata) } #[repr(C)] @@ -258,6 +254,7 @@ impl PartialEq for DynMetadata { impl Ord for DynMetadata { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn cmp(&self, other: &Self) -> crate::cmp::Ordering { (self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable)) } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 1f0204daf7238..5f3815859c8fa 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -340,13 +340,13 @@ //! clear where a satisfying unambiguous semantics can be defined for Exposed Provenance. //! Furthermore, Exposed Provenance will not work (well) with tools like [Miri] and [CHERI]. //! -//! Exposed Provenance is provided by the [`expose_addr`] and [`from_exposed_addr`] methods, which -//! are meant to replace `as` casts between pointers and integers. [`expose_addr`] is a lot like +//! Exposed Provenance is provided by the [`expose_provenance`] and [`with_exposed_provenance`] methods, +//! which are meant to replace `as` casts between pointers and integers. [`expose_provenance`] is a lot like //! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed' //! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but -//! is not materialized in actual executions, except in tools like [Miri].) [`from_exposed_addr`] +//! is not materialized in actual executions, except in tools like [Miri].) [`with_exposed_provenance`] //! can be used to construct a pointer with one of these previously 'exposed' provenances. -//! [`from_exposed_addr`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is +//! [`with_exposed_provenance`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is //! no indication of what the correct provenance for the returned pointer is -- and that is exactly //! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no //! algorithm that decides which provenance will be used. You can think of this as "guessing" the @@ -355,10 +355,10 @@ //! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will //! be used, the program has undefined behavior. //! -//! Using [`expose_addr`] or [`from_exposed_addr`] (or the `as` casts) means that code is +//! Using [`expose_provenance`] or [`with_exposed_provenance`] (or the `as` casts) means that code is //! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to -//! determine how far one can get in Rust without the use of [`expose_addr`] and -//! [`from_exposed_addr`], and to encourage code to be written with Strict Provenance APIs only. +//! determine how far one can get in Rust without the use of [`expose_provenance`] and +//! [`with_exposed_provenance`], and to encourage code to be written with Strict Provenance APIs only. //! Maximizing the amount of such code is a major win for avoiding specification complexity and to //! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the //! confidence in (unsafe) Rust code. @@ -374,8 +374,8 @@ //! [`map_addr`]: pointer::map_addr //! [`addr`]: pointer::addr //! [`ptr::dangling`]: core::ptr::dangling -//! [`expose_addr`]: pointer::expose_addr -//! [`from_exposed_addr`]: from_exposed_addr +//! [`expose_provenance`]: pointer::expose_provenance +//! [`with_exposed_provenance`]: with_exposed_provenance //! [Miri]: https://github.com/rust-lang/miri //! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/ //! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228 @@ -388,10 +388,9 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash; -use crate::intrinsics::{ - self, assert_unsafe_precondition, is_aligned_and_not_null, is_nonoverlapping, -}; +use crate::intrinsics; use crate::marker::FnPtr; +use crate::ub_checks; use crate::mem::{self, align_of, size_of, MaybeUninit}; @@ -582,7 +581,7 @@ pub const fn null_mut() -> *mut T { /// little more than a usize address in disguise. /// /// This is different from `addr as *const T`, which creates a pointer that picks up a previously -/// exposed provenance. See [`from_exposed_addr`] for more details on that operation. +/// exposed provenance. See [`with_exposed_provenance`] for more details on that operation. /// /// This API and its claimed semantics are part of the Strict Provenance experiment, /// see the [module documentation][crate::ptr] for details. @@ -593,7 +592,7 @@ pub const fn null_mut() -> *mut T { pub const fn without_provenance(addr: usize) -> *const T { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as from_exposed_addr. + // is *not* the same as with_exposed_provenance. // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that // pointer). unsafe { mem::transmute(addr) } @@ -626,7 +625,7 @@ pub const fn dangling() -> *const T { /// little more than a usize address in disguise. /// /// This is different from `addr as *mut T`, which creates a pointer that picks up a previously -/// exposed provenance. See [`from_exposed_addr_mut`] for more details on that operation. +/// exposed provenance. See [`with_exposed_provenance_mut`] for more details on that operation. /// /// This API and its claimed semantics are part of the Strict Provenance experiment, /// see the [module documentation][crate::ptr] for details. @@ -637,7 +636,7 @@ pub const fn dangling() -> *const T { pub const fn without_provenance_mut(addr: usize) -> *mut T { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // We use transmute rather than a cast so tools like Miri can tell that this - // is *not* the same as from_exposed_addr. + // is *not* the same as with_exposed_provenance. // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that // pointer). unsafe { mem::transmute(addr) } @@ -664,7 +663,7 @@ pub const fn dangling_mut() -> *mut T { /// /// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the /// returned pointer is that of *any* pointer that was previously exposed by passing it to -/// [`expose_addr`][pointer::expose_addr], or a `ptr as usize` cast. In addition, memory which is +/// [`expose_provenance`][pointer::expose_provenance], or a `ptr as usize` cast. In addition, memory which is /// outside the control of the Rust abstract machine (MMIO registers, for example) is always /// considered to be exposed, so long as this memory is disjoint from memory that will be used by /// the abstract machine such as the stack, heap, and statics. @@ -700,7 +699,7 @@ pub const fn dangling_mut() -> *mut T { #[unstable(feature = "exposed_provenance", issue = "95228")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn from_exposed_addr(addr: usize) -> *const T +pub fn with_exposed_provenance(addr: usize) -> *const T where T: Sized, { @@ -712,7 +711,7 @@ where /// /// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the /// returned pointer is that of *any* pointer that was previously passed to -/// [`expose_addr`][pointer::expose_addr] or a `ptr as usize` cast. If there is no previously +/// [`expose_provenance`][pointer::expose_provenance] or a `ptr as usize` cast. If there is no previously /// 'exposed' provenance that justifies the way this pointer will be used, the program has undefined /// behavior. Note that there is no algorithm that decides which provenance will be used. You can /// think of this as "guessing" the right provenance, and the guess will be "maximally in your @@ -740,7 +739,7 @@ where #[unstable(feature = "exposed_provenance", issue = "95228")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn from_exposed_addr_mut(addr: usize) -> *mut T +pub fn with_exposed_provenance_mut(addr: usize) -> *mut T where T: Sized, { @@ -1019,7 +1018,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { }; } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -1030,9 +1029,9 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { align: usize = align_of::(), count: usize = count, ) => - is_aligned_and_not_null(x, align) - && is_aligned_and_not_null(y, align) - && is_nonoverlapping(x, y, size, count) + ub_checks::is_aligned_and_not_null(x, align) + && ub_checks::is_aligned_and_not_null(y, align) + && ub_checks::is_nonoverlapping(x, y, size, count) ); // Split up the slice into small power-of-two-sized chunks that LLVM is able @@ -1062,11 +1061,26 @@ const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, coun let mut i = 0; while i < count { // SAFETY: By precondition, `i` is in-bounds because it's below `n` - let x = unsafe { &mut *x.add(i) }; + let x = unsafe { x.add(i) }; // SAFETY: By precondition, `i` is in-bounds because it's below `n` // and it's distinct from `x` since the ranges are non-overlapping - let y = unsafe { &mut *y.add(i) }; - mem::swap_simple::>(x, y); + let y = unsafe { y.add(i) }; + + // If we end up here, it's because we're using a simple type -- like + // a small power-of-two-sized thing -- or a special type with particularly + // large alignment, particularly SIMD types. + // Thus we're fine just reading-and-writing it, as either it's small + // and that works well anyway or it's special and the type's author + // presumably wanted things to be done in the larger chunk. + + // SAFETY: we're only ever given pointers that are valid to read/write, + // including being aligned, and nothing here panics so it's drop-safe. + unsafe { + let a: MaybeUninit = read(x); + let b: MaybeUninit = read(y); + write(x, b); + write(y, a); + } i += 1; } @@ -1120,13 +1134,13 @@ pub const unsafe fn replace(dst: *mut T, src: T) -> T { // and cannot overlap `src` since `dst` must point to a distinct // allocated object. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::replace requires that the pointer argument is aligned and non-null", ( addr: *const () = dst as *const (), align: usize = align_of::(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); mem::replace(&mut *dst, src) } @@ -1272,13 +1286,13 @@ pub const unsafe fn read(src: *const T) -> T { // SAFETY: the caller must guarantee that `src` is valid for reads. unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::read requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), align: usize = align_of::(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); crate::intrinsics::read_via_copy(src) } @@ -1481,13 +1495,13 @@ pub const unsafe fn write(dst: *mut T, src: T) { // to `dst` while `src` is owned by this function. unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), align: usize = align_of::(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::write_via_move(dst, src) } @@ -1653,13 +1667,13 @@ pub const unsafe fn write_unaligned(dst: *mut T, src: T) { pub unsafe fn read_volatile(src: *const T) -> T { // SAFETY: the caller must uphold the safety contract for `volatile_load`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::read_volatile requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), align: usize = align_of::(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::volatile_load(src) } @@ -1732,13 +1746,13 @@ pub unsafe fn read_volatile(src: *const T) -> T { pub unsafe fn write_volatile(dst: *mut T, src: T) { // SAFETY: the caller must uphold the safety contract for `volatile_store`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write_volatile requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), align: usize = align_of::(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::volatile_store(dst, src); } @@ -1767,9 +1781,10 @@ pub(crate) const unsafe fn align_offset(p: *const T, a: usize) -> usiz // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= // 1, where the method versions of these operations are not inlined. use intrinsics::{ - assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_shl, - unchecked_shr, unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub, + assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_sub, + wrapping_add, wrapping_mul, wrapping_sub, }; + use intrinsics::{unchecked_shl, unchecked_shr}; /// Calculate multiplicative modular inverse of `x` modulo `m`. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 6a9033a144deb..c5a188dc7d4f2 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -48,11 +48,7 @@ impl *mut T { } } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: The two versions are equivalent at runtime. - unsafe { - const_eval_select((self as *mut u8,), const_impl, runtime_impl) - } + const_eval_select((self as *mut u8,), const_impl, runtime_impl) } /// Casts to a pointer of another type. @@ -146,7 +142,7 @@ impl *mut T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `expose_addr` method, or update your code \ + note = "replaced by the `expose_provenance` method, or update your code \ to follow the strict provenance rules using its APIs" )] #[inline(always)] @@ -175,7 +171,7 @@ impl *mut T { #[unstable(feature = "ptr_to_from_bits", issue = "91126")] #[deprecated( since = "1.67.0", - note = "replaced by the `ptr::from_exposed_addr_mut` function, or \ + note = "replaced by the `ptr::with_exposed_provenance_mut` function, or \ update your code to follow the strict provenance rules using its APIs" )] #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function @@ -198,7 +194,7 @@ impl *mut T { /// /// If using those APIs is not possible because there is no way to preserve a pointer with the /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_addr`][pointer::expose_addr] and [`from_exposed_addr`][from_exposed_addr] + /// or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] /// instead. However, note that this makes your code less portable and less amenable to tools /// that check for compliance with the Rust memory model. /// @@ -221,35 +217,34 @@ impl *mut T { unsafe { mem::transmute(self.cast::<()>()) } } - /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future - /// use in [`from_exposed_addr`][]. + /// Exposes the "provenance" part of the pointer for future use in + /// [`with_exposed_provenance`][] and returns the "address" portion. /// /// This is equivalent to `self as usize`, which semantically discards *provenance* and /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can - /// later call [`from_exposed_addr_mut`][] to reconstitute the original pointer including its + /// later call [`with_exposed_provenance_mut`][] to reconstitute the original pointer including its /// provenance. (Reconstructing address space information, if required, is your responsibility.) /// /// Using this method means that code is *not* following [Strict /// Provenance][super#strict-provenance] rules. Supporting - /// [`from_exposed_addr_mut`][] complicates specification and reasoning and may not be supported + /// [`with_exposed_provenance_mut`][] complicates specification and reasoning and may not be supported /// by tools that help you to stay conformant with the Rust memory model, so it is recommended /// to use [`addr`][pointer::addr] wherever possible. /// /// On most platforms this will produce a value with the same bytes as the original pointer, /// because all the bytes are dedicated to describing the address. Platforms which need to store /// additional information in the pointer may not support this operation, since the 'expose' - /// side-effect which is required for [`from_exposed_addr_mut`][] to work is typically not + /// side-effect which is required for [`with_exposed_provenance_mut`][] to work is typically not /// available. /// /// It is unclear whether this method can be given a satisfying unambiguous specification. This /// API and its claimed semantics are part of [Exposed Provenance][super#exposed-provenance]. /// - /// [`from_exposed_addr_mut`]: from_exposed_addr_mut - #[must_use] + /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[inline(always)] #[unstable(feature = "exposed_provenance", issue = "95228")] - pub fn expose_addr(self) -> usize { + pub fn expose_provenance(self) -> usize { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. self.cast::<()>() as usize } @@ -469,8 +464,8 @@ impl *mut T { /// let ptr: *mut u32 = s.as_mut_ptr(); /// /// unsafe { - /// println!("{}", *ptr.offset(1)); - /// println!("{}", *ptr.offset(2)); + /// assert_eq!(2, *ptr.offset(1)); + /// assert_eq!(3, *ptr.offset(2)); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1032,8 +1027,8 @@ impl *mut T { /// let ptr: *const u8 = s.as_ptr(); /// /// unsafe { - /// println!("{}", *ptr.add(1) as char); - /// println!("{}", *ptr.add(2) as char); + /// assert_eq!('2', *ptr.add(1) as char); + /// assert_eq!('3', *ptr.add(2) as char); /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] @@ -1116,15 +1111,13 @@ impl *mut T { /// /// unsafe { /// let end: *const u8 = s.as_ptr().add(3); - /// println!("{}", *end.sub(1) as char); - /// println!("{}", *end.sub(2) as char); + /// assert_eq!('3', *end.sub(1) as char); + /// assert_eq!('2', *end.sub(2) as char); /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] - // We could always go back to wrapping if unchecked becomes unacceptable - #[rustc_allow_const_fn_unstable(const_int_unchecked_arith)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1666,8 +1659,6 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] - /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] /// struct AlignedI32(i32); @@ -1690,7 +1681,6 @@ impl *mut T { /// underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// #![feature(const_mut_refs)] /// @@ -1717,7 +1707,6 @@ impl *mut T { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1744,7 +1733,6 @@ impl *mut T { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1768,7 +1756,7 @@ impl *mut T { /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 #[must_use] #[inline] - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[stable(feature = "pointer_is_aligned", since = "1.79.0")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned(self) -> bool where @@ -1789,7 +1777,7 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] @@ -1818,7 +1806,7 @@ impl *mut T { /// cannot be stricter aligned than the reference's underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// #![feature(const_mut_refs)] /// @@ -1844,7 +1832,7 @@ impl *mut T { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1869,7 +1857,7 @@ impl *mut T { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// const _: () = { @@ -1885,7 +1873,7 @@ impl *mut T { /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 #[must_use] #[inline] - #[unstable(feature = "pointer_is_aligned", issue = "96284")] + #[unstable(feature = "pointer_is_aligned_to", issue = "96284")] #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned_to(self, align: usize) -> bool { if !align.is_power_of_two() { @@ -1906,11 +1894,7 @@ impl *mut T { // The cast to `()` is used to // 1. deal with fat pointers; and // 2. ensure that `align_offset` (in `const_impl`) doesn't actually try to compute an offset. - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: The two versions are equivalent at runtime. - unsafe { - const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl) - } + const_eval_select((self.cast::<()>(), align), const_impl, runtime_impl) } } @@ -1925,15 +1909,15 @@ impl *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 = "1.79.0")] + #[rustc_const_stable(feature = "const_slice_ptr_len", since = "1.79.0")] + #[rustc_allow_const_fn_unstable(ptr_metadata)] pub const fn len(self) -> usize { metadata(self) } @@ -1943,15 +1927,14 @@ impl *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 = "1.79.0")] + #[rustc_const_stable(feature = "const_slice_ptr_len", since = "1.79.0")] pub const fn is_empty(self) -> bool { self.len() == 0 } @@ -2209,6 +2192,49 @@ impl *mut [T] { } } +impl *mut [T; N] { + /// Returns a raw pointer to the array's buffer. + /// + /// This is equivalent to casting `self` to `*mut T`, but more type-safe. + /// + /// # Examples + /// + /// ```rust + /// #![feature(array_ptr_get)] + /// use std::ptr; + /// + /// let arr: *mut [i8; 3] = ptr::null_mut(); + /// assert_eq!(arr.as_mut_ptr(), ptr::null_mut()); + /// ``` + #[inline] + #[unstable(feature = "array_ptr_get", issue = "119834")] + #[rustc_const_unstable(feature = "array_ptr_get", issue = "119834")] + pub const fn as_mut_ptr(self) -> *mut T { + self as *mut T + } + + /// Returns a raw pointer to a mutable slice containing the entire array. + /// + /// # Examples + /// + /// ``` + /// #![feature(array_ptr_get)] + /// + /// let mut arr = [1, 2, 5]; + /// let ptr: *mut [i32; 3] = &mut arr; + /// unsafe { + /// (&mut *ptr.as_mut_slice())[..2].copy_from_slice(&[3, 4]); + /// } + /// assert_eq!(arr, [3, 4, 5]); + /// ``` + #[inline] + #[unstable(feature = "array_ptr_get", issue = "119834")] + #[rustc_const_unstable(feature = "array_ptr_get", issue = "119834")] + pub const fn as_mut_slice(self) -> *mut [T] { + self + } +} + // Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *mut T { @@ -2240,6 +2266,7 @@ impl Ord for *mut T { #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *mut T { #[inline(always)] + #[allow(ambiguous_wide_pointer_comparisons)] fn partial_cmp(&self, other: &*mut T) -> Option { Some(self.cmp(other)) } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 9c0236c172a93..617890cf083b1 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -2,7 +2,6 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash; use crate::intrinsics; -use crate::intrinsics::assert_unsafe_precondition; use crate::marker::Unsize; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; @@ -10,6 +9,7 @@ use crate::ops::{CoerceUnsized, DispatchFromDyn}; use crate::ptr; use crate::ptr::Unique; use crate::slice::{self, SliceIndex}; +use crate::ub_checks::assert_unsafe_precondition; /// `*mut T` but non-zero and [covariant]. /// @@ -512,7 +512,6 @@ impl NonNull { /// # Examples /// /// ``` - /// #![feature(non_null_convenience)] /// use std::ptr::NonNull; /// /// let mut s = [1, 2, 3]; @@ -523,12 +522,12 @@ impl NonNull { /// println!("{}", ptr.offset(2).read()); /// } /// ``` - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - #[must_use = "returns a new pointer rather than modifying its argument"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub const unsafe fn offset(self, count: isize) -> NonNull + #[must_use = "returns a new pointer rather than modifying its argument"] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + pub const unsafe fn offset(self, count: isize) -> Self where T: Sized, { @@ -549,11 +548,11 @@ impl NonNull { /// /// For non-`Sized` pointees this operation changes only the data pointer, /// leaving the metadata untouched. - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[must_use] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset` and `byte_offset` has // the same safety contract. @@ -599,7 +598,6 @@ impl NonNull { /// # Examples /// /// ``` - /// #![feature(non_null_convenience)] /// use std::ptr::NonNull; /// /// let s: &str = "123"; @@ -610,11 +608,11 @@ impl NonNull { /// println!("{}", ptr.add(2).read() as char); /// } /// ``` - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - #[must_use = "returns a new pointer rather than modifying its argument"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[must_use = "returns a new pointer rather than modifying its argument"] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -636,12 +634,12 @@ impl NonNull { /// /// For non-`Sized` pointees this operation changes only the data pointer, /// leaving the metadata untouched. - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[must_use] #[inline(always)] - #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_allow_const_fn_unstable(set_ptr_value)] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add` and `byte_add` has the same // safety contract. @@ -688,7 +686,6 @@ impl NonNull { /// # Examples /// /// ``` - /// #![feature(non_null_convenience)] /// use std::ptr::NonNull; /// /// let s: &str = "123"; @@ -699,13 +696,11 @@ impl NonNull { /// println!("{}", end.sub(2).read() as char); /// } /// ``` - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - #[must_use = "returns a new pointer rather than modifying its argument"] - // We could always go back to wrapping if unchecked becomes unacceptable - #[rustc_allow_const_fn_unstable(const_int_unchecked_arith)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[must_use = "returns a new pointer rather than modifying its argument"] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, @@ -732,12 +727,12 @@ impl NonNull { /// /// For non-`Sized` pointees this operation changes only the data pointer, /// leaving the metadata untouched. - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[must_use] #[inline(always)] - #[rustc_allow_const_fn_unstable(set_ptr_value)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_allow_const_fn_unstable(set_ptr_value)] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub` and `byte_sub` has the same // safety contract. @@ -818,7 +813,6 @@ impl NonNull { /// Basic usage: /// /// ``` - /// #![feature(non_null_convenience)] /// use std::ptr::NonNull; /// /// let a = [0; 5]; @@ -835,7 +829,7 @@ impl NonNull { /// *Incorrect* usage: /// /// ```rust,no_run - /// #![feature(non_null_convenience, strict_provenance)] + /// #![feature(strict_provenance)] /// use std::ptr::NonNull; /// /// let ptr1 = NonNull::new(Box::into_raw(Box::new(0u8))).unwrap(); @@ -847,14 +841,13 @@ impl NonNull { /// // Since ptr2_other and ptr2 are derived from pointers to different objects, /// // computing their offset is undefined behavior, even though /// // they point to the same address! - /// unsafe { - /// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior - /// } + /// + /// let zero = unsafe { ptr2_other.offset_from(ptr2) }; // Undefined Behavior /// ``` - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn offset_from(self, origin: NonNull) -> isize where T: Sized, @@ -872,10 +865,10 @@ impl NonNull { /// /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn byte_offset_from(self, origin: NonNull) -> isize { // SAFETY: the caller must uphold the safety contract for `byte_offset_from`. unsafe { self.pointer.byte_offset_from(origin.pointer) } @@ -899,7 +892,7 @@ impl NonNull { /// to [`sub`](#method.sub)). The following are all equivalent, assuming /// that their safety preconditions are met: /// ```rust - /// # #![feature(non_null_convenience)] + /// # #![feature(ptr_sub_ptr)] /// # unsafe fn blah(ptr: std::ptr::NonNull, origin: std::ptr::NonNull, count: usize) -> bool { /// ptr.sub_ptr(origin) == count /// # && @@ -928,7 +921,7 @@ impl NonNull { /// # Examples /// /// ``` - /// #![feature(non_null_convenience)] + /// #![feature(ptr_sub_ptr)] /// use std::ptr::NonNull; /// /// let a = [0; 5]; @@ -944,12 +937,10 @@ impl NonNull { /// // This would be incorrect, as the pointers are not correctly ordered: /// // ptr1.sub_ptr(ptr2) /// ``` - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - // #[unstable(feature = "ptr_sub_ptr", issue = "95892")] - // #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[unstable(feature = "ptr_sub_ptr", issue = "95892")] + #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")] pub const unsafe fn sub_ptr(self, subtracted: NonNull) -> usize where T: Sized, @@ -964,10 +955,10 @@ impl NonNull { /// See [`ptr::read`] for safety concerns and examples. /// /// [`ptr::read`]: crate::ptr::read() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn read(self) -> T where T: Sized, @@ -986,9 +977,9 @@ impl NonNull { /// See [`ptr::read_volatile`] for safety concerns and examples. /// /// [`ptr::read_volatile`]: crate::ptr::read_volatile() - #[unstable(feature = "non_null_convenience", issue = "117691")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub unsafe fn read_volatile(self) -> T where T: Sized, @@ -1005,10 +996,10 @@ impl NonNull { /// See [`ptr::read_unaligned`] for safety concerns and examples. /// /// [`ptr::read_unaligned`]: crate::ptr::read_unaligned() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn read_unaligned(self) -> T where T: Sized, @@ -1025,10 +1016,10 @@ impl NonNull { /// See [`ptr::copy`] for safety concerns and examples. /// /// [`ptr::copy`]: crate::ptr::copy() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_to(self, dest: NonNull, count: usize) where T: Sized, @@ -1045,10 +1036,10 @@ impl NonNull { /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. /// /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_to_nonoverlapping(self, dest: NonNull, count: usize) where T: Sized, @@ -1065,10 +1056,10 @@ impl NonNull { /// See [`ptr::copy`] for safety concerns and examples. /// /// [`ptr::copy`]: crate::ptr::copy() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_from(self, src: NonNull, count: usize) where T: Sized, @@ -1085,10 +1076,10 @@ impl NonNull { /// See [`ptr::copy_nonoverlapping`] for safety concerns and examples. /// /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_from_nonoverlapping(self, src: NonNull, count: usize) where T: Sized, @@ -1102,8 +1093,8 @@ impl NonNull { /// See [`ptr::drop_in_place`] for safety concerns and examples. /// /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() - #[unstable(feature = "non_null_convenience", issue = "117691")] #[inline(always)] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub unsafe fn drop_in_place(self) { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { ptr::drop_in_place(self.as_ptr()) } @@ -1115,11 +1106,10 @@ impl NonNull { /// See [`ptr::write`] for safety concerns and examples. /// /// [`ptr::write`]: crate::ptr::write() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - //#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write(self, val: T) where T: Sized, @@ -1134,12 +1124,11 @@ impl NonNull { /// See [`ptr::write_bytes`] for safety concerns and examples. /// /// [`ptr::write_bytes`]: crate::ptr::write_bytes() - #[doc(alias = "memset")] - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - //#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] #[inline(always)] + #[doc(alias = "memset")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write_bytes(self, val: u8, count: usize) where T: Sized, @@ -1158,9 +1147,9 @@ impl NonNull { /// See [`ptr::write_volatile`] for safety concerns and examples. /// /// [`ptr::write_volatile`]: crate::ptr::write_volatile() - #[unstable(feature = "non_null_convenience", issue = "117691")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub unsafe fn write_volatile(self, val: T) where T: Sized, @@ -1177,11 +1166,10 @@ impl NonNull { /// See [`ptr::write_unaligned`] for safety concerns and examples. /// /// [`ptr::write_unaligned`]: crate::ptr::write_unaligned() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - //#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write_unaligned(self, val: T) where T: Sized, @@ -1196,8 +1184,8 @@ impl NonNull { /// See [`ptr::replace`] for safety concerns and examples. /// /// [`ptr::replace`]: crate::ptr::replace() - #[unstable(feature = "non_null_convenience", issue = "117691")] #[inline(always)] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] pub unsafe fn replace(self, src: T) -> T where T: Sized, @@ -1213,10 +1201,9 @@ impl NonNull { /// See [`ptr::swap`] for safety concerns and examples. /// /// [`ptr::swap`]: crate::ptr::swap() - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - //#[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[inline(always)] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap(self, with: NonNull) where T: Sized, @@ -1248,7 +1235,6 @@ impl NonNull { /// Accessing adjacent `u8` as `u16` /// /// ``` - /// #![feature(non_null_convenience)] /// use std::mem::align_of; /// use std::ptr::NonNull; /// @@ -1266,11 +1252,10 @@ impl NonNull { /// } /// # } /// ``` - #[unstable(feature = "non_null_convenience", issue = "117691")] - #[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")] - //#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")] - #[must_use] #[inline] + #[must_use] + #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")] pub const fn align_offset(self, align: usize) -> usize where T: Sized, @@ -1290,7 +1275,6 @@ impl NonNull { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] /// use std::ptr::NonNull; /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1315,11 +1299,9 @@ impl NonNull { /// underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] - /// #![feature(const_pointer_is_aligned)] - /// #![feature(non_null_convenience)] - /// #![feature(const_option)] /// #![feature(const_nonnull_new)] + /// #![feature(const_option)] + /// #![feature(const_pointer_is_aligned)] /// use std::ptr::NonNull; /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1345,7 +1327,6 @@ impl NonNull { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of primitives is less than their size. @@ -1371,7 +1352,6 @@ impl NonNull { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] /// #![feature(const_pointer_is_aligned)] /// #![feature(const_option)] /// #![feature(const_nonnull_new)] @@ -1396,10 +1376,10 @@ impl NonNull { /// ``` /// /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 - #[unstable(feature = "pointer_is_aligned", issue = "96284")] - #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] - #[must_use] #[inline] + #[must_use] + #[stable(feature = "pointer_is_aligned", since = "1.79.0")] + #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned(self) -> bool where T: Sized, @@ -1419,7 +1399,7 @@ impl NonNull { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// /// // On some platforms, the alignment of i32 is less than 4. /// #[repr(align(4))] @@ -1448,7 +1428,7 @@ impl NonNull { /// cannot be stricter aligned than the reference's underlying allocation. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1473,7 +1453,7 @@ impl NonNull { /// pointer is aligned, even if the compiletime pointer wasn't aligned. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// // On some platforms, the alignment of i32 is less than 4. @@ -1497,7 +1477,7 @@ impl NonNull { /// runtime and compiletime. /// /// ``` - /// #![feature(pointer_is_aligned)] + /// #![feature(pointer_is_aligned_to)] /// #![feature(const_pointer_is_aligned)] /// /// const _: () = { @@ -1511,10 +1491,10 @@ impl NonNull { /// ``` /// /// [tracking issue]: https://github.com/rust-lang/rust/issues/104203 - #[unstable(feature = "pointer_is_aligned", issue = "96284")] - #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] - #[must_use] #[inline] + #[must_use] + #[unstable(feature = "pointer_is_aligned_to", issue = "96284")] + #[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")] pub const fn is_aligned_to(self, align: usize) -> bool { self.pointer.is_aligned_to(align) } @@ -1568,13 +1548,30 @@ impl 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 { self.as_ptr().len() } + /// Returns `true` if the non-null raw slice has a length of 0. + /// + /// # Examples + /// + /// ```rust + /// use std::ptr::NonNull; + /// + /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); + /// assert!(!slice.is_empty()); + /// ``` + #[stable(feature = "slice_ptr_is_empty_nonnull", since = "1.79.0")] + #[rustc_const_stable(feature = "const_slice_ptr_is_empty_nonnull", since = "1.79.0")] + #[must_use] + #[inline] + pub const fn is_empty(self) -> bool { + self.len() == 0 + } + /// Returns a non-null pointer to the slice's buffer. /// /// # Examples @@ -1802,6 +1799,7 @@ impl PartialEq for NonNull { #[stable(feature = "nonnull", since = "1.25.0")] impl Ord for NonNull { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn cmp(&self, other: &Self) -> Ordering { self.as_ptr().cmp(&other.as_ptr()) } @@ -1810,6 +1808,7 @@ impl Ord for NonNull { #[stable(feature = "nonnull", since = "1.25.0")] impl PartialOrd for NonNull { #[inline] + #[allow(ambiguous_wide_pointer_comparisons)] fn partial_cmp(&self, other: &Self) -> Option { self.as_ptr().partial_cmp(&other.as_ptr()) } diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 6879ac03f70bc..b2b627fe6a9cc 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -488,7 +488,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; +use crate::iter::{self, FusedIterator, TrustedLen}; use crate::ops::{self, ControlFlow, Deref, DerefMut}; use crate::{convert, fmt, hint}; diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 64f1f360821da..8d7b6165510a8 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,10 +1,10 @@ //! Indexing implementations for `[T]`. -use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::const_eval_select; use crate::intrinsics::unchecked_sub; use crate::ops; use crate::ptr; +use crate::ub_checks::assert_unsafe_precondition; #[stable(feature = "rust1", since = "1.0.0")] impl ops::Index for [T] @@ -35,15 +35,7 @@ where #[track_caller] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] const fn slice_start_index_len_fail(index: usize, len: usize) -> ! { - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: we are just panicking here - unsafe { - const_eval_select( - (index, len), - slice_start_index_len_fail_ct, - slice_start_index_len_fail_rt, - ) - } + const_eval_select((index, len), slice_start_index_len_fail_ct, slice_start_index_len_fail_rt) } // FIXME const-hack @@ -64,11 +56,7 @@ const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! { #[track_caller] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] const fn slice_end_index_len_fail(index: usize, len: usize) -> ! { - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: we are just panicking here - unsafe { - const_eval_select((index, len), slice_end_index_len_fail_ct, slice_end_index_len_fail_rt) - } + const_eval_select((index, len), slice_end_index_len_fail_ct, slice_end_index_len_fail_rt) } // FIXME const-hack @@ -89,11 +77,7 @@ const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! { #[track_caller] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] const fn slice_index_order_fail(index: usize, end: usize) -> ! { - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: we are just panicking here - unsafe { - const_eval_select((index, end), slice_index_order_fail_ct, slice_index_order_fail_rt) - } + const_eval_select((index, end), slice_index_order_fail_ct, slice_index_order_fail_rt) } // FIXME const-hack @@ -211,6 +195,7 @@ pub unsafe trait SliceIndex: private_slice_index::Sealed { fn index_mut(self, slice: &mut T) -> &mut Self::Output; } +/// The methods `index` and `index_mut` panic if the index is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for usize { @@ -344,6 +329,9 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { } } +/// The methods `index` and `index_mut` panic if: +/// - the start of the range is greater than the end of the range or +/// - the end of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::Range { @@ -432,6 +420,7 @@ unsafe impl SliceIndex<[T]> for ops::Range { } } +/// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeTo { @@ -470,6 +459,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeTo { } } +/// The methods `index` and `index_mut` panic if the start of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeFrom { @@ -552,6 +542,10 @@ unsafe impl SliceIndex<[T]> for ops::RangeFull { } } +/// The methods `index` and `index_mut` panic if: +/// - the end of the range is `usize::MAX` or +/// - the start of the range is greater than the end of the range or +/// - the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeInclusive { @@ -596,6 +590,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { } } +/// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeToInclusive { diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 7910981d0f5ee..0b8ff5cc01242 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -70,21 +70,19 @@ macro_rules! iterator { $into_ref:ident, {$($extra:tt)*} ) => { - // Returns the first element and moves the start of the iterator forwards by 1. - // Greatly improves performance compared to an inlined function. The iterator - // must not be empty. - macro_rules! next_unchecked { - ($self: ident) => { $self.post_inc_start(1).$into_ref() } - } - - // Returns the last element and moves the end of the iterator backwards by 1. - // Greatly improves performance compared to an inlined function. The iterator - // must not be empty. - macro_rules! next_back_unchecked { - ($self: ident) => { $self.pre_dec_end(1).$into_ref() } - } - impl<'a, T> $name<'a, T> { + /// Returns the last element and moves the end of the iterator backwards by 1. + /// + /// # Safety + /// + /// The iterator must not be empty + #[inline] + unsafe fn next_back_unchecked(&mut self) -> $elem { + // SAFETY: the caller promised it's not empty, so + // the offsetting is in-bounds and there's an element to return. + unsafe { self.pre_dec_end(1).$into_ref() } + } + // Helper function for creating a slice from the iterator. #[inline(always)] fn make_slice(&self) -> &'a [T] { @@ -156,13 +154,13 @@ macro_rules! iterator { fn next(&mut self) -> Option<$elem> { // could be implemented with slices, but this avoids bounds checks - // SAFETY: The call to `next_unchecked!` is + // SAFETY: The call to `next_unchecked` is // safe since we check if the iterator is empty first. unsafe { if is_empty!(self) { None } else { - Some(next_unchecked!(self)) + Some(self.next_unchecked()) } } } @@ -191,7 +189,7 @@ macro_rules! iterator { // SAFETY: We are in bounds. `post_inc_start` does the right thing even for ZSTs. unsafe { self.post_inc_start(n); - Some(next_unchecked!(self)) + Some(self.next_unchecked()) } } @@ -392,13 +390,13 @@ macro_rules! iterator { fn next_back(&mut self) -> Option<$elem> { // could be implemented with slices, but this avoids bounds checks - // SAFETY: The call to `next_back_unchecked!` + // SAFETY: The call to `next_back_unchecked` // is safe since we check if the iterator is empty first. unsafe { if is_empty!(self) { None } else { - Some(next_back_unchecked!(self)) + Some(self.next_back_unchecked()) } } } @@ -416,7 +414,7 @@ macro_rules! iterator { // SAFETY: We are in bounds. `pre_dec_end` does the right thing even for ZSTs. unsafe { self.pre_dec_end(n); - Some(next_back_unchecked!(self)) + Some(self.next_back_unchecked()) } } @@ -436,10 +434,11 @@ macro_rules! iterator { unsafe impl TrustedLen for $name<'_, T> {} impl<'a, T> UncheckedIterator for $name<'a, T> { + #[inline] unsafe fn next_unchecked(&mut self) -> $elem { // SAFETY: The caller promised there's at least one more item. unsafe { - next_unchecked!(self) + self.post_inc_start(1).$into_ref() } } } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 4a574bf034745..9c523fd6295d7 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -9,7 +9,6 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::fmt; use crate::hint; -use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::exact_div; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; @@ -17,6 +16,7 @@ use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; use crate::ptr; use crate::simd::{self, Simd}; use crate::slice; +use crate::ub_checks::assert_unsafe_precondition; #[unstable( feature = "slice_internals", @@ -1944,8 +1944,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_at_unchecked)] - /// /// let v = [1, 2, 3, 4, 5, 6]; /// /// unsafe { @@ -1966,7 +1964,7 @@ impl [T] { /// assert_eq!(right, []); /// } /// ``` - #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[stable(feature = "slice_split_at_unchecked", since = "1.79.0")] #[rustc_const_stable(feature = "const_slice_split_at_unchecked", since = "1.77.0")] #[inline] #[must_use] @@ -2008,8 +2006,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_at_unchecked)] - /// /// let mut v = [1, 0, 3, 0, 5, 6]; /// // scoped to restrict the lifetime of the borrows /// unsafe { @@ -2021,7 +2017,7 @@ impl [T] { /// } /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); /// ``` - #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[stable(feature = "slice_split_at_unchecked", since = "1.79.0")] #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] #[inline] #[must_use] @@ -2728,8 +2724,10 @@ impl [T] { /// ``` /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; /// let num = 42; - /// let idx = s.partition_point(|&x| x < num); - /// // The above is equivalent to `let idx = s.binary_search(&num).unwrap_or_else(|x| x);` + /// let idx = s.partition_point(|&x| x <= num); + /// // If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to + /// // `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` will allow `insert` + /// // to shift less elements. /// s.insert(idx, num); /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` @@ -4179,7 +4177,7 @@ impl [T] { /// ``` /// let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; /// let num = 42; - /// let idx = s.partition_point(|&x| x < num); + /// let idx = s.partition_point(|&x| x <= num); /// s.insert(idx, num); /// assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 2199614ce27e4..29a12f106c5ed 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -1,12 +1,10 @@ //! Free functions to create `&[T]` and `&mut [T]`. use crate::array; -use crate::intrinsics::{ - assert_unsafe_precondition, is_aligned_and_not_null, is_valid_allocation_size, -}; use crate::mem::{align_of, size_of}; use crate::ops::Range; use crate::ptr; +use crate::ub_checks; /// Forms a slice from a pointer and a length. /// @@ -95,7 +93,7 @@ use crate::ptr; pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( @@ -104,8 +102,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] align: usize = align_of::(), len: usize = len, ) => - is_aligned_and_not_null(data, align) - && is_valid_allocation_size(size, len) + ub_checks::is_aligned_and_not_null(data, align) + && ub_checks::is_valid_allocation_size(size, len) ); &*ptr::slice_from_raw_parts(data, len) } @@ -149,7 +147,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( @@ -158,8 +156,8 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m align: usize = align_of::(), len: usize = len, ) => - is_aligned_and_not_null(data, align) - && is_valid_allocation_size(size, len) + ub_checks::is_aligned_and_not_null(data, align) + && ub_checks::is_valid_allocation_size(size, len) ); &mut *ptr::slice_from_raw_parts_mut(data, len) } diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index ba282f09d2067..b6ffb0a608d05 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -239,7 +239,7 @@ pub const unsafe fn from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str { #[must_use] #[unstable(feature = "str_from_raw_parts", issue = "119206")] #[rustc_const_unstable(feature = "const_str_from_raw_parts_mut", issue = "119206")] -pub const unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a str { +pub const unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut str { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { &mut *ptr::from_raw_parts_mut(ptr.cast(), len) } } diff --git a/library/core/src/str/lossy.rs b/library/core/src/str/lossy.rs index 59f873d1268ce..51a0777c2d613 100644 --- a/library/core/src/str/lossy.rs +++ b/library/core/src/str/lossy.rs @@ -6,6 +6,46 @@ use crate::iter::FusedIterator; use super::from_utf8_unchecked; use super::validations::utf8_char_width; +impl [u8] { + /// Creates an iterator over the contiguous valid UTF-8 ranges of this + /// slice, and the non-UTF-8 fragments in between. + /// + /// # Examples + /// + /// This function formats arbitrary but mostly-UTF-8 bytes into Rust source + /// code in the form of a C-string literal (`c"..."`). + /// + /// ``` + /// use std::fmt::Write as _; + /// + /// pub fn cstr_literal(bytes: &[u8]) -> String { + /// let mut repr = String::new(); + /// repr.push_str("c\""); + /// for chunk in bytes.utf8_chunks() { + /// for ch in chunk.valid().chars() { + /// // Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for non-printable characters. + /// write!(repr, "{}", ch.escape_debug()).unwrap(); + /// } + /// for byte in chunk.invalid() { + /// write!(repr, "\\x{:02X}", byte).unwrap(); + /// } + /// } + /// repr.push('"'); + /// repr + /// } + /// + /// fn main() { + /// let lit = cstr_literal(b"\xferris the \xf0\x9f\xa6\x80\x07"); + /// let expected = stringify!(c"\xFErris the 🦀\u{7}"); + /// assert_eq!(lit, expected); + /// } + /// ``` + #[stable(feature = "utf8_chunks", since = "1.79.0")] + pub fn utf8_chunks(&self) -> Utf8Chunks<'_> { + Utf8Chunks { source: self } + } +} + /// An item returned by the [`Utf8Chunks`] iterator. /// /// A `Utf8Chunk` stores a sequence of [`u8`] up to the first broken character @@ -14,15 +54,11 @@ use super::validations::utf8_char_width; /// # Examples /// /// ``` -/// #![feature(utf8_chunks)] -/// -/// use std::str::Utf8Chunks; -/// /// // An invalid UTF-8 string /// let bytes = b"foo\xF1\x80bar"; /// /// // Decode the first `Utf8Chunk` -/// let chunk = Utf8Chunks::new(bytes).next().unwrap(); +/// let chunk = bytes.utf8_chunks().next().unwrap(); /// /// // The first three characters are valid UTF-8 /// assert_eq!("foo", chunk.valid()); @@ -30,7 +66,7 @@ use super::validations::utf8_char_width; /// // The fourth character is broken /// assert_eq!(b"\xF1\x80", chunk.invalid()); /// ``` -#[unstable(feature = "utf8_chunks", issue = "99543")] +#[stable(feature = "utf8_chunks", since = "1.79.0")] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Utf8Chunk<'a> { valid: &'a str, @@ -43,7 +79,7 @@ impl<'a> Utf8Chunk<'a> { /// This substring can be empty at the start of the string or between /// broken UTF-8 characters. #[must_use] - #[unstable(feature = "utf8_chunks", issue = "99543")] + #[stable(feature = "utf8_chunks", since = "1.79.0")] pub fn valid(&self) -> &'a str { self.valid } @@ -63,7 +99,7 @@ impl<'a> Utf8Chunk<'a> { /// [`valid`]: Self::valid /// [`U+FFFD REPLACEMENT CHARACTER`]: crate::char::REPLACEMENT_CHARACTER #[must_use] - #[unstable(feature = "utf8_chunks", issue = "99543")] + #[stable(feature = "utf8_chunks", since = "1.79.0")] pub fn invalid(&self) -> &'a [u8] { self.invalid } @@ -78,7 +114,7 @@ impl fmt::Debug for Debug<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_char('"')?; - for chunk in Utf8Chunks::new(self.0) { + for chunk in self.0.utf8_chunks() { // Valid part. // Here we partially parse UTF-8 again which is suboptimal. { @@ -123,12 +159,8 @@ impl fmt::Debug for Debug<'_> { /// [`String::from_utf8_lossy`] without allocating heap memory: /// /// ``` -/// #![feature(utf8_chunks)] -/// -/// use std::str::Utf8Chunks; -/// /// fn from_utf8_lossy(input: &[u8], mut push: F) where F: FnMut(&str) { -/// for chunk in Utf8Chunks::new(input) { +/// for chunk in input.utf8_chunks() { /// push(chunk.valid()); /// /// if !chunk.invalid().is_empty() { @@ -140,19 +172,13 @@ impl fmt::Debug for Debug<'_> { /// /// [`String::from_utf8_lossy`]: ../../std/string/struct.String.html#method.from_utf8_lossy #[must_use = "iterators are lazy and do nothing unless consumed"] -#[unstable(feature = "utf8_chunks", issue = "99543")] +#[stable(feature = "utf8_chunks", since = "1.79.0")] #[derive(Clone)] pub struct Utf8Chunks<'a> { source: &'a [u8], } impl<'a> Utf8Chunks<'a> { - /// Creates a new iterator to decode the bytes. - #[unstable(feature = "utf8_chunks", issue = "99543")] - pub fn new(bytes: &'a [u8]) -> Self { - Self { source: bytes } - } - #[doc(hidden)] #[unstable(feature = "str_internals", issue = "none")] pub fn debug(&self) -> Debug<'_> { @@ -160,7 +186,7 @@ impl<'a> Utf8Chunks<'a> { } } -#[unstable(feature = "utf8_chunks", issue = "99543")] +#[stable(feature = "utf8_chunks", since = "1.79.0")] impl<'a> Iterator for Utf8Chunks<'a> { type Item = Utf8Chunk<'a>; @@ -259,10 +285,10 @@ impl<'a> Iterator for Utf8Chunks<'a> { } } -#[unstable(feature = "utf8_chunks", issue = "99543")] +#[stable(feature = "utf8_chunks", since = "1.79.0")] impl FusedIterator for Utf8Chunks<'_> {} -#[unstable(feature = "utf8_chunks", issue = "99543")] +#[stable(feature = "utf8_chunks", since = "1.79.0")] impl fmt::Debug for Utf8Chunks<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("Utf8Chunks").field("source", &self.debug()).finish() diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 4943bbc45d07a..adccbe02d46d4 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -24,7 +24,7 @@ use crate::slice::{self, SliceIndex}; pub mod pattern; mod lossy; -#[unstable(feature = "utf8_chunks", issue = "99543")] +#[stable(feature = "utf8_chunks", since = "1.79.0")] pub use lossy::{Utf8Chunk, Utf8Chunks}; #[stable(feature = "rust1", since = "1.0.0")] @@ -86,15 +86,7 @@ use iter::{MatchesInternal, SplitNInternal}; #[rustc_allow_const_fn_unstable(const_eval_select)] #[cfg(not(feature = "panic_immediate_abort"))] const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: panics for both branches - unsafe { - crate::intrinsics::const_eval_select( - (s, begin, end), - slice_error_fail_ct, - slice_error_fail_rt, - ) - } + crate::intrinsics::const_eval_select((s, begin, end), slice_error_fail_ct, slice_error_fail_rt) } #[cfg(feature = "panic_immediate_abort")] diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index ec81fd095d530..ba2d6f644962e 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -1,10 +1,11 @@ //! Trait implementations for `str`. use crate::cmp::Ordering; -use crate::intrinsics::assert_unsafe_precondition; +use crate::intrinsics::unchecked_sub; use crate::ops; use crate::ptr; use crate::slice::SliceIndex; +use crate::ub_checks::assert_unsafe_precondition; use super::ParseBoolError; @@ -210,9 +211,10 @@ unsafe impl SliceIndex for ops::Range { // SAFETY: the caller guarantees that `self` is in bounds of `slice` // which satisfies all the conditions for `add`. - let ptr = unsafe { slice.as_ptr().add(self.start) }; - let len = self.end - self.start; - ptr::slice_from_raw_parts(ptr, len) as *const str + unsafe { + let new_len = unchecked_sub(self.end, self.start); + ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len) as *const str + } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { @@ -229,9 +231,10 @@ unsafe impl SliceIndex for ops::Range { ); // SAFETY: see comments for `get_unchecked`. - let ptr = unsafe { slice.as_mut_ptr().add(self.start) }; - let len = self.end - self.start; - ptr::slice_from_raw_parts_mut(ptr, len) as *mut str + unsafe { + let new_len = unchecked_sub(self.end, self.start); + ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len) as *mut str + } } #[inline] fn index(self, slice: &str) -> &Self::Output { diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 00e75ec9a2540..232ec589093d3 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -243,7 +243,7 @@ const EMULATE_ATOMIC_BOOL: bool = /// A boolean type which can be safely shared between threads. /// -/// This type has the same in-memory representation as a [`bool`]. +/// This type has the same size, alignment, and bit validity as a [`bool`]. /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of `u8`. @@ -272,7 +272,7 @@ unsafe impl Sync for AtomicBool {} /// A raw pointer type which can be safely shared between threads. /// -/// This type has the same in-memory representation as a `*mut T`. +/// This type has the same size and bit validity as a `*mut T`. /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of pointers. Its size depends on the target pointer's size. @@ -418,14 +418,12 @@ impl AtomicBool { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] /// use std::sync::atomic::{self, AtomicBool}; - /// use std::mem::align_of; /// /// // Get a pointer to an allocated value /// let ptr: *mut bool = Box::into_raw(Box::new(false)); /// - /// assert!(ptr.is_aligned_to(align_of::())); + /// assert!(ptr.cast::().is_aligned()); /// /// { /// // Create an atomic view of the allocated value @@ -513,7 +511,7 @@ impl AtomicBool { /// # Examples /// /// ``` - /// #![feature(atomic_from_mut, inline_const)] + /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let mut some_bools = [const { AtomicBool::new(false) }; 10]; @@ -580,9 +578,9 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "atomic_access", since = "1.15.0")] - #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + #[rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0")] pub const fn into_inner(self) -> bool { - self.v.into_inner() != 0 + self.v.primitive_into_inner() != 0 } /// Loads a value from the bool. @@ -1216,14 +1214,12 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] /// use std::sync::atomic::{self, AtomicPtr}; - /// use std::mem::align_of; /// /// // Get a pointer to an allocated value /// let ptr: *mut *mut u8 = Box::into_raw(Box::new(std::ptr::null_mut())); /// - /// assert!(ptr.is_aligned_to(align_of::>())); + /// assert!(ptr.cast::>().is_aligned()); /// /// { /// // Create an atomic view of the allocated value @@ -1317,7 +1313,7 @@ impl AtomicPtr { /// # Examples /// /// ``` - /// #![feature(atomic_from_mut, inline_const)] + /// #![feature(atomic_from_mut)] /// use std::ptr::null_mut; /// use std::sync::atomic::{AtomicPtr, Ordering}; /// @@ -1401,9 +1397,9 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "atomic_access", since = "1.15.0")] - #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + #[rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0")] pub const fn into_inner(self) -> *mut T { - self.p.into_inner() + self.p.primitive_into_inner() } /// Loads a value from the pointer. @@ -2121,7 +2117,7 @@ macro_rules! atomic_int { $int_type:ident $atomic_type:ident) => { /// An integer type which can be safely shared between threads. /// - /// This type has the same in-memory representation as the underlying + /// This type has the same size and bit validity as the underlying /// integer type, [` #[doc = $s_int_type] /// `]. @@ -2199,14 +2195,12 @@ macro_rules! atomic_int { /// # Examples /// /// ``` - /// #![feature(pointer_is_aligned)] #[doc = concat!($extra_feature, "use std::sync::atomic::{self, ", stringify!($atomic_type), "};")] - /// use std::mem::align_of; /// /// // Get a pointer to an allocated value #[doc = concat!("let ptr: *mut ", stringify!($int_type), " = Box::into_raw(Box::new(0));")] /// - #[doc = concat!("assert!(ptr.is_aligned_to(align_of::<", stringify!($atomic_type), ">()));")] + #[doc = concat!("assert!(ptr.cast::<", stringify!($atomic_type), ">().is_aligned());")] /// /// { /// // Create an atomic view of the allocated value @@ -2309,7 +2303,7 @@ macro_rules! atomic_int { /// # Examples /// /// ``` - /// #![feature(atomic_from_mut, inline_const)] + /// #![feature(atomic_from_mut)] #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] /// #[doc = concat!("let mut some_ints = [const { ", stringify!($atomic_type), "::new(0) }; 10];")] @@ -2384,9 +2378,9 @@ macro_rules! atomic_int { /// ``` #[inline] #[$stable_access] - #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + #[rustc_const_stable(feature = "const_atomic_into_inner", since = "1.79.0")] pub const fn into_inner(self) -> $int_type { - self.v.into_inner() + self.v.primitive_into_inner() } /// Loads a value from the atomic integer. diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 1b43c46bda515..7691721b91e21 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -2,6 +2,7 @@ use crate::mem::transmute; +use crate::any::Any; use crate::fmt; use crate::marker::PhantomData; use crate::ptr; @@ -220,6 +221,12 @@ impl RawWakerVTable { } } +#[derive(Debug)] +enum ExtData<'a> { + Some(&'a mut dyn Any), + None(()), +} + /// The context of an asynchronous task. /// /// Currently, `Context` only serves to provide access to a [`&Waker`](Waker) @@ -229,6 +236,7 @@ impl RawWakerVTable { pub struct Context<'a> { waker: &'a Waker, local_waker: &'a LocalWaker, + ext: ExtData<'a>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -257,6 +265,7 @@ impl<'a> Context<'a> { pub const fn waker(&self) -> &'a Waker { &self.waker } + /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] #[unstable(feature = "local_waker", issue = "118959")] @@ -264,6 +273,17 @@ impl<'a> Context<'a> { pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker } + + /// Returns a reference to the extension data for the current task. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn ext(&mut self) -> &mut dyn Any { + match &mut self.ext { + ExtData::Some(data) => *data, + ExtData::None(unit) => unit, + } + } } #[stable(feature = "futures_api", since = "1.36.0")] @@ -300,6 +320,7 @@ impl fmt::Debug for Context<'_> { pub struct ContextBuilder<'a> { waker: &'a Waker, local_waker: &'a LocalWaker, + ext: ExtData<'a>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -318,7 +339,39 @@ impl<'a> ContextBuilder<'a> { pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; - Self { waker: waker, local_waker, _marker: PhantomData, _marker2: PhantomData } + Self { + waker: waker, + local_waker, + ext: ExtData::None(()), + _marker: PhantomData, + _marker2: PhantomData, + } + } + + /// Create a ContextBuilder from an existing Context. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "context_ext", issue = "123392")] + pub const fn from(cx: &'a mut Context<'_>) -> Self { + let ext = match &mut cx.ext { + ExtData::Some(ext) => ExtData::Some(*ext), + ExtData::None(()) => ExtData::None(()), + }; + Self { + waker: cx.waker, + local_waker: cx.local_waker, + ext, + _marker: PhantomData, + _marker2: PhantomData, + } + } + + /// This method is used to set the value for the waker on `Context`. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn waker(self, waker: &'a Waker) -> Self { + Self { waker, ..self } } /// This method is used to set the value for the local waker on `Context`. @@ -329,13 +382,21 @@ impl<'a> ContextBuilder<'a> { Self { local_waker, ..self } } + /// This method is used to set the value for the extension data on `Context`. + #[inline] + #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn ext(self, data: &'a mut dyn Any) -> Self { + Self { ext: ExtData::Some(data), ..self } + } + /// Builds the `Context`. #[inline] #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { - let ContextBuilder { waker, local_waker, _marker, _marker2 } = self; - Context { waker, local_waker, _marker, _marker2 } + let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self; + Context { waker, local_waker, ext, _marker, _marker2 } } } @@ -515,6 +576,42 @@ impl Clone for Waker { } } + /// Assigns a clone of `source` to `self`, unless [`self.will_wake(source)`][Waker::will_wake] anyway. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids cloning the waker if `self` is already the same waker. + /// + /// # Examples + /// + /// ``` + /// use std::future::Future; + /// use std::pin::Pin; + /// use std::sync::{Arc, Mutex}; + /// use std::task::{Context, Poll, Waker}; + /// + /// struct Waiter { + /// shared: Arc>, + /// } + /// + /// struct Shared { + /// waker: Waker, + /// // ... + /// } + /// + /// impl Future for Waiter { + /// type Output = (); + /// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + /// let mut shared = self.shared.lock().unwrap(); + /// + /// // update the waker + /// shared.waker.clone_from(cx.waker()); + /// + /// // readiness logic ... + /// # Poll::Ready(()) + /// } + /// } + /// + /// ``` #[inline] fn clone_from(&mut self, source: &Self) { if !self.will_wake(source) { diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs new file mode 100644 index 0000000000000..1aa6a288e7082 --- /dev/null +++ b/library/core/src/ub_checks.rs @@ -0,0 +1,162 @@ +//! Provides the [`assert_unsafe_precondition`] macro as well as some utility functions that cover +//! common preconditions. + +use crate::intrinsics::{self, const_eval_select}; + +/// Check that the preconditions of an unsafe function are followed. The check is enabled at +/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri +/// checks implemented with this macro for language UB are always ignored. +/// +/// This macro should be called as +/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` +/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all +/// those arguments are passed to a function with the body `check_expr`. +/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB +/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation +/// of a documented library precondition that does not *immediately* lead to language UB. +/// +/// If `check_library_ub` is used but the check is actually guarding language UB, the check will +/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice +/// diagnostic, but our ability to detect UB is unchanged. +/// But if `check_language_ub` is used when the check is actually for library UB, the check is +/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the +/// library UB, the backtrace Miri reports may be far removed from original cause. +/// +/// These checks are behind a condition which is evaluated at codegen time, not expansion time like +/// [`debug_assert`]. This means that a standard library built with optimizations and debug +/// assertions disabled will have these checks optimized out of its monomorphizations, but if a +/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of +/// this macro, that monomorphization will contain the check. +/// +/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and +/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to +/// this structure: +/// ```ignore (pseudocode) +/// if ::core::intrinsics::check_language_ub() { +/// precondition_check(args) +/// } +/// ``` +/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and +/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is +/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in +/// MIR, but *can* be inlined and fully optimized by a codegen backend. +/// +/// Callers should avoid introducing any other `let` bindings or any code outside this macro in +/// order to call it. Since the precompiled standard library is built with full debuginfo and these +/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough +/// debuginfo to have a measurable compile-time impact on debug builds. +#[allow_internal_unstable(const_ub_checks)] // permit this to be called in stably-const fn +#[macro_export] +#[unstable(feature = "ub_checks", issue = "none")] +macro_rules! assert_unsafe_precondition { + ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { + { + // This check is inlineable, but not by the MIR inliner. + // The reason for this is that the MIR inliner is in an exceptionally bad position + // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`, + // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and + // would be bad for compile times. + // + // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without + // inlining the check. If it's `true`, it can inline it and get significantly better performance. + #[rustc_no_mir_inline] + #[inline] + #[rustc_nounwind] + #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] + const fn precondition_check($($name:$ty),*) { + if !$e { + ::core::panicking::panic_nounwind( + concat!("unsafe precondition(s) violated: ", $message) + ); + } + } + + if ::core::ub_checks::$kind() { + precondition_check($($arg,)*); + } + } + }; +} +#[unstable(feature = "ub_checks", issue = "none")] +pub use assert_unsafe_precondition; + +/// Checking library UB is always enabled when UB-checking is done +/// (and we use a reexport so that there is no unnecessary wrapper function). +#[unstable(feature = "ub_checks", issue = "none")] +pub use intrinsics::ub_checks as check_library_ub; + +/// Determines whether we should check for language UB. +/// +/// The intention is to not do that when running in the interpreter, as that one has its own +/// language UB checks which generally produce better errors. +#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] +#[inline] +pub(crate) const fn check_language_ub() -> bool { + #[inline] + fn runtime() -> bool { + // Disable UB checks in Miri. + !cfg!(miri) + } + + #[inline] + const fn comptime() -> bool { + // Always disable UB checks. + false + } + + // Only used for UB checks so we may const_eval_select. + intrinsics::ub_checks() && const_eval_select((), comptime, runtime) +} + +/// Checks whether `ptr` is properly aligned with respect to +/// `align_of::()`. +/// +/// In `const` this is approximate and can fail spuriously. It is primarily intended +/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the +/// check is anyway not executed in `const`. +#[inline] +pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { + !ptr.is_null() && ptr.is_aligned_to(align) +} + +#[inline] +pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { + let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size }; + len <= max_len +} + +/// Checks whether the regions of memory starting at `src` and `dst` of size +/// `count * size` do *not* overlap. +/// +/// Note that in const-eval this function just returns `true` and therefore must +/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`. +#[inline] +pub(crate) const fn is_nonoverlapping( + src: *const (), + dst: *const (), + size: usize, + count: usize, +) -> bool { + #[inline] + fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool { + let src_usize = src.addr(); + let dst_usize = dst.addr(); + let Some(size) = size.checked_mul(count) else { + crate::panicking::panic_nounwind( + "is_nonoverlapping: `size_of::() * count` overflows a usize", + ) + }; + let diff = src_usize.abs_diff(dst_usize); + // If the absolute distance between the ptrs is at least as big as the size of the buffer, + // they do not overlap. + diff >= size + } + + #[inline] + const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool { + true + } + + // This is just for safety checks so we can const_eval_select. + const_eval_select((src, dst, size, count), comptime, runtime) +} diff --git a/library/core/src/unicode/printable.rs b/library/core/src/unicode/printable.rs index ffb18a5ba8b36..33c3ef8083de5 100644 --- a/library/core/src/unicode/printable.rs +++ b/library/core/src/unicode/printable.rs @@ -63,7 +63,10 @@ pub(crate) fn is_printable(x: char) -> bool { if 0x2cea2 <= x && x < 0x2ceb0 { return false; } - if 0x2ebe1 <= x && x < 0x2f800 { + if 0x2ebe1 <= x && x < 0x2ebf0 { + return false; + } + if 0x2ee5e <= x && x < 0x2f800 { return false; } if 0x2fa1e <= x && x < 0x30000 { @@ -112,7 +115,7 @@ const SINGLETONS0U: &[(u8, u8)] = &[ (0x2b, 3), (0x2d, 11), (0x2e, 1), - (0x30, 3), + (0x30, 4), (0x31, 2), (0x32, 1), (0xa7, 2), @@ -157,12 +160,12 @@ const SINGLETONS0L: &[u8] = &[ 0x7e, 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x96, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, 0xc7, - 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, - 0x8f, 0x1f, 0xd2, 0xd4, 0xce, 0xff, 0x4e, 0x4f, - 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, 0x2f, - 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, - 0x45, 0x90, 0x91, 0x53, 0x67, 0x75, 0xc8, 0xc9, - 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, 0xfe, 0xff, + 0xcf, 0xd7, 0xdf, 0x9a, 0x00, 0x40, 0x97, 0x98, + 0x30, 0x8f, 0x1f, 0xd2, 0xd4, 0xce, 0xff, 0x4e, + 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, + 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, + 0x42, 0x45, 0x90, 0x91, 0x53, 0x67, 0x75, 0xc8, + 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, 0xfe, 0xff, ]; #[rustfmt::skip] const SINGLETONS1U: &[(u8, u8)] = &[ @@ -339,10 +342,9 @@ const NORMAL0: &[u8] = &[ 0x80, 0xbe, 0x22, 0x74, 0x0c, 0x80, 0xd6, 0x1a, - 0x0c, 0x05, - 0x80, 0xff, 0x05, - 0x80, 0xdf, 0x0c, - 0xf2, 0x9d, 0x03, + 0x81, 0x10, 0x05, + 0x80, 0xdf, 0x0b, + 0xf2, 0x9e, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index dd2ad9a58f679..1b3d6729663b5 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -315,7 +315,11 @@ pub mod grapheme_extend { 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 100, 1, 160, 7, 0, 1, 61, 4, 0, 4, 0, 7, 109, 7, 0, 96, 128, 240, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + (c as u32) >= 0x300 && lookup_slow(c) + } + fn lookup_slow(c: char) -> bool { super::skip_search( c as u32, &SHORT_OFFSET_RUNS, diff --git a/library/core/src/unit.rs b/library/core/src/unit.rs index 6656dd5c40beb..d656005f3d42d 100644 --- a/library/core/src/unit.rs +++ b/library/core/src/unit.rs @@ -1,5 +1,3 @@ -use crate::iter::FromIterator; - /// Collapses all unit items from an iterator into one. /// /// This is more useful when combined with higher-level abstractions, like diff --git a/library/core/tests/intrinsics.rs b/library/core/tests/intrinsics.rs index 740565d0df6b4..8b731cf5b25d1 100644 --- a/library/core/tests/intrinsics.rs +++ b/library/core/tests/intrinsics.rs @@ -99,3 +99,29 @@ fn test_const_deallocate_at_runtime() { const_deallocate(core::ptr::null_mut(), 1, 1); // nop } } + +#[test] +fn test_three_way_compare_in_const_contexts() { + use core::cmp::Ordering::{self, *}; + use core::intrinsics::three_way_compare; + + const UNSIGNED_LESS: Ordering = three_way_compare(123_u16, 456); + const UNSIGNED_EQUAL: Ordering = three_way_compare(456_u16, 456); + const UNSIGNED_GREATER: Ordering = three_way_compare(789_u16, 456); + const CHAR_LESS: Ordering = three_way_compare('A', 'B'); + const CHAR_EQUAL: Ordering = three_way_compare('B', 'B'); + const CHAR_GREATER: Ordering = three_way_compare('C', 'B'); + const SIGNED_LESS: Ordering = three_way_compare(123_i64, 456); + const SIGNED_EQUAL: Ordering = three_way_compare(456_i64, 456); + const SIGNED_GREATER: Ordering = three_way_compare(789_i64, 456); + + assert_eq!(UNSIGNED_LESS, Less); + assert_eq!(UNSIGNED_EQUAL, Equal); + assert_eq!(UNSIGNED_GREATER, Greater); + assert_eq!(CHAR_LESS, Less); + assert_eq!(CHAR_EQUAL, Equal); + assert_eq!(CHAR_GREATER, Greater); + assert_eq!(SIGNED_LESS, Less); + assert_eq!(SIGNED_EQUAL, Equal); + assert_eq!(SIGNED_GREATER, Greater); +} diff --git a/library/core/tests/iter/adapters/step_by.rs b/library/core/tests/iter/adapters/step_by.rs index 29adf0b42fae3..6f3300e7a8820 100644 --- a/library/core/tests/iter/adapters/step_by.rs +++ b/library/core/tests/iter/adapters/step_by.rs @@ -49,7 +49,7 @@ fn test_iterator_step_by_nth() { } #[test] -#[cfg_attr(not(bootstrap), allow(non_local_definitions))] +#[allow(non_local_definitions)] fn test_iterator_step_by_nth_overflow() { #[cfg(target_pointer_width = "16")] type Bigger = u32; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index c5a7e87c4aa4f..797108a8425de 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -1,5 +1,6 @@ #![feature(alloc_layout_extra)] #![feature(array_chunks)] +#![feature(array_ptr_get)] #![feature(array_windows)] #![feature(ascii_char)] #![feature(ascii_char_variants)] @@ -10,19 +11,19 @@ #![feature(const_align_offset)] #![feature(const_align_of_val_raw)] #![feature(const_black_box)] -#![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_hash)] #![feature(const_heap)] #![feature(const_intrinsic_copy)] +#![feature(const_int_from_str)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_nonnull_new)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_as_ref)] #![feature(const_ptr_write)] +#![feature(const_three_way_compare)] #![feature(const_trait_impl)] #![feature(const_likely)] -#![feature(const_location_fields)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] #![feature(core_private_bignum)] @@ -35,17 +36,16 @@ #![feature(duration_constructors)] #![feature(exact_size_is_empty)] #![feature(extern_types)] +#![feature(freeze)] #![feature(flt2dec)] #![feature(fmt_internals)] #![feature(float_minimum_maximum)] #![feature(future_join)] #![feature(generic_assert_internals)] -#![feature(generic_nonzero)] #![feature(array_try_from_fn)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(try_find)] -#![feature(inline_const)] #![feature(is_sorted)] #![feature(layout_for_ptr)] #![feature(pattern)] @@ -93,7 +93,7 @@ #![feature(const_waker)] #![feature(never_type)] #![feature(unwrap_infallible)] -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(lazy_cell)] @@ -116,7 +116,6 @@ #![feature(error_generic_member_access)] #![feature(error_in_core)] #![feature(trait_upcasting)] -#![feature(utf8_chunks)] #![feature(is_ascii_octdigit)] #![feature(get_many_mut)] #![feature(iter_map_windows)] diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 863da9b18a289..0fed854318d54 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -214,6 +214,16 @@ fn test_infallible_try_from_int_error() { assert!(func(0).is_ok()); } +const _TEST_CONST_PARSE: () = { + let Ok(-0x8000) = i16::from_str_radix("-8000", 16) else { panic!() }; + let Ok(12345) = u64::from_str_radix("12345", 10) else { panic!() }; + if let Err(e) = i8::from_str_radix("+", 10) { + let IntErrorKind::InvalidDigit = e.kind() else { panic!() }; + } else { + panic!() + } +}; + macro_rules! test_impl_from { ($fn_name:ident, bool, $target: ty) => { #[test] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 659fbd255c168..7b55c2bf8a813 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use core::marker::Freeze; use core::mem::{self, MaybeUninit}; use core::num::NonZero; use core::ptr; @@ -841,11 +842,19 @@ fn ptr_metadata_bounds() { fn static_assert_expected_bounds_for_metadata() where // Keep this in sync with the associated type in `library/core/src/ptr/metadata.rs` - Meta: Copy + Send + Sync + Ord + std::hash::Hash + Unpin, + Meta: Debug + Copy + Send + Sync + Ord + std::hash::Hash + Unpin + Freeze, { } } +#[test] +fn pointee_metadata_debug() { + assert_eq!("()", format!("{:?}", metadata::(&17))); + assert_eq!("2", format!("{:?}", metadata::<[u32]>(&[19, 23]))); + let for_dyn = format!("{:?}", metadata::(&29)); + assert!(for_dyn.starts_with("DynMetadata(0x"), "{:?}", for_dyn); +} + #[test] fn dyn_metadata() { #[derive(Debug)] @@ -1141,3 +1150,24 @@ fn test_const_copy() { assert!(*ptr2 == 1); }; } + +#[test] +fn test_null_array_as_slice() { + let arr: *mut [u8; 4] = null_mut(); + let ptr: *mut [u8] = arr.as_mut_slice(); + assert!(ptr.is_null()); + assert_eq!(ptr.len(), 4); + + let arr: *const [u8; 4] = null(); + let ptr: *const [u8] = arr.as_slice(); + assert!(ptr.is_null()); + assert_eq!(ptr.len(), 4); +} + +#[test] +fn test_ptr_from_raw_parts_in_const() { + const EMPTY_SLICE_PTR: *const [i32] = + std::ptr::slice_from_raw_parts(std::ptr::without_provenance(123), 456); + assert_eq!(EMPTY_SLICE_PTR.addr(), 123); + assert_eq!(EMPTY_SLICE_PTR.len(), 456); +} diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs index d02dc45da34c2..a47ef7aa1ebc6 100644 --- a/library/core/tests/result.rs +++ b/library/core/tests/result.rs @@ -195,7 +195,7 @@ pub fn test_unwrap_or_default() { } #[test] -#[cfg_attr(not(bootstrap), allow(non_local_definitions))] +#[allow(non_local_definitions)] pub fn test_into_ok() { fn infallible_op() -> Result { Ok(666) @@ -218,7 +218,7 @@ pub fn test_into_ok() { } #[test] -#[cfg_attr(not(bootstrap), allow(non_local_definitions))] +#[allow(non_local_definitions)] pub fn test_into_err() { fn until_error_op() -> Result { Err(666) diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index c5743eda3e802..ffe8ffcc7f2f7 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -2678,3 +2678,16 @@ fn test_get_many_mut_duplicate() { let mut v = vec![1, 2, 3, 4, 5]; assert!(v.get_many_mut([1, 3, 3, 4]).is_err()); } + +#[test] +fn test_slice_from_raw_parts_in_const() { + static FANCY: i32 = 4; + static FANCY_SLICE: &[i32] = unsafe { std::slice::from_raw_parts(&FANCY, 1) }; + assert_eq!(FANCY_SLICE.as_ptr(), std::ptr::addr_of!(FANCY)); + assert_eq!(FANCY_SLICE.len(), 1); + + const EMPTY_SLICE: &[i32] = + unsafe { std::slice::from_raw_parts(std::ptr::without_provenance(123456), 0) }; + assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); + assert_eq!(EMPTY_SLICE.len(), 0); +} diff --git a/library/core/tests/str_lossy.rs b/library/core/tests/str_lossy.rs index 9d3f0b65fdb94..6e70ea3e28574 100644 --- a/library/core/tests/str_lossy.rs +++ b/library/core/tests/str_lossy.rs @@ -1,10 +1,8 @@ -use core::str::Utf8Chunks; - #[test] fn chunks() { macro_rules! assert_chunks { ( $string:expr, $(($valid:expr, $invalid:expr)),* $(,)? ) => {{ - let mut iter = Utf8Chunks::new($string); + let mut iter = $string.utf8_chunks(); $( let chunk = iter.next().expect("missing chunk"); assert_eq!($valid, chunk.valid()); @@ -79,7 +77,7 @@ fn debug() { "\"Hello\\xC0\\x80 There\\xE6\\x83 Goodbye\\u{10d4ea}\"", &format!( "{:?}", - Utf8Chunks::new(b"Hello\xC0\x80 There\xE6\x83 Goodbye\xf4\x8d\x93\xaa").debug(), + b"Hello\xC0\x80 There\xE6\x83 Goodbye\xf4\x8d\x93\xaa".utf8_chunks().debug(), ), ); } diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index e6ea2b1849b4c..a9d1f53761cbf 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -15,5 +15,7 @@ doc = false alloc = { path = "../alloc" } cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } core = { path = "../core" } -libc = { version = "0.2", default-features = false } compiler_builtins = "0.1.0" + +[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] +libc = { version = "0.2", default-features = false } diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index a6d07fd1984b4..353de8c5c5743 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -8,6 +8,7 @@ #![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] #![panic_runtime] #![allow(unused_features)] +#![feature(asm_experimental_arch)] #![feature(core_intrinsics)] #![feature(panic_runtime)] #![feature(std_internals)] @@ -78,7 +79,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } else if #[cfg(target_arch = "aarch64")] { + } else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] { core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); } else { core::intrinsics::abort(); diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index 85386976d639a..dce2da3164440 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -14,7 +14,9 @@ doc = false [dependencies] alloc = { path = "../alloc" } core = { path = "../core" } -libc = { version = "0.2", default-features = false } unwind = { path = "../unwind" } compiler_builtins = "0.1.0" cfg-if = "1.0" + +[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] +libc = { version = "0.2", default-features = false } diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index af18e19337c7a..fed4c52e83c5f 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -84,7 +84,7 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { super::__rust_foreign_exception(); } - let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst); + let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::Relaxed); if was_caught { // Since cleanup() isn't allowed to panic, we just abort instead. intrinsics::abort(); diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index dde1c64c6f193..b0245de501e7e 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 e63749c77cef3..04c3d3bf9c359 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -48,9 +48,9 @@ use alloc::boxed::Box; use core::any::Any; +use core::ffi::{c_int, c_uint, c_void}; use core::mem::{self, ManuallyDrop}; use core::ptr::{addr_of, addr_of_mut}; -use libc::{c_int, c_uint, c_void}; // NOTE(nbdd0121): The `canary` field is part of stable ABI. #[repr(C)] @@ -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` 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::() 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) -> 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/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index 90543044ea845..b292be2d6f999 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -141,6 +141,11 @@ jobs: - name: Test (release) run: cargo test --verbose --target=${{ matrix.target }} --release + - name: Generate docs + run: cargo doc --verbose --target=${{ matrix.target }} + env: + RUSTDOCFLAGS: -Dwarnings + wasm-tests: name: "wasm (firefox, ${{ matrix.name }})" runs-on: ubuntu-latest diff --git a/library/portable-simd/Cargo.lock b/library/portable-simd/Cargo.lock index 46312c09657d5..1584c704fb221 100644 --- a/library/portable-simd/Cargo.lock +++ b/library/portable-simd/Cargo.lock @@ -177,6 +177,9 @@ name = "std_float" version = "0.1.0" dependencies = [ "core_simd", + "test_helpers", + "wasm-bindgen", + "wasm-bindgen-test", ] [[package]] diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index a25723e11cef9..331b66262490c 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -7,17 +7,17 @@ convert_float_to_int, core_intrinsics, decl_macro, - inline_const, intra_doc_pointers, repr_simd, simd_ffi, staged_api, strict_provenance, + prelude_import, ptr_metadata )] #![cfg_attr( all( - any(target_arch = "aarch64", target_arch = "arm",), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm",), any( all(target_feature = "v6", not(target_feature = "mclass")), all(target_feature = "mclass", target_feature = "dsp"), @@ -33,8 +33,13 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) )] +#![cfg_attr( + all(target_arch = "x86_64", target_feature = "avx512f"), + feature(stdarch_x86_avx512) +)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] +#![doc(test(attr(deny(warnings))))] #![allow(internal_features)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index e480c25a51e6f..e6e27c76a5e99 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -34,6 +34,7 @@ mod sealed { fn eq(self, other: Self) -> bool; fn to_usize(self) -> usize; + fn max_unsigned() -> u64; type Unsigned: SimdElement; @@ -78,6 +79,11 @@ macro_rules! impl_element { self as usize } + #[inline] + fn max_unsigned() -> u64 { + <$unsigned>::MAX as u64 + } + type Unsigned = $unsigned; const TRUE: Self = -1; diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index e217d1c8c87ca..0f1719206c9ce 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -50,14 +50,14 @@ pub trait SimdConstPtr: Copy + Sealed { /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; - /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. - fn expose_addr(self) -> Self::Usize; + /// Exposes the "provenance" part of the pointer for future use in + /// [`Self::with_exposed_provenance`] and returns the "address" portion. + fn expose_provenance(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -131,15 +131,15 @@ where } #[inline] - fn expose_addr(self) -> Self::Usize { + fn expose_provenance(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_provenance(self) } } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index 5cb27af4fdeba..7ba996d149c0c 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -47,14 +47,14 @@ pub trait SimdMutPtr: Copy + Sealed { /// Equivalent to calling [`pointer::with_addr`] on each element. fn with_addr(self, addr: Self::Usize) -> Self; - /// Gets the "address" portion of the pointer, and "exposes" the provenance part for future use - /// in [`Self::from_exposed_addr`]. - fn expose_addr(self) -> Self::Usize; + /// Exposes the "provenance" part of the pointer for future use in + /// [`Self::with_exposed_provenance`] and returns the "address" portion. + fn expose_provenance(self) -> Self::Usize; /// Convert an address back to a pointer, picking up a previously "exposed" provenance. /// - /// Equivalent to calling [`core::ptr::from_exposed_addr_mut`] on each element. - fn from_exposed_addr(addr: Self::Usize) -> Self; + /// Equivalent to calling [`core::ptr::with_exposed_provenance_mut`] on each element. + fn with_exposed_provenance(addr: Self::Usize) -> Self; /// Calculates the offset from a pointer using wrapping arithmetic. /// @@ -128,15 +128,15 @@ where } #[inline] - fn expose_addr(self) -> Self::Usize { + fn expose_provenance(self) -> Self::Usize { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_expose_addr(self) } + unsafe { core::intrinsics::simd::simd_expose_provenance(self) } } #[inline] - fn from_exposed_addr(addr: Self::Usize) -> Self { + fn with_exposed_provenance(addr: Self::Usize) -> Self { // Safety: `self` is a pointer vector - unsafe { core::intrinsics::simd::simd_from_exposed_addr(addr) } + unsafe { core::intrinsics::simd::simd_with_exposed_provenance(addr) } } #[inline] diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index ae9ff6894b0a7..8a1079042f076 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -16,7 +16,10 @@ where #[inline] pub fn swizzle_dyn(self, idxs: Simd) -> Self { #![allow(unused_imports, unused_unsafe)] - #[cfg(all(target_arch = "aarch64", target_endian = "little"))] + #[cfg(all( + any(target_arch = "aarch64", target_arch = "arm64ec"), + target_endian = "little" + ))] use core::arch::aarch64::{uint8x8_t, vqtbl1q_u8, vtbl1_u8}; #[cfg(all( target_arch = "arm", @@ -37,6 +40,7 @@ where #[cfg(all( any( target_arch = "aarch64", + target_arch = "arm64ec", all(target_arch = "arm", target_feature = "v7") ), target_feature = "neon", @@ -48,7 +52,7 @@ where #[cfg(target_feature = "simd128")] 16 => transize(wasm::i8x16_swizzle, self, idxs), #[cfg(all( - target_arch = "aarch64", + any(target_arch = "aarch64", target_arch = "arm64ec"), target_feature = "neon", target_endian = "little" ))] diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 9e97a3161bb2b..8dbdfc0e1fe03 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -1,9 +1,9 @@ use crate::simd::{ cmp::SimdPartialOrd, + num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; -use core::convert::{TryFrom, TryInto}; /// A SIMD vector with the shape of `[T; N]` but the operations of `T`. /// @@ -262,6 +262,7 @@ where /// # Panics /// /// Panics if the slice's length is less than the vector's `Simd::N`. + /// Use `load_or_default` for an alternative that does not panic. /// /// # Example /// @@ -315,6 +316,143 @@ where unsafe { self.store(slice.as_mut_ptr().cast()) } } + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for + /// the `slice`. Otherwise, the default value for the element type is returned. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; + /// let vec: Vec = vec![10, 11]; + /// + /// let result = Simd::::load_or_default(&vec); + /// assert_eq!(result, Simd::from_array([10, 11, 0, 0])); + /// ``` + #[must_use] + #[inline] + pub fn load_or_default(slice: &[T]) -> Self + where + T: Default, + { + Self::load_or(slice, Default::default()) + } + + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for + /// the `slice`. Otherwise, the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; + /// let vec: Vec = vec![10, 11]; + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_or(&vec, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, -2])); + /// ``` + #[must_use] + #[inline] + pub fn load_or(slice: &[T], or: Self) -> Self { + Self::load_select(slice, Mask::splat(true), or) + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled or out of bounds for the slice, that memory location + /// is not accessed and the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_select(&vec, enable, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); + /// ``` + #[must_use] + #[inline] + pub fn load_select_or_default(slice: &[T], enable: Mask<::Mask, N>) -> Self + where + T: Default, + { + Self::load_select(slice, enable, Default::default()) + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled or out of bounds for the slice, that memory location + /// is not accessed and the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_select(&vec, enable, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); + /// ``` + #[must_use] + #[inline] + pub fn load_select( + slice: &[T], + mut enable: Mask<::Mask, N>, + or: Self, + ) -> Self { + enable &= mask_up_to(slice.len()); + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to + // the element. + unsafe { Self::load_select_ptr(slice.as_ptr(), enable, or) } + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled, that memory location is not accessed and the corresponding + /// value from `or` is passed through. + #[must_use] + #[inline] + pub unsafe fn load_select_unchecked( + slice: &[T], + enable: Mask<::Mask, N>, + or: Self, + ) -> Self { + let ptr = slice.as_ptr(); + // SAFETY: The safety of reading elements from `slice` is ensured by the caller. + unsafe { Self::load_select_ptr(ptr, enable, or) } + } + + /// Reads contiguous elements starting at `ptr`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled, that memory location is not accessed and the corresponding + /// value from `or` is passed through. + #[must_use] + #[inline] + pub unsafe fn load_select_ptr( + ptr: *const T, + enable: Mask<::Mask, N>, + or: Self, + ) -> Self { + // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. + unsafe { core::intrinsics::simd::simd_masked_load(enable.to_int(), ptr, or) } + } + /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the element is instead selected from the `or` vector. /// @@ -493,6 +631,77 @@ where unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) } } + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls + /// which elements are written, as long as they're in-bounds of the `slice`. + /// If the element is disabled or out of bounds, no memory access to that location + /// is made. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let mut arr = [0i32; 4]; + /// let write = Simd::from_array([-5, -4, -3, -2]); + /// let enable = Mask::from_array([false, true, true, true]); + /// + /// write.store_select(&mut arr[..3], enable); + /// assert_eq!(arr, [0, -4, -3, 0]); + /// ``` + #[inline] + pub fn store_select(self, slice: &mut [T], mut enable: Mask<::Mask, N>) { + enable &= mask_up_to(slice.len()); + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to + // the element. + unsafe { self.store_select_ptr(slice.as_mut_ptr(), enable) } + } + + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls + /// which elements are written. + /// + /// # Safety + /// + /// Every enabled element must be in bounds for the `slice`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let mut arr = [0i32; 4]; + /// let write = Simd::from_array([-5, -4, -3, -2]); + /// let enable = Mask::from_array([false, true, true, true]); + /// + /// unsafe { write.store_select_unchecked(&mut arr, enable) }; + /// assert_eq!(arr, [0, -4, -3, -2]); + /// ``` + #[inline] + pub unsafe fn store_select_unchecked( + self, + slice: &mut [T], + enable: Mask<::Mask, N>, + ) { + let ptr = slice.as_mut_ptr(); + // SAFETY: The safety of writing elements in `slice` is ensured by the caller. + unsafe { self.store_select_ptr(ptr, enable) } + } + + /// Conditionally write contiguous elements starting from `ptr`. + /// The `enable` mask controls which elements are written. + /// When disabled, the memory location corresponding to that element is not accessed. + /// + /// # Safety + /// + /// Memory addresses for element are calculated [`pointer::wrapping_offset`] and + /// each enabled element must satisfy the same conditions as [`core::ptr::write`]. + #[inline] + pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<::Mask, N>) { + // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. + unsafe { core::intrinsics::simd::simd_masked_store(enable.to_int(), ptr, self) } + } + /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. /// If an index is out-of-bounds, the write is suppressed without panicking. /// If two elements in the scattered vector would write to the same index @@ -980,3 +1189,37 @@ where { type Mask = isize; } + +#[inline] +fn lane_indices() -> Simd +where + LaneCount: SupportedLaneCount, +{ + let mut index = [0; N]; + for i in 0..N { + index[i] = i; + } + Simd::from_array(index) +} + +#[inline] +fn mask_up_to(len: usize) -> Mask +where + LaneCount: SupportedLaneCount, + M: MaskElement, +{ + let index = lane_indices::(); + let max_value: u64 = M::max_unsigned(); + macro_rules! case { + ($ty:ty) => { + if N < <$ty>::MAX as usize && max_value as $ty as u64 == max_value { + return index.cast().simd_lt(Simd::splat(len.min(N) as $ty)).cast(); + } + }; + } + case!(u8); + case!(u16); + case!(u32); + case!(u64); + index.simd_lt(Simd::splat(len)).cast() +} diff --git a/library/portable-simd/crates/core_simd/src/vendor.rs b/library/portable-simd/crates/core_simd/src/vendor.rs index 6223bedb4e13c..1a34a3a8de5c4 100644 --- a/library/portable-simd/crates/core_simd/src/vendor.rs +++ b/library/portable-simd/crates/core_simd/src/vendor.rs @@ -24,7 +24,7 @@ mod x86; #[cfg(target_arch = "wasm32")] mod wasm32; -#[cfg(any(target_arch = "aarch64", target_arch = "arm",))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm",))] mod arm; #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] diff --git a/library/portable-simd/crates/core_simd/src/vendor/arm.rs b/library/portable-simd/crates/core_simd/src/vendor/arm.rs index ee5c642137367..f8878d11f094d 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/arm.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/arm.rs @@ -4,12 +4,13 @@ use crate::simd::*; #[cfg(target_arch = "arm")] use core::arch::arm::*; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] use core::arch::aarch64::*; #[cfg(all( any( target_arch = "aarch64", + target_arch = "arm64ec", all(target_arch = "arm", target_feature = "v7"), ), target_endian = "little" @@ -69,7 +70,10 @@ mod simd32 { from_transmute! { unsafe Simd => int8x4_t } } -#[cfg(target_arch = "aarch64")] +#[cfg(all( + any(target_arch = "aarch64", target_arch = "arm64ec"), + target_endian = "little" +))] mod aarch64 { use super::neon::*; use super::*; diff --git a/library/portable-simd/crates/core_simd/tests/masked_load_store.rs b/library/portable-simd/crates/core_simd/tests/masked_load_store.rs new file mode 100644 index 0000000000000..3d38658e945f7 --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/masked_load_store.rs @@ -0,0 +1,35 @@ +#![feature(portable_simd)] +use core_simd::simd::prelude::*; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test_configure!(run_in_browser); + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn masked_load_store() { + let mut arr = [u8::MAX; 7]; + + u8x4::splat(0).store_select(&mut arr[5..], Mask::from_array([false, true, false, true])); + // write to index 8 is OOB and dropped + assert_eq!(arr, [255u8, 255, 255, 255, 255, 255, 0]); + + u8x4::from_array([0, 1, 2, 3]).store_select(&mut arr[1..], Mask::splat(true)); + assert_eq!(arr, [255u8, 0, 1, 2, 3, 255, 0]); + + // read from index 8 is OOB and dropped + assert_eq!( + u8x4::load_or(&arr[4..], u8x4::splat(42)), + u8x4::from_array([3, 255, 0, 42]) + ); + assert_eq!( + u8x4::load_select( + &arr[4..], + Mask::from_array([true, false, true, true]), + u8x4::splat(42) + ), + u8x4::from_array([3, 42, 0, 42]) + ); +} diff --git a/library/portable-simd/crates/core_simd/tests/pointers.rs b/library/portable-simd/crates/core_simd/tests/pointers.rs index b9f32d16e01d1..90bfc5d5fd6a5 100644 --- a/library/portable-simd/crates/core_simd/tests/pointers.rs +++ b/library/portable-simd/crates/core_simd/tests/pointers.rs @@ -32,10 +32,10 @@ macro_rules! common_tests { ); } - fn expose_addr() { + fn expose_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*$constness u32, LANES>::expose_addr, - &<*$constness u32>::expose_addr, + &Simd::<*$constness u32, LANES>::expose_provenance, + &<*$constness u32>::expose_provenance, &|_| true, ); } @@ -80,10 +80,10 @@ mod const_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*const u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr::, + &Simd::<*const u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance::, &|_| true, ); } @@ -103,10 +103,10 @@ mod mut_ptr { ); } - fn from_exposed_addr() { + fn with_exposed_provenance() { test_helpers::test_unary_elementwise( - &Simd::<*mut u32, LANES>::from_exposed_addr, - &core::ptr::from_exposed_addr_mut::, + &Simd::<*mut u32, LANES>::with_exposed_provenance, + &core::ptr::with_exposed_provenance_mut::, &|_| true, ); } diff --git a/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs index f21a937f01c44..19ffe1417c8c4 100644 --- a/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs @@ -1,6 +1,6 @@ #![feature(portable_simd)] use core::{fmt, ops::RangeInclusive}; -use test_helpers::{self, biteq, make_runner, prop_assert_biteq}; +use test_helpers::{biteq, make_runner, prop_assert_biteq}; fn swizzle_dyn_scalar_ver(values: [u8; N], idxs: [u8; N]) -> [u8; N] { let mut array = [0; N]; diff --git a/library/portable-simd/crates/std_float/Cargo.toml b/library/portable-simd/crates/std_float/Cargo.toml index 84c69774cbdfe..0896094ee63f4 100644 --- a/library/portable-simd/crates/std_float/Cargo.toml +++ b/library/portable-simd/crates/std_float/Cargo.toml @@ -8,6 +8,13 @@ edition = "2021" [dependencies] core_simd = { path = "../core_simd", default-features = false } +[dev-dependencies.test_helpers] +path = "../test_helpers" + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3" + [features] default = ["as_crate"] as_crate = [] diff --git a/library/portable-simd/crates/std_float/src/lib.rs b/library/portable-simd/crates/std_float/src/lib.rs index 4c547777fdeb3..148aa5f9f1771 100644 --- a/library/portable-simd/crates/std_float/src/lib.rs +++ b/library/portable-simd/crates/std_float/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "as_crate", no_std)] // We are std! #![cfg_attr( feature = "as_crate", feature(core_intrinsics), @@ -44,7 +43,7 @@ use crate::sealed::Sealed; /// For now this trait is available to permit experimentation with SIMD float /// operations that may lack hardware support, such as `mul_add`. pub trait StdFloat: Sealed + Sized { - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, + /// Elementwise fused multiply-add. Computes `(self * a) + b` with only one rounding error, /// yielding a more accurate result than an unfused multiply-add. /// /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target @@ -57,22 +56,65 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_fma(self, a, b) } } - /// Produces a vector where every lane has the square root value - /// of the equivalently-indexed lane in `self` + /// Produces a vector where every element has the square root value + /// of the equivalently-indexed element in `self` #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sqrt(self) -> Self { unsafe { intrinsics::simd_fsqrt(self) } } - /// Returns the smallest integer greater than or equal to each lane. + /// Produces a vector where every element has the sine of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn sin(self) -> Self; + + /// Produces a vector where every element has the cosine of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn cos(self) -> Self; + + /// Produces a vector where every element has the exponential (base e) of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp(self) -> Self; + + /// Produces a vector where every element has the exponential (base 2) of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp2(self) -> Self; + + /// Produces a vector where every element has the natural logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn ln(self) -> Self; + + /// Produces a vector where every element has the logarithm with respect to an arbitrary + /// in the equivalently-indexed elements in `self` and `base`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log(self, base: Self) -> Self { + unsafe { intrinsics::simd_div(self.ln(), base.ln()) } + } + + /// Produces a vector where every element has the base-2 logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log2(self) -> Self; + + /// Produces a vector where every element has the base-10 logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log10(self) -> Self; + + /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn ceil(self) -> Self { unsafe { intrinsics::simd_ceil(self) } } - /// Returns the largest integer value less than or equal to each lane. + /// Returns the largest integer value less than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn floor(self) -> Self { @@ -101,46 +143,65 @@ pub trait StdFloat: Sealed + Sized { impl Sealed for Simd where LaneCount: SupportedLaneCount {} impl Sealed for Simd where LaneCount: SupportedLaneCount {} -// We can safely just use all the defaults. -impl StdFloat for Simd -where - LaneCount: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } -} - -impl StdFloat for Simd -where - LaneCount: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() +macro_rules! impl_float { + { + $($fn:ident: $intrinsic:ident,)* + } => { + impl StdFloat for Simd + where + LaneCount: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + unsafe { intrinsics::$intrinsic(self) } + } + )* + } + + impl StdFloat for Simd + where + LaneCount: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + // https://github.com/llvm/llvm-project/issues/83729 + #[cfg(target_arch = "aarch64")] + { + let mut ln = Self::splat(0f64); + for i in 0..N { + ln[i] = self[i].$fn() + } + ln + } + + #[cfg(not(target_arch = "aarch64"))] + { + unsafe { intrinsics::$intrinsic(self) } + } + } + )* + } } } -#[cfg(test)] -mod tests { - use super::*; - use simd::prelude::*; - - #[test] - fn everything_works() { - let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); - let x2 = x + x; - let _xc = x.ceil(); - let _xf = x.floor(); - let _xr = x.round(); - let _xt = x.trunc(); - let _xfma = x.mul_add(x, x); - let _xsqrt = x.sqrt(); - let _ = x2.abs() * x2; - } +impl_float! { + sin: simd_fsin, + cos: simd_fcos, + exp: simd_fexp, + exp2: simd_fexp2, + ln: simd_flog, + log2: simd_flog2, + log10: simd_flog10, } diff --git a/library/portable-simd/crates/std_float/tests/float.rs b/library/portable-simd/crates/std_float/tests/float.rs new file mode 100644 index 0000000000000..c66c968f8c667 --- /dev/null +++ b/library/portable-simd/crates/std_float/tests/float.rs @@ -0,0 +1,74 @@ +#![feature(portable_simd)] + +macro_rules! unary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_unary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + ) + } + )* + } + } +} + +macro_rules! binary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_binary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _| true, + ) + } + )* + } + } +} + +macro_rules! ternary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_ternary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _, _| true, + ) + } + )* + } + } +} + +macro_rules! impl_tests { + { $scalar:tt } => { + mod $scalar { + use std_float::StdFloat; + + unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } + binary_test! { $scalar, log } + ternary_test! { $scalar, mul_add } + + test_helpers::test_lanes! { + fn fract() { + test_helpers::test_unary_elementwise_flush_subnormals( + &core_simd::simd::Simd::<$scalar, LANES>::fract, + &$scalar::fract, + &|_| true, + ) + } + } + } + } +} + +impl_tests! { f32 } +impl_tests! { f64 } diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 8a1ba436f7261..faca745e56f74 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -2,6 +2,7 @@ use super::*; +use std::cell::RefCell; use std::marker::PhantomData; use std::sync::atomic::AtomicU32; @@ -189,61 +190,61 @@ struct Bridge<'a> { impl<'a> !Send for Bridge<'a> {} impl<'a> !Sync for Bridge<'a> {} -enum BridgeState<'a> { - /// No server is currently connected to this client. - NotConnected, +#[allow(unsafe_code)] +mod state { + use super::Bridge; + use std::cell::{Cell, RefCell}; + use std::ptr; - /// A server is connected and available for requests. - Connected(Bridge<'a>), - - /// Access to the bridge is being exclusively acquired - /// (e.g., during `BridgeState::with`). - InUse, -} + thread_local! { + static BRIDGE_STATE: Cell<*const ()> = const { Cell::new(ptr::null()) }; + } -enum BridgeStateL {} + pub(super) fn set<'bridge, R>(state: &RefCell>, f: impl FnOnce() -> R) -> R { + struct RestoreOnDrop(*const ()); + impl Drop for RestoreOnDrop { + fn drop(&mut self) { + BRIDGE_STATE.set(self.0); + } + } -impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL { - type Out = BridgeState<'a>; -} + let inner = ptr::from_ref(state).cast(); + let outer = BRIDGE_STATE.replace(inner); + let _restore = RestoreOnDrop(outer); -thread_local! { - static BRIDGE_STATE: scoped_cell::ScopedCell = - const { scoped_cell::ScopedCell::new(BridgeState::NotConnected) }; -} + f() + } -impl BridgeState<'_> { - /// Take exclusive control of the thread-local - /// `BridgeState`, and pass it to `f`, mutably. - /// The state will be restored after `f` exits, even - /// by panic, including modifications made to it by `f`. - /// - /// N.B., while `f` is running, the thread-local state - /// is `BridgeState::InUse`. - fn with(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R { - BRIDGE_STATE.with(|state| state.replace(BridgeState::InUse, f)) + pub(super) fn with( + f: impl for<'bridge> FnOnce(Option<&RefCell>>) -> R, + ) -> R { + let state = BRIDGE_STATE.get(); + // SAFETY: the only place where the pointer is set is in `set`. It puts + // back the previous value after the inner call has returned, so we know + // that as long as the pointer is not null, it came from a reference to + // a `RefCell` that outlasts the call to this function. Since `f` + // works the same for any lifetime of the bridge, including the actual + // one, we can lie here and say that the lifetime is `'static` without + // anyone noticing. + let bridge = unsafe { state.cast::>>().as_ref() }; + f(bridge) } } impl Bridge<'_> { fn with(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R { - BridgeState::with(|state| match state { - BridgeState::NotConnected => { - panic!("procedural macro API is used outside of a procedural macro"); - } - BridgeState::InUse => { - panic!("procedural macro API is used while it's already in use"); - } - BridgeState::Connected(bridge) => f(bridge), + state::with(|state| { + let bridge = state.expect("procedural macro API is used outside of a procedural macro"); + let mut bridge = bridge + .try_borrow_mut() + .expect("procedural macro API is used while it's already in use"); + f(&mut bridge) }) } } pub(crate) fn is_available() -> bool { - BridgeState::with(|state| match state { - BridgeState::Connected(_) | BridgeState::InUse => true, - BridgeState::NotConnected => false, - }) + state::with(|s| s.is_some()) } /// A client-side RPC entry-point, which may be using a different `proc_macro` @@ -282,11 +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| { - let show = BridgeState::with(|state| match state { - BridgeState::NotConnected => true, - BridgeState::Connected(_) | BridgeState::InUse => force_show_panics, - }); - if show { + // 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) } })); @@ -312,29 +313,24 @@ fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( let (globals, input) = <(ExpnGlobals, A)>::decode(reader, &mut ()); // Put the buffer we used for input back in the `Bridge` for requests. - let new_state = - BridgeState::Connected(Bridge { cached_buffer: buf.take(), dispatch, globals }); - - BRIDGE_STATE.with(|state| { - state.set(new_state, || { - let output = f(input); - - // Take the `cached_buffer` back out, for the output value. - buf = Bridge::with(|bridge| bridge.cached_buffer.take()); - - // HACK(eddyb) Separate encoding a success value (`Ok(output)`) - // from encoding a panic (`Err(e: PanicMessage)`) to avoid - // having handles outside the `bridge.enter(|| ...)` scope, and - // to catch panics that could happen while encoding the success. - // - // Note that panics should be impossible beyond this point, but - // this is defensively trying to avoid any accidental panicking - // reaching the `extern "C"` (which should `abort` but might not - // at the moment, so this is also potentially preventing UB). - buf.clear(); - Ok::<_, ()>(output).encode(&mut buf, &mut ()); - }) - }) + let state = RefCell::new(Bridge { cached_buffer: buf.take(), dispatch, globals }); + + let output = state::set(&state, || f(input)); + + // Take the `cached_buffer` back out, for the output value. + buf = RefCell::into_inner(state).cached_buffer; + + // HACK(eddyb) Separate encoding a success value (`Ok(output)`) + // from encoding a panic (`Err(e: PanicMessage)`) to avoid + // having handles outside the `bridge.enter(|| ...)` scope, and + // to catch panics that could happen while encoding the success. + // + // Note that panics should be impossible beyond this point, but + // this is defensively trying to avoid any accidental panicking + // reaching the `extern "C"` (which should `abort` but might not + // at the moment, so this is also potentially preventing UB). + buf.clear(); + Ok::<_, ()>(output).encode(&mut buf, &mut ()); })) .map_err(PanicMessage::from) .unwrap_or_else(|e| { diff --git a/library/proc_macro/src/bridge/handle.rs b/library/proc_macro/src/bridge/handle.rs index 894acae217e44..8c53bb609f60c 100644 --- a/library/proc_macro/src/bridge/handle.rs +++ b/library/proc_macro/src/bridge/handle.rs @@ -21,7 +21,7 @@ impl OwnedStore { pub(super) fn new(counter: &'static AtomicU32) -> Self { // Ensure the handle counter isn't 0, which would panic later, // when `NonZero::new` (aka `Handle::new`) is called in `alloc`. - assert_ne!(counter.load(Ordering::SeqCst), 0); + assert_ne!(counter.load(Ordering::Relaxed), 0); OwnedStore { counter, data: BTreeMap::new() } } @@ -29,7 +29,7 @@ impl OwnedStore { impl OwnedStore { pub(super) fn alloc(&mut self, x: T) -> Handle { - let counter = self.counter.fetch_add(1, Ordering::SeqCst); + let counter = self.counter.fetch_add(1, Ordering::Relaxed); let handle = Handle::new(counter).expect("`proc_macro` handle counter overflowed"); assert!(self.data.insert(handle, x).is_none()); handle diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 67c72cf98d405..8553e8d5e4fd6 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -154,7 +154,7 @@ macro_rules! reverse_decode { mod arena; #[allow(unsafe_code)] mod buffer; -#[forbid(unsafe_code)] +#[deny(unsafe_code)] pub mod client; #[allow(unsafe_code)] mod closure; @@ -166,8 +166,6 @@ mod handle; #[forbid(unsafe_code)] mod rpc; #[allow(unsafe_code)] -mod scoped_cell; -#[allow(unsafe_code)] mod selfless_reify; #[forbid(unsafe_code)] pub mod server; diff --git a/library/proc_macro/src/bridge/scoped_cell.rs b/library/proc_macro/src/bridge/scoped_cell.rs deleted file mode 100644 index a8b341439084c..0000000000000 --- a/library/proc_macro/src/bridge/scoped_cell.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! `Cell` variant for (scoped) existential lifetimes. - -use std::cell::Cell; -use std::mem; - -/// Type lambda application, with a lifetime. -#[allow(unused_lifetimes)] -pub trait ApplyL<'a> { - type Out; -} - -/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`. -pub trait LambdaL: for<'a> ApplyL<'a> {} - -impl ApplyL<'a>> LambdaL for T {} - -pub struct ScopedCell(Cell<>::Out>); - -impl ScopedCell { - pub const fn new(value: >::Out) -> Self { - ScopedCell(Cell::new(value)) - } - - /// Sets the value in `self` to `replacement` while - /// running `f`, which gets the old value, mutably. - /// The old value will be restored after `f` exits, even - /// by panic, including modifications made to it by `f`. - #[rustc_confusables("swap")] - pub fn replace<'a, R>( - &self, - replacement: >::Out, - f: impl for<'b, 'c> FnOnce(&'b mut >::Out) -> R, - ) -> R { - /// Wrapper that ensures that the cell always gets filled - /// (with the original state, optionally changed by `f`), - /// even if `f` had panicked. - struct PutBackOnDrop<'a, T: LambdaL> { - cell: &'a ScopedCell, - value: Option<>::Out>, - } - - impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> { - fn drop(&mut self) { - self.cell.0.set(self.value.take().unwrap()); - } - } - - let mut put_back_on_drop = PutBackOnDrop { - cell: self, - value: Some(self.0.replace(unsafe { - let erased = mem::transmute_copy(&replacement); - mem::forget(replacement); - erased - })), - }; - - f(put_back_on_drop.value.as_mut().unwrap()) - } - - /// Sets the value in `self` to `value` while running `f`. - pub fn set(&self, value: >::Out, f: impl FnOnce() -> R) -> R { - self.replace(value, |_| f()) - } -} diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index e04bf69ef5117..23ae2e7dc0ded 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -26,10 +26,10 @@ #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(maybe_uninit_write_slice)] #![feature(negative_impls)] #![feature(new_uninit)] +#![feature(panic_can_unwind)] #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(min_specialization)] @@ -810,7 +810,7 @@ pub enum Delimiter { /// `[ ... ]` #[stable(feature = "proc_macro_lib2", since = "1.29.0")] Bracket, - /// `Ø ... Ø` + /// `∅ ... ∅` /// An invisible delimiter, that may, for example, appear around tokens coming from a /// "macro variable" `$var`. It is important to preserve operator priorities in cases like /// `$var * 3` where `$var` is `1 + 2`. @@ -1360,7 +1360,7 @@ impl Literal { } /// Byte character literal. - #[unstable(feature = "proc_macro_byte_character", issue = "115268")] + #[stable(feature = "proc_macro_byte_character", since = "1.79.0")] pub fn byte_character(byte: u8) -> Literal { let string = [byte].escape_ascii().to_string(); Literal::new(bridge::LitKind::Byte, &string, None) @@ -1374,7 +1374,7 @@ impl Literal { } /// C string literal. - #[unstable(feature = "proc_macro_c_str_literals", issue = "119750")] + #[stable(feature = "proc_macro_c_str_literals", since = "1.79.0")] pub fn c_string(string: &CStr) -> Literal { let string = string.to_bytes().escape_ascii().to_string(); Literal::new(bridge::LitKind::CStr, &string, None) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index d2804b4d20af3..cba9ff4485d05 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,6 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } compiler_builtins = { version = "0.1.105" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } @@ -31,6 +30,12 @@ rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] } miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.21.0", optional = true, default-features = false } +[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] +libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } + +[target.'cfg(all(windows, target_env = "msvc"))'.dependencies] +libc = { version = "0.2.153", default-features = false } + [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } diff --git a/library/std/benches/lib.rs b/library/std/benches/lib.rs index 4d1cf7fab7b7b..1b21c230a0bf2 100644 --- a/library/std/benches/lib.rs +++ b/library/std/benches/lib.rs @@ -1,3 +1,5 @@ +// Disabling in Miri as these would take too long. +#![cfg(not(miri))] #![feature(test)] extern crate test; diff --git a/library/std/build.rs b/library/std/build.rs index ee3f3612d2e0f..7a47b52e8e4eb 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -22,6 +22,7 @@ fn main() { || target_os == "ios" || target_os == "tvos" || target_os == "watchos" + || target_os == "visionos" || target_os == "windows" || target_os == "fuchsia" || (target_vendor == "fortanix" && target_env == "sgx") diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index a834b36697c00..dc0e302a81088 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -329,7 +329,7 @@ static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// ``` #[unstable(feature = "alloc_error_hook", issue = "51245")] pub fn set_alloc_error_hook(hook: fn(Layout)) { - HOOK.store(hook as *mut (), Ordering::SeqCst); + HOOK.store(hook as *mut (), Ordering::Release); } /// Unregisters the current allocation error hook, returning it. @@ -339,7 +339,7 @@ pub fn set_alloc_error_hook(hook: fn(Layout)) { /// If no custom hook is registered, the default hook will be returned. #[unstable(feature = "alloc_error_hook", issue = "51245")] pub fn take_alloc_error_hook() -> fn(Layout) { - let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); + let hook = HOOK.swap(ptr::null_mut(), Ordering::Acquire); if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } } @@ -362,7 +362,7 @@ fn default_alloc_error_hook(layout: Layout) { #[alloc_error_handler] #[unstable(feature = "alloc_internals", issue = "none")] pub fn rust_oom(layout: Layout) -> ! { - let hook = HOOK.load(Ordering::SeqCst); + let hook = HOOK.load(Ordering::Acquire); let hook: fn(Layout) = if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; hook(layout); diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 627befb63a1bb..5039f0b6bb289 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1101,7 +1101,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_confusables("push", "append")] + #[rustc_confusables("push", "append", "put")] pub fn insert(&mut self, k: K, v: V) -> Option { self.base.insert(k, v) } @@ -1271,8 +1271,8 @@ where } #[inline] - fn clone_from(&mut self, other: &Self) { - self.base.clone_from(&other.base); + fn clone_from(&mut self, source: &Self) { + self.base.clone_from(&source.base); } } diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 371201ff44cbe..f0a498fc7bbca 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -885,7 +885,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_confusables("push", "append")] + #[rustc_confusables("push", "append", "put")] pub fn insert(&mut self, value: T) -> bool { self.base.insert(value) } @@ -978,6 +978,10 @@ where Self { base: self.base.clone() } } + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation if possible. #[inline] fn clone_from(&mut self, other: &Self) { self.base.clone_from(&other.base); diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs new file mode 100644 index 0000000000000..4710d7c50b444 --- /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 0000000000000..b64c7f856a15f --- /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 0000000000000..c36f9f5d4c6a8 --- /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 0000000000000..d65c43eca4bb8 --- /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/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 8927e9a47fa83..9dd3d7d3fa16a 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -532,6 +532,12 @@ impl OsString { let rw = Box::into_raw(self.inner.into_box()) as *mut OsStr; unsafe { Box::from_raw(rw) } } + + /// Part of a hack to make PathBuf::push/pop more efficient. + #[inline] + pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { + self.inner.as_mut_vec_for_path_buf() + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -606,6 +612,10 @@ impl Clone for OsString { OsString { inner: self.inner.clone() } } + /// Clones the contents of `source` into `self`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation if possible. #[inline] fn clone_from(&mut self, source: &Self) { self.inner.clone_from(&source.inner) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 5d0de2d06a0ad..409ead0e28433 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -37,7 +37,7 @@ use crate::time::SystemTime; /// /// # Examples /// -/// Creates a new file and write bytes to it (you can also use [`write()`]): +/// Creates a new file and write bytes to it (you can also use [`write`]): /// /// ```no_run /// use std::fs::File; @@ -97,7 +97,7 @@ use crate::time::SystemTime; /// have been opened for asynchronous I/O (e.g. by using `FILE_FLAG_OVERLAPPED`). /// /// [`BufReader`]: io::BufReader -/// [`BufWriter`]: io::BufReader +/// [`BufWriter`]: io::BufWriter /// [`sync_all`]: File::sync_all /// [`write`]: File::write /// [`read`]: File::read @@ -214,7 +214,7 @@ pub struct Permissions(fs_imp::FilePermissions); /// A structure representing a type of file with accessors for each file type. /// It is returned by [`Metadata::file_type`] method. #[stable(feature = "file_type", since = "1.1.0")] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(not(test), rustc_diagnostic_item = "FileType")] pub struct FileType(fs_imp::FileType); @@ -385,7 +385,7 @@ impl File { /// See the [`OpenOptions::open`] function for more details. /// /// See also [`std::fs::write()`][self::write] for a simple function to - /// create a file with a given data. + /// create a file with some given data. /// /// # Examples /// @@ -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() } @@ -972,20 +980,26 @@ impl OpenOptions { /// Note that setting `.write(true).append(true)` has the same effect as /// setting only `.append(true)`. /// - /// For most filesystems, the operating system guarantees that all writes are - /// atomic: no writes get mangled because another process writes at the same - /// time. - /// - /// One maybe obvious note when using append-mode: make sure that all data - /// that belongs together is written to the file in one operation. This - /// can be done by concatenating strings before passing them to [`write()`], - /// or using a buffered writer (with a buffer of adequate size), - /// and calling [`flush()`] when the message is complete. + /// Append mode guarantees that writes will be positioned at the current end of file, + /// even when there are other processes or threads appending to the same file. This is + /// unlike [seek]\([SeekFrom]::[End]\(0)) followed by `write()`, which + /// has a race between seeking and writing during which another writer can write, with + /// our `write()` overwriting their data. + /// + /// Keep in mind that this does not necessarily guarantee that data appended by + /// different processes or threads does not interleave. The amount of data accepted a + /// single `write()` call depends on the operating system and file system. A + /// successful `write()` is allowed to write only part of the given data, so even if + /// you're careful to provide the whole message in a single call to `write()`, there + /// is no guarantee that it will be written out in full. If you rely on the filesystem + /// accepting the message in a single write, make sure that all data that belongs + /// together is written in one operation. This can be done by concatenating strings + /// before passing them to [`write()`]. /// /// If a file is opened with both read and append access, beware that after /// opening, and after every write, the position for reading may be set at the /// end of the file. So, before writing, save the current position (using - /// [seek]\([SeekFrom]::[Current]\(0))), and restore it before the next read. + /// [Seek]::[stream_position]), and restore it before the next read. /// /// ## Note /// @@ -994,8 +1008,10 @@ impl OpenOptions { /// /// [`write()`]: Write::write "io::Write::write" /// [`flush()`]: Write::flush "io::Write::flush" + /// [stream_position]: Seek::stream_position "io::Seek::stream_position" /// [seek]: Seek::seek "io::Seek::seek" /// [Current]: SeekFrom::Current "io::SeekFrom::Current" + /// [End]: SeekFrom::End "io::SeekFrom::End" /// /// # Examples /// @@ -1036,7 +1052,7 @@ impl OpenOptions { /// [`OpenOptions::append`] access must be used. /// /// See also [`std::fs::write()`][self::write] for a simple function to - /// create a file with a given data. + /// create a file with some given data. /// /// # Examples /// @@ -1394,15 +1410,20 @@ impl Metadata { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Metadata { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Metadata") - .field("file_type", &self.file_type()) - .field("is_dir", &self.is_dir()) - .field("is_file", &self.is_file()) - .field("permissions", &self.permissions()) - .field("modified", &self.modified()) - .field("accessed", &self.accessed()) - .field("created", &self.created()) - .finish_non_exhaustive() + let mut debug = f.debug_struct("Metadata"); + debug.field("file_type", &self.file_type()); + debug.field("permissions", &self.permissions()); + debug.field("len", &self.len()); + if let Ok(modified) = self.modified() { + debug.field("modified", &modified); + } + if let Ok(accessed) = self.accessed() { + debug.field("accessed", &accessed); + } + if let Ok(created) = self.created() { + debug.field("created", &created); + } + debug.finish_non_exhaustive() } } @@ -1668,6 +1689,17 @@ impl FileType { } } +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FileType") + .field("is_file", &self.is_file()) + .field("is_dir", &self.is_dir()) + .field("is_symlink", &self.is_symlink()) + .finish_non_exhaustive() + } +} + impl AsInner for FileType { #[inline] fn as_inner(&self) -> &fs_imp::FileType { @@ -2018,7 +2050,7 @@ pub fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> /// the length of the `to` file as reported by `metadata`. /// /// If you want to copy the contents of one file to another and you’re -/// working with [`File`]s, see the [`io::copy()`] function. +/// working with [`File`]s, see the [`io::copy`](io::copy()) function. /// /// # Platform-specific behavior /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 65dec3863cc69..dfa05671ab0f1 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -187,9 +187,9 @@ fn file_test_io_seek_and_tell_smoke_test() { { let mut read_stream = check!(File::open(filename)); check!(read_stream.seek(SeekFrom::Start(set_cursor))); - tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0))); + tell_pos_pre_read = check!(read_stream.stream_position()); check!(read_stream.read(&mut read_mem)); - tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0))); + tell_pos_post_read = check!(read_stream.stream_position()); } check!(fs::remove_file(filename)); let read_str = str::from_utf8(&read_mem).unwrap(); @@ -284,42 +284,42 @@ fn file_test_io_read_write_at() { let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); let mut rw = check!(oo.open(&filename)); assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.stream_position()), 0); assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len()); assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.stream_position()), 0); assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0")); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.stream_position()), 0); assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.stream_position()), 5); assert_eq!(check!(rw.read(&mut buf)), write1.len()); assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.stream_position()), 9); assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.stream_position()), 9); assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.stream_position()), 9); } { let mut read = check!(File::open(&filename)); assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(read.stream_position()), 0); assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(read.stream_position()), 9); assert_eq!(check!(read.read(&mut buf)), write3.len()); assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.stream_position()), 14); assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.stream_position()), 14); assert_eq!(check!(read.read_at(&mut buf, 14)), 0); assert_eq!(check!(read.read_at(&mut buf, 15)), 0); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.stream_position()), 14); } check!(fs::remove_file(&filename)); } @@ -362,38 +362,38 @@ fn file_test_io_seek_read_write() { let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); let mut rw = check!(oo.open(&filename)); assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.stream_position()), 9); assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len()); assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.stream_position()), 9); assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.stream_position()), 5); assert_eq!(check!(rw.read(&mut buf)), write1.len()); assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.stream_position()), 9); assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len()); assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.stream_position()), 5); assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len()); - assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(rw.stream_position()), 14); } { let mut read = check!(File::open(&filename)); assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.stream_position()), 14); assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.stream_position()), 14); assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); assert_eq!(check!(read.read(&mut buf)), write3.len()); assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.stream_position()), 14); assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); - assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.stream_position()), 14); assert_eq!(check!(read.seek_read(&mut buf, 14)), 0); assert_eq!(check!(read.seek_read(&mut buf, 15)), 0); } @@ -1644,8 +1644,8 @@ fn test_file_times() { use crate::os::macos::fs::FileTimesExt; #[cfg(target_os = "tvos")] use crate::os::tvos::fs::FileTimesExt; - #[cfg(target_os = "tvos")] - use crate::os::tvos::fs::FileTimesExt; + #[cfg(target_os = "visionos")] + use crate::os::visionos::fs::FileTimesExt; #[cfg(target_os = "watchos")] use crate::os::watchos::fs::FileTimesExt; #[cfg(windows)] @@ -1657,21 +1657,9 @@ fn test_file_times() { let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); times = times.set_accessed(accessed).set_modified(modified); - #[cfg(any( - windows, - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos", - ))] + #[cfg(any(windows, target_vendor = "apple"))] let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); - #[cfg(any( - windows, - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos", - ))] + #[cfg(any(windows, target_vendor = "apple"))] { times = times.set_created(created); } @@ -1696,20 +1684,14 @@ fn test_file_times() { let metadata = file.metadata().unwrap(); assert_eq!(metadata.accessed().unwrap(), accessed); assert_eq!(metadata.modified().unwrap(), modified); - #[cfg(any( - windows, - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos", - ))] + #[cfg(any(windows, target_vendor = "apple"))] { assert_eq!(metadata.created().unwrap(), created); } } #[test] -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(target_vendor = "apple")] fn test_file_times_pre_epoch_with_nanos() { #[cfg(target_os = "ios")] use crate::os::ios::fs::FileTimesExt; @@ -1717,6 +1699,8 @@ fn test_file_times_pre_epoch_with_nanos() { use crate::os::macos::fs::FileTimesExt; #[cfg(target_os = "tvos")] use crate::os::tvos::fs::FileTimesExt; + #[cfg(target_os = "visionos")] + use crate::os::visionos::fs::FileTimesExt; #[cfg(target_os = "watchos")] use crate::os::watchos::fs::FileTimesExt; diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 83db332ee2558..0cdc49c87d8fc 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -322,16 +322,23 @@ impl Read for BufReader { crate::io::default_read_exact(self, buf) } + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + if self.buf.consume_with(cursor.capacity(), |claimed| cursor.append(claimed)) { + return Ok(()); + } + + crate::io::default_read_buf_exact(self, cursor) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let total_len = bufs.iter().map(|b| b.len()).sum::(); if self.buf.pos() == self.buf.filled() && total_len >= self.capacity() { self.discard_buffer(); return self.inner.read_vectored(bufs); } - let nread = { - let mut rem = self.fill_buf()?; - rem.read_vectored(bufs)? - }; + let mut rem = self.fill_buf()?; + let nread = rem.read_vectored(bufs)?; + self.consume(nread); Ok(nread) } @@ -376,12 +383,7 @@ impl Read for BufReader { // 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/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index 35a5291a34769..ee0db30e22c2e 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -114,7 +114,7 @@ fn test_buffered_reader_seek() { assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); - assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(3)); + assert_eq!(reader.stream_position().ok(), Some(3)); assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); assert_eq!(reader.seek(SeekFrom::Current(1)).ok(), Some(4)); assert_eq!(reader.fill_buf().ok(), Some(&[1, 2][..])); @@ -230,6 +230,9 @@ fn test_buffered_reader_seek_underflow() { Ok(len) } } + // note: this implementation of `Seek` is "broken" due to position + // wrapping, so calling `reader.seek(Current(0))` is semantically different + // than `reader.stream_position()` impl Seek for PositionReader { fn seek(&mut self, pos: SeekFrom) -> io::Result { match pos { @@ -374,7 +377,7 @@ fn test_buffered_writer_seek() { let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new())); w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap(); w.write_all(&[6, 7]).unwrap(); - assert_eq!(w.seek(SeekFrom::Current(0)).ok(), Some(8)); + assert_eq!(w.stream_position().ok(), Some(8)); assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]); assert_eq!(w.seek(SeekFrom::Start(2)).ok(), Some(2)); w.write_all(&[8, 9]).unwrap(); diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 4ef1f1b695e60..f6680b211c7fa 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -95,7 +95,7 @@ impl Cursor { /// # force_inference(&buff); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn new(inner: T) -> Cursor { Cursor { pos: 0, inner } } @@ -132,7 +132,7 @@ impl Cursor { /// let reference = buff.get_ref(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn get_ref(&self) -> &T { &self.inner } @@ -178,7 +178,7 @@ impl Cursor { /// assert_eq!(buff.position(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + #[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn position(&self) -> u64 { self.pos } @@ -357,6 +357,13 @@ where self.pos += n as u64; Ok(()) } + + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + let n = cursor.capacity(); + Read::read_buf_exact(&mut self.remaining_slice(), cursor)?; + self.pos += n as u64; + Ok(()) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 7ae15e0fd017e..f366cb8f42baa 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 for Error { /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. @@ -83,7 +107,7 @@ impl From for Error { } } -#[stable(feature = "io_error_from_try_reserve", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] impl From for Error { /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. /// @@ -828,21 +852,23 @@ impl Error { } } - /// Attempt to downcast the inner error to `E` if any. + /// Attempt to downcast the custom boxed error to `E`. /// - /// If this [`Error`] was constructed via [`new`] then this function will - /// attempt to perform downgrade on it, otherwise it will return [`Err`]. + /// If this [`Error`] contains a custom boxed error, + /// then it would attempt downcasting on the boxed error, + /// otherwise it will return [`Err`]. /// - /// If the downcast succeeds, it will return [`Ok`], otherwise it will also - /// return [`Err`]. + /// If the custom boxed error has the same type as `E`, it will return [`Ok`], + /// otherwise it will also return [`Err`]. + /// + /// This method is meant to be a convenience routine for calling + /// `Box::downcast` on the custom boxed error, returned by + /// [`Error::into_inner`]. /// - /// [`new`]: Error::new /// /// # Examples /// /// ``` - /// #![feature(io_error_downcast)] - /// /// use std::fmt; /// use std::io; /// use std::error::Error; @@ -895,7 +921,7 @@ impl Error { /// assert!(io_error.raw_os_error().is_none()); /// # } /// ``` - #[unstable(feature = "io_error_downcast", issue = "99262")] + #[stable(feature = "io_error_downcast", since = "1.79.0")] pub fn downcast(self) -> result::Result where E: error::Error + Send + Sync + 'static, diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index cb972abd2b821..dd7e0725176e8 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; @@ -50,6 +48,10 @@ impl Read for &mut R { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { (**self).read_exact(buf) } + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf_exact(cursor) + } } #[stable(feature = "rust1", since = "1.0.0")] impl Write for &mut W { @@ -154,6 +156,10 @@ impl Read for Box { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { (**self).read_exact(buf) } + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf_exact(cursor) + } } #[stable(feature = "rust1", since = "1.0.0")] impl Write for Box { @@ -281,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()); @@ -301,6 +304,19 @@ impl Read for &[u8] { Ok(()) } + #[inline] + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + if cursor.capacity() > self.len() { + return Err(io::Error::READ_EXACT_EOF); + } + let (a, b) = self.split_at(cursor.capacity()); + + cursor.append(a); + + *self = b; + Ok(()) + } + #[inline] fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { let len = self.len(); @@ -312,9 +328,7 @@ impl Read for &[u8] { #[inline] fn read_to_string(&mut self, buf: &mut String) -> io::Result { - 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..]; @@ -375,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] @@ -467,9 +477,7 @@ impl Read for VecDeque { // 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 10bf9c51d16aa..98973a43e1ded 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(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(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()> @@ -582,6 +573,26 @@ where Ok(()) } +pub(crate) fn default_read_buf_exact( + this: &mut R, + mut cursor: BorrowedCursor<'_>, +) -> Result<()> { + while cursor.capacity() > 0 { + let prev_written = cursor.written(); + match this.read_buf(cursor.reborrow()) { + Ok(()) => {} + Err(e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + } + + if cursor.written() == prev_written { + return Err(Error::READ_EXACT_EOF); + } + } + + Ok(()) +} + /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -978,24 +989,8 @@ pub trait Read { /// /// If this function returns an error, all bytes read will be appended to `cursor`. #[unstable(feature = "read_buf", issue = "78485")] - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { - while cursor.capacity() > 0 { - let prev_written = cursor.written(); - match self.read_buf(cursor.reborrow()) { - Ok(()) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - - if cursor.written() == prev_written { - return Err(error::const_io_error!( - ErrorKind::UnexpectedEof, - "failed to fill whole buffer" - )); - } - } - - Ok(()) + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + default_read_buf_exact(self, cursor) } /// Creates a "by reference" adaptor for this instance of `Read`. @@ -1702,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() => {} @@ -1770,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 ccc2ed916884f..07fa9259e0b7f 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>>; @@ -451,9 +452,12 @@ impl Read for Stdin { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { self.lock().read_exact(buf) } + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf_exact(cursor) + } } -#[stable(feature = "read_shared_stdin", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "read_shared_stdin", since = "1.78.0")] impl Read for &Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.lock().read(buf) @@ -477,6 +481,9 @@ impl Read for &Stdin { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { self.lock().read_exact(buf) } + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf_exact(cursor) + } } // only used by platform-dependent io::copy specializations, i.e. unused on some platforms @@ -517,6 +524,10 @@ impl Read for StdinLock<'_> { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { self.inner.read_exact(buf) } + + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf_exact(cursor) + } } impl SpecReadByte for StdinLock<'_> { @@ -1054,12 +1065,31 @@ impl fmt::Debug for StderrLock<'_> { )] #[doc(hidden)] pub fn set_output_capture(sink: Option) -> Option { + 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, +) -> Result, 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 eb5d59887683b..090a091b09a13 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -209,6 +209,15 @@ fn read_buf_exact() { assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); } +#[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/io/util.rs b/library/std/src/io/util.rs index 16eaed15e720c..b4c4dffc371c1 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -51,7 +51,7 @@ pub struct Empty; /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn empty() -> Empty { Empty } @@ -173,7 +173,7 @@ pub struct Repeat { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn repeat(byte: u8) -> Repeat { Repeat { byte } } @@ -276,7 +276,7 @@ pub struct Sink; /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +#[rustc_const_stable(feature = "const_io_structs", since = "1.79.0")] pub const fn sink() -> Sink { Sink } diff --git a/library/std/src/lib.miri.rs b/library/std/src/lib.miri.rs new file mode 100644 index 0000000000000..1f9bfb5b1b5c0 --- /dev/null +++ b/library/std/src/lib.miri.rs @@ -0,0 +1,4 @@ +//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about. +#![no_std] +extern crate std as realstd; +pub use realstd::*; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 3db5cda83b7d9..27b46b462044a 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -212,15 +212,19 @@ //! [rust-discord]: https://discord.gg/rust-lang //! [array]: prim@array //! [slice]: prim@slice -// To run std tests without x.py without ending up with two copies of std, Miri needs to be -// able to "empty" this crate. See . -// rustc itself never sets the feature, so this line has no effect there. -#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] -// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies. -#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))] -// + #![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." + ) +)] +#![rustc_preserve_ub_checks] #![doc( html_playground_url = "https://play.rust-lang.org/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", @@ -261,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"), @@ -270,13 +273,11 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(exhaustive_patterns))] -#![cfg_attr(bootstrap, feature(platform_intrinsics))] -#![cfg_attr(not(bootstrap), feature(min_exhaustive_patterns))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] +#![feature(asm_experimental_arch)] #![feature(c_unwind)] #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] @@ -291,12 +292,15 @@ #![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)] #![feature(let_chains)] #![feature(link_cfg)] #![feature(linkage)] +#![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] @@ -310,7 +314,6 @@ #![feature(thread_local)] #![feature(try_blocks)] #![feature(type_alias_impl_trait)] -#![feature(utf8_chunks)] // tidy-alphabetical-end // // Library features (core): @@ -330,7 +333,7 @@ #![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(float_next_up_down)] -#![feature(generic_nonzero)] +#![feature(fmt_internals)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(hint_assert_unchecked)] @@ -341,7 +344,7 @@ #![feature(panic_can_unwind)] #![feature(panic_info_message)] #![feature(panic_internals)] -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(prelude_2024)] #![feature(ptr_as_uninit)] @@ -353,6 +356,7 @@ #![feature(str_internals)] #![feature(strict_provenance)] #![feature(strict_provenance_atomic_ptr)] +#![feature(ub_checks)] // tidy-alphabetical-end // // Library features (alloc): @@ -403,7 +407,6 @@ // tidy-alphabetical-start #![feature(const_collections_with_hasher)] #![feature(const_hash)] -#![feature(const_io_structs)] #![feature(const_ip)] #![feature(const_ipv4)] #![feature(const_ipv6)] @@ -427,6 +430,9 @@ extern crate test; #[allow(unused_imports)] // macros from `alloc` are not used on all platforms #[macro_use] extern crate alloc as alloc_crate; + +// Many compiler tests depend on libc being pulled in by std +// so include it here even if it's unused. #[doc(masked)] #[allow(unused_extern_crates)] extern crate libc; @@ -564,6 +570,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; @@ -582,6 +592,8 @@ pub mod net; pub mod num; pub mod os; pub mod panic; +#[unstable(feature = "core_pattern_types", issue = "none")] +pub mod pat; pub mod path; pub mod process; pub mod sync; diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index 37937b5ea9541..d318d457f3569 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -7,12 +7,12 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); pub fn next_test_ip4() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) } pub fn next_test_ip6() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0)) } diff --git a/library/std/src/num.rs b/library/std/src/num.rs index 1343fdfd1dfdd..8910cdea7c0c9 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -23,11 +23,12 @@ pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError} )] pub use core::num::ZeroablePrimitive; -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "1.79.0")] pub use core::num::NonZero; #[stable(feature = "signed_nonzero", since = "1.34.0")] pub use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; + #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 0b3c51538718d..8c7fc4cb2e453 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 { - Err(crate::io::const_io_error!( - crate::io::ErrorKind::Unsupported, - "operation not supported on this platform", - )) + Err(crate::io::Error::UNSUPPORTED_PLATFORM) } } @@ -179,7 +176,12 @@ impl Drop for OwnedFd { // something like EINTR), we might close another valid file descriptor // opened after we closed ours. #[cfg(not(target_os = "hermit"))] - let _ = libc::close(self.fd); + { + #[cfg(unix)] + crate::sys::fs::debug_assert_fd_is_open(self.fd); + + let _ = libc::close(self.fd); + } #[cfg(target_os = "hermit")] let _ = hermit_abi::close(self.fd); } diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs index 33990d54caae0..b7e0fdc0a9aab 100644 --- a/library/std/src/os/freebsd/net.rs +++ b/library/std/src/os/freebsd/net.rs @@ -2,6 +2,7 @@ #![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; @@ -40,6 +41,15 @@ pub trait UnixSocketExt: Sealed { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()>; + + /// Get a filter name if one had been set previously on the socket. + #[unstable(feature = "acceptfilter", issue = "121891")] + fn acceptfilter(&self) -> io::Result<&CStr>; + + /// Set or disable a filter on the socket to filter incoming connections + /// to defer it before accept(2) + #[unstable(feature = "acceptfilter", issue = "121891")] + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()>; } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -51,6 +61,14 @@ impl UnixSocketExt for net::UnixDatagram { fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { self.as_inner().set_local_creds_persistent(local_creds_persistent) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -62,4 +80,12 @@ impl UnixSocketExt for net::UnixStream { fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { self.as_inner().set_local_creds_persistent(local_creds_persistent) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index bebf3a797d813..ca3584e82f918 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -149,6 +149,8 @@ pub mod solid; pub(crate) mod tvos; #[cfg(target_os = "uefi")] pub mod uefi; +#[cfg(target_os = "visionos")] +pub(crate) mod visionos; #[cfg(target_os = "vita")] pub mod vita; #[cfg(target_os = "vxworks")] diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs index 5c82f43077d4c..b9679c7b3af33 100644 --- a/library/std/src/os/netbsd/net.rs +++ b/library/std/src/os/netbsd/net.rs @@ -2,6 +2,7 @@ #![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; @@ -40,6 +41,15 @@ pub trait UnixSocketExt: Sealed { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds(&self, local_creds: bool) -> io::Result<()>; + + /// Get a filter name if one had been set previously on the socket. + #[unstable(feature = "acceptfilter", issue = "121891")] + fn acceptfilter(&self) -> io::Result<&CStr>; + + /// Set or disable a filter on the socket to filter incoming connections + /// to defer it before accept(2) + #[unstable(feature = "acceptfilter", issue = "121891")] + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()>; } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -51,6 +61,14 @@ impl UnixSocketExt for net::UnixDatagram { fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { self.as_inner().set_local_creds(local_creds) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -62,4 +80,12 @@ impl UnixSocketExt for net::UnixStream { fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { self.as_inner().set_local_creds(local_creds) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 058e9b90cc7a1..970023d8cf19e 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/mod.rs b/library/std/src/os/unix/mod.rs index b0633fd7bfccb..d7a622012a5ac 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -79,6 +79,8 @@ mod platform { pub use crate::os::solaris::*; #[cfg(target_os = "tvos")] pub use crate::os::tvos::*; + #[cfg(target_os = "visionos")] + pub use crate::os::visionos::*; #[cfg(target_os = "vita")] pub use crate::os::vita::*; #[cfg(target_os = "vxworks")] diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 9757653e02c06..79f2c365025b7 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -11,7 +11,7 @@ use crate::{fmt, io, mem, ptr}; #[cfg(not(unix))] #[allow(non_camel_case_types)] mod libc { - pub use libc::c_int; + pub use core::ffi::c_int; pub type socklen_t = u32; pub struct sockaddr; #[derive(Clone)] @@ -107,6 +107,16 @@ impl SocketAddr { addr: libc::sockaddr_un, mut len: libc::socklen_t, ) -> io::Result { + 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/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index 1d279d6adbc25..0597fdcbd7289 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -20,7 +20,7 @@ use crate::sys::net::Socket; ))] #[allow(non_camel_case_types)] mod libc { - pub use libc::c_int; + pub use core::ffi::c_int; pub struct ucred; pub struct cmsghdr; pub struct sockcred2; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index df698c17f6cc2..b29f9099a1111 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -34,7 +34,7 @@ use libc::MSG_NOSIGNAL; target_os = "haiku", target_os = "nto", )))] -const MSG_NOSIGNAL: libc::c_int = 0x0; +const MSG_NOSIGNAL: core::ffi::c_int = 0x0; /// A Unix datagram socket. /// @@ -317,7 +317,7 @@ impl UnixDatagram { fn recv_from_flags( &self, buf: &mut [u8], - flags: libc::c_int, + flags: core::ffi::c_int, ) -> io::Result<(usize, SocketAddr)> { let mut count = 0; let addr = SocketAddr::new(|addr, len| unsafe { diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 31286a906ea99..7e53acbc3878a 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -79,14 +79,14 @@ impl UnixListener { target_os = "espidf", target_os = "horizon" ))] - const backlog: libc::c_int = 128; + const backlog: core::ffi::c_int = 128; #[cfg(any( target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "macos" ))] - const backlog: libc::c_int = -1; + const backlog: core::ffi::c_int = -1; #[cfg(not(any( target_os = "windows", target_os = "redox", @@ -138,9 +138,9 @@ impl UnixListener { unsafe { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; #[cfg(target_os = "linux")] - const backlog: libc::c_int = -1; + const backlog: core::ffi::c_int = -1; #[cfg(not(target_os = "linux"))] - const backlog: libc::c_int = 128; + const backlog: core::ffi::c_int = 128; cvt(libc::bind( inner.as_raw_fd(), core::ptr::addr_of!(socket_addr.addr) as *const _, diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 28c1188677b6e..3e45e3533ed28 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -17,13 +17,10 @@ mod tests; target_os = "linux", target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_vendor = "apple", ))] mod ucred; @@ -43,13 +40,10 @@ pub use self::stream::*; target_os = "linux", target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_vendor = "apple", ))] #[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")] pub use self::ucred::*; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index d2e23bdee6c88..19fc7b3d8532f 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -3,12 +3,10 @@ target_os = "linux", target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", - target_os = "macos", - target_os = "watchos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "nto", + target_vendor = "apple", ))] use super::{peer_cred, UCred}; #[cfg(any(doc, target_os = "android", target_os = "linux"))] @@ -229,12 +227,10 @@ impl UnixStream { target_os = "linux", target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", - target_os = "macos", - target_os = "watchos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "nto", + target_vendor = "apple", ))] pub fn peer_cred(&self) -> io::Result { peer_cred(self) @@ -578,6 +574,10 @@ impl io::Read for UnixStream { io::Read::read(&mut &*self, buf) } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + io::Read::read_buf(&mut &*self, buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { io::Read::read_vectored(&mut &*self, bufs) } @@ -594,6 +594,10 @@ impl<'a> io::Read for &'a UnixStream { self.0.read(buf) } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.0.read_vectored(bufs) } diff --git a/library/std/src/os/unix/net/ucred.rs b/library/std/src/os/unix/net/ucred.rs index de09c93840a54..1497e730bbf15 100644 --- a/library/std/src/os/unix/net/ucred.rs +++ b/library/std/src/os/unix/net/ucred.rs @@ -30,12 +30,13 @@ pub(super) use self::impl_linux::peer_cred; target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", - target_os = "netbsd" + target_os = "netbsd", + target_os = "nto" ))] pub(super) use self::impl_bsd::peer_cred; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] -pub(super) use self::impl_mac::peer_cred; +#[cfg(target_vendor = "apple")] +pub(super) use self::impl_apple::peer_cred; #[cfg(any(target_os = "linux", target_os = "android"))] mod impl_linux { @@ -96,8 +97,8 @@ mod impl_bsd { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] -mod impl_mac { +#[cfg(target_vendor = "apple")] +mod impl_apple { use super::UCred; use crate::os::unix::io::AsRawFd; use crate::os::unix::net::UnixStream; diff --git a/library/std/src/os/unix/net/ucred/tests.rs b/library/std/src/os/unix/net/ucred/tests.rs index dd99ecdd819a7..a6cc81318fc08 100644 --- a/library/std/src/os/unix/net/ucred/tests.rs +++ b/library/std/src/os/unix/net/ucred/tests.rs @@ -7,11 +7,8 @@ use libc::{getegid, geteuid, getpid}; target_os = "linux", target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", - target_os = "macos", - target_os = "watchos", - target_os = "openbsd" + target_os = "openbsd", + target_vendor = "apple", ))] fn test_socket_pair() { // Create two connected sockets and get their peer credentials. They should be equal. @@ -27,13 +24,7 @@ fn test_socket_pair() { } #[test] -#[cfg(any( - target_os = "linux", - target_os = "ios", - target_os = "macos", - target_os = "watchos", - target_os = "tvos", -))] +#[cfg(any(target_os = "linux", target_vendor = "apple"))] fn test_socket_pair_pids(arg: Type) -> RetType { // Create two connected sockets and get their peer credentials. let (sock_a, sock_b) = UnixStream::pair().unwrap(); diff --git a/library/std/src/os/visionos/fs.rs b/library/std/src/os/visionos/fs.rs new file mode 100644 index 0000000000000..e5df4de0b7f71 --- /dev/null +++ b/library/std/src/os/visionos/fs.rs @@ -0,0 +1,160 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::{self, Metadata}; +use crate::sealed::Sealed; +use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::time::SystemTime; + +#[allow(deprecated)] +use super::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned `stat` are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[deprecated( + since = "1.8.0", + note = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_birthtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_flags(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gen(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_lspare(&self) -> u32; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_birthtime(&self) -> i64 { + self.as_inner().as_inner().st_birthtime as i64 + } + fn st_birthtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_birthtime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_gen(&self) -> u32 { + self.as_inner().as_inner().st_gen as u32 + } + fn st_flags(&self) -> u32 { + self.as_inner().as_inner().st_flags as u32 + } + fn st_lspare(&self) -> u32 { + self.as_inner().as_inner().st_lspare as u32 + } +} + +/// OS-specific extensions to [`fs::FileTimes`]. +#[stable(feature = "file_set_times", since = "1.75.0")] +pub trait FileTimesExt: Sealed { + /// Set the creation time of a file. + #[stable(feature = "file_set_times", since = "1.75.0")] + fn set_created(self, t: SystemTime) -> Self; +} + +#[stable(feature = "file_set_times", since = "1.75.0")] +impl FileTimesExt for fs::FileTimes { + fn set_created(mut self, t: SystemTime) -> Self { + self.as_inner_mut().set_created(t.into_inner()); + self + } +} diff --git a/library/std/src/os/visionos/mod.rs b/library/std/src/os/visionos/mod.rs new file mode 100644 index 0000000000000..f4b061ffda898 --- /dev/null +++ b/library/std/src/os/visionos/mod.rs @@ -0,0 +1,6 @@ +//! visionos-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub mod raw; diff --git a/library/std/src/os/visionos/raw.rs b/library/std/src/os/visionos/raw.rs new file mode 100644 index 0000000000000..2b3eca6f493df --- /dev/null +++ b/library/std/src/os/visionos/raw.rs @@ -0,0 +1,83 @@ +//! visionos-specific raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +use crate::os::raw::c_long; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = i64; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; + +#[repr(C)] +#[derive(Clone)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u16, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u16, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_birthtime: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_birthtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_flags: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gen: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_lspare: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_qspare: [i64; 2], +} diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 4525c3aa91477..46fc2a50de911 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/os/windows/process.rs b/library/std/src/os/windows/process.rs index 1be3acf5d4365..15ab225012238 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -199,8 +199,60 @@ pub trait CommandExt: Sealed { /// Append literal text to the command line without any quoting or escaping. /// - /// This is useful for passing arguments to `cmd.exe /c`, which doesn't follow - /// `CommandLineToArgvW` escaping rules. + /// This is useful for passing arguments to applications which doesn't follow + /// the standard C run-time escaping rules, such as `cmd.exe /c`. + /// + /// # Bat files + /// + /// Note the `cmd /c` command line has slightly different escaping rules then bat files + /// themselves. If possible, it may be better to write complex arguments to a temporary + /// .bat file, with appropriate escaping, and simply run that using: + /// + /// ```no_run + /// # use std::process::Command; + /// # let temp_bat_file = ""; + /// # #[allow(unused)] + /// let output = Command::new("cmd").args(["/c", &format!("\"{temp_bat_file}\"")]).output(); + /// ``` + /// + /// # Example + /// + /// Run a bat script using both trusted and untrusted arguments. + /// + /// ```no_run + /// #[cfg(windows)] + /// // `my_script_path` is a path to known bat file. + /// // `user_name` is an untrusted name given by the user. + /// fn run_script( + /// my_script_path: &str, + /// user_name: &str, + /// ) -> Result { + /// use std::io::{Error, ErrorKind}; + /// use std::os::windows::process::CommandExt; + /// use std::process::Command; + /// + /// // Create the command line, making sure to quote the script path. + /// // This assumes the fixed arguments have been tested to work with the script we're using. + /// let mut cmd_args = format!(r#""{my_script_path}" "--features=[a,b,c]""#); + /// + /// // Make sure the user name is safe. In particular we need to be + /// // cautious of ascii symbols that cmd may interpret specially. + /// // Here we only allow alphanumeric characters. + /// if !user_name.chars().all(|c| c.is_alphanumeric()) { + /// return Err(Error::new(ErrorKind::InvalidInput, "invalid user name")); + /// } + /// // now we've checked the user name, let's add that too. + /// cmd_args.push(' '); + /// cmd_args.push_str(&format!("--user {user_name}")); + /// + /// // call cmd.exe and return the output + /// Command::new("cmd.exe") + /// .arg("/c") + /// // surround the entire command in an extra pair of quotes, as required by cmd.exe. + /// .raw_arg(&format!("\"{cmd_args}\"")) + /// .output() + /// } + /// ```` #[stable(feature = "windows_process_extensions_raw_arg", since = "1.62.0")] fn raw_arg>(&mut self, text_to_append_as_is: S) -> &mut process::Command; diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 7fe84db515c34..e9a9f53372026 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -389,7 +389,7 @@ pub(crate) unsafe fn map_memory( let result = a0; if result == SyscallResult::MemoryRange as usize { - let start = core::ptr::from_exposed_addr_mut::(a1); + let start = core::ptr::with_exposed_provenance_mut::(a1); let len = a2 / core::mem::size_of::(); let end = unsafe { start.add(len) }; Ok(unsafe { core::slice::from_raw_parts_mut(start, len) }) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 3d576af681e03..e63b46ab70548 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -126,6 +126,9 @@ where /// Also note that unwinding into Rust code with a foreign exception (e.g. /// an exception thrown from C++ code) is undefined behavior. /// +/// Finally, be **careful in how you drop the result of this function**. +/// If it is `Err`, it contains the panic payload, and dropping that may in turn panic! +/// /// # Examples /// /// ``` diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 464a46264cbdd..5699937cdb49b 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -21,15 +21,14 @@ use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; use crate::sys::stdio::panic_output; use crate::sys_common::backtrace; -use crate::sys_common::thread_info; 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. // @@ -256,7 +255,7 @@ fn default_hook(info: &PanicInfo<'_>) { None => "Box", }, }; - let thread = thread_info::current_thread(); + let thread = thread::try_current(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = |err: &mut dyn crate::io::Write| { @@ -272,12 +271,19 @@ fn default_hook(info: &PanicInfo<'_>) { drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Full)) } Some(BacktraceStyle::Off) => { - if FIRST_PANIC.swap(false, Ordering::SeqCst) { + if FIRST_PANIC.swap(false, Ordering::Relaxed) { let _ = writeln!( err, "note: run with `RUST_BACKTRACE=1` environment variable to display a \ backtrace" ); + if cfg!(miri) { + let _ = writeln!( + err, + "note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` \ + for the environment variable to have an effect" + ); + } } } // If backtraces aren't supported or are forced-off, do nothing. @@ -285,9 +291,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); } @@ -391,6 +397,7 @@ pub mod panic_count { pub fn increase(run_panic_hook: bool) -> Option { let global_count = GLOBAL_PANIC_COUNT.fetch_add(1, Ordering::Relaxed); if global_count & ALWAYS_ABORT_FLAG != 0 { + // Do *not* access thread-local state, we might be after a `fork`. return Some(MustAbort::AlwaysAbort); } @@ -744,13 +751,22 @@ fn rust_panic_with_hook( if let Some(must_abort) = must_abort { match must_abort { panic_count::MustAbort::PanicInHook => { - // Don't try to print the message in this case - // - perhaps that is causing the recursive panics. - rtprintpanic!("thread panicked while processing panic. aborting.\n"); + // Don't try to format the message in this case, perhaps that is causing the + // recursive panics. However if the message is just a string, no user-defined + // code is involved in printing it, so that is risk-free. + let msg_str = message.and_then(|m| m.as_str()).map(|m| [m]); + let message = msg_str.as_ref().map(|m| fmt::Arguments::new_const(m)); + let panicinfo = PanicInfo::internal_constructor( + message.as_ref(), + location, + can_unwind, + force_no_backtrace, + ); + rtprintpanic!("{panicinfo}\nthread panicked while processing panic. aborting.\n"); } panic_count::MustAbort::AlwaysAbort => { // Unfortunately, this does not print a backtrace, because creating - // a `Backtrace` will allocate, which we must to avoid here. + // a `Backtrace` will allocate, which we must avoid here. let panicinfo = PanicInfo::internal_constructor( message, location, diff --git a/library/std/src/pat.rs b/library/std/src/pat.rs new file mode 100644 index 0000000000000..aeddd84c2cb54 --- /dev/null +++ b/library/std/src/pat.rs @@ -0,0 +1,3 @@ +//! Helper module for exporting the `pattern_type` macro + +pub use core::pattern_type; diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 56ea51226f9d5..79d800ff0729b 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1158,12 +1158,6 @@ impl FusedIterator for Ancestors<'_> {} /// Which method works best depends on what kind of situation you're in. #[cfg_attr(not(test), rustc_diagnostic_item = "PathBuf")] #[stable(feature = "rust1", since = "1.0.0")] -// `PathBuf::as_mut_vec` current implementation relies -// on `PathBuf` being layout-compatible with `Vec`. -// However, `PathBuf` layout is considered an implementation detail and must not be relied upon. We -// want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under -// `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. -#[cfg_attr(not(doc), repr(transparent))] pub struct PathBuf { inner: OsString, } @@ -1171,7 +1165,7 @@ pub struct PathBuf { impl PathBuf { #[inline] fn as_mut_vec(&mut self) -> &mut Vec { - unsafe { &mut *(self as *mut PathBuf as *mut Vec) } + self.inner.as_mut_vec_for_path_buf() } /// Allocates an empty `PathBuf`. @@ -1628,6 +1622,10 @@ impl Clone for PathBuf { PathBuf { inner: self.inner.clone() } } + /// Clones the contents of `source` into `self`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation if possible. #[inline] fn clone_from(&mut self, source: &Self) { self.inner.clone_from(&source.inner) @@ -2143,10 +2141,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 +2276,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 /// @@ -3329,7 +3326,6 @@ impl Error for StripPrefixError { /// ## Posix paths /// /// ``` -/// #![feature(absolute_path)] /// # #[cfg(unix)] /// fn main() -> std::io::Result<()> { /// use std::path::{self, Path}; @@ -3354,7 +3350,6 @@ impl Error for StripPrefixError { /// ## Windows paths /// /// ``` -/// #![feature(absolute_path)] /// # #[cfg(windows)] /// fn main() -> std::io::Result<()> { /// use std::path::{self, Path}; @@ -3379,7 +3374,7 @@ impl Error for StripPrefixError { /// /// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13 /// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew -#[unstable(feature = "absolute_path", issue = "92750")] +#[stable(feature = "absolute_path", since = "1.79.0")] pub fn absolute>(path: P) -> io::Result { let path = path.as_ref(); if path.as_os_str().is_empty() { diff --git a/library/std/src/prelude/common.rs b/library/std/src/prelude/common.rs new file mode 100644 index 0000000000000..01936734d7548 --- /dev/null +++ b/library/std/src/prelude/common.rs @@ -0,0 +1,111 @@ +//! Items common to the prelude of all editions. +//! +//! See the [module-level documentation](super) for more. + +// Re-exported core operators +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::marker::{Send, Sized, Sync, Unpin}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; + +// Re-exported functions +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::mem::drop; + +// Re-exported types and traits +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::convert::{AsMut, AsRef, From, Into}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::iter::{Extend, IntoIterator, Iterator}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::option::Option::{self, None, Some}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::result::Result::{self, Err, Ok}; + +// Re-exported built-in macros +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] +#[doc(no_inline)] +pub use core::prelude::v1::{ + assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, +}; + +#[unstable( + feature = "concat_bytes", + issue = "87555", + reason = "`concat_bytes` is not stable enough for use and is subject to change" +)] +#[doc(no_inline)] +pub use core::prelude::v1::concat_bytes; + +// Do not `doc(no_inline)` so that they become doc items on their own +// (no public module for them to be re-exported from). +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +pub use core::prelude::v1::{ + alloc_error_handler, bench, derive, global_allocator, test, test_case, +}; + +#[unstable(feature = "derive_const", issue = "none")] +pub use core::prelude::v1::derive_const; + +// Do not `doc(no_inline)` either. +#[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" +)] +pub use core::prelude::v1::cfg_accessible; + +// Do not `doc(no_inline)` either. +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +pub use core::prelude::v1::cfg_eval; + +// Do not `doc(no_inline)` either. +#[unstable( + feature = "type_ascription", + issue = "23416", + reason = "placeholder syntax for type ascription" +)] +pub use core::prelude::v1::type_ascribe; + +// Do not `doc(no_inline)` either. +#[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" +)] +pub use core::prelude::v1::deref; + +// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated +// rather than glob imported because we want docs to show these re-exports as +// pointing to within `std`. +// Below are the items from the alloc crate. + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::borrow::ToOwned; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::boxed::Box; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::string::{String, ToString}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::vec::Vec; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 7d44d2e4b5da9..0bdbab716adb4 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -93,7 +93,26 @@ #![stable(feature = "rust1", since = "1.0.0")] -pub mod v1; +mod common; + +/// The first version of the prelude of The Rust Standard Library. +/// +/// See the [module-level documentation](self) for more. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod v1 { + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::common::*; + + // Do not `doc(inline)` these `doc(hidden)` items. + #[unstable( + feature = "rustc_encodable_decodable", + issue = "none", + soft, + reason = "derive macro for `rustc-serialize`; should not be used in new code" + )] + #[allow(deprecated)] + pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; +} /// The 2015 version of the prelude of The Rust Standard Library. /// @@ -134,9 +153,8 @@ pub mod rust_2021 { /// See the [module-level documentation](self) for more. #[unstable(feature = "prelude_2024", issue = "121042")] pub mod rust_2024 { - #[unstable(feature = "prelude_2024", issue = "121042")] - #[doc(no_inline)] - pub use super::v1::*; + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::common::*; #[unstable(feature = "prelude_2024", issue = "121042")] #[doc(no_inline)] diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs deleted file mode 100644 index 7a7a773763559..0000000000000 --- a/library/std/src/prelude/v1.rs +++ /dev/null @@ -1,110 +0,0 @@ -//! The first version of the prelude of The Rust Standard Library. -//! -//! See the [module-level documentation](super) for more. - -#![stable(feature = "rust1", since = "1.0.0")] - -// Re-exported core operators -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::marker::{Send, Sized, Sync, Unpin}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; - -// Re-exported functions -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::mem::drop; - -// Re-exported types and traits -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::convert::{AsMut, AsRef, From, Into}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::iter::{Extend, IntoIterator, Iterator}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::option::Option::{self, None, Some}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::result::Result::{self, Err, Ok}; - -// Re-exported built-in macros -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -#[doc(no_inline)] -pub use core::prelude::v1::{ - assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, - stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, -}; - -#[unstable( - feature = "concat_bytes", - issue = "87555", - reason = "`concat_bytes` is not stable enough for use and is subject to change" -)] -#[doc(no_inline)] -pub use core::prelude::v1::concat_bytes; - -// Do not `doc(inline)` these `doc(hidden)` items. -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; - -// Do not `doc(no_inline)` so that they become doc items on their own -// (no public module for them to be re-exported from). -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -pub use core::prelude::v1::{ - alloc_error_handler, bench, derive, global_allocator, test, test_case, -}; - -#[unstable(feature = "derive_const", issue = "none")] -pub use core::prelude::v1::derive_const; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "cfg_accessible", - issue = "64797", - reason = "`cfg_accessible` is not fully implemented" -)] -pub use core::prelude::v1::cfg_accessible; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "cfg_eval", - issue = "82679", - reason = "`cfg_eval` is a recently implemented feature" -)] -pub use core::prelude::v1::cfg_eval; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "type_ascription", - issue = "23416", - reason = "placeholder syntax for type ascription" -)] -pub use core::prelude::v1::type_ascribe; - -// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated -// rather than glob imported because we want docs to show these re-exports as -// pointing to within `std`. -// Below are the items from the alloc crate. - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::borrow::ToOwned; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::boxed::Box; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::string::{String, ToString}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::vec::Vec; diff --git a/library/std/src/process.rs b/library/std/src/process.rs index b84d5f11954de..4a73a9be88bc1 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -88,6 +88,47 @@ //! assert_eq!(b"test", output.stdout.as_slice()); //! ``` //! +//! # Windows argument splitting +//! +//! On Unix systems arguments are passed to a new process as an array of strings +//! but on Windows arguments are passed as a single commandline string and it's +//! up to the child process to parse it into an array. Therefore the parent and +//! child processes must agree on how the commandline string is encoded. +//! +//! Most programs use the standard C run-time `argv`, which in practice results +//! in consistent argument handling. However some programs have their own way of +//! parsing the commandline string. In these cases using [`arg`] or [`args`] may +//! result in the child process seeing a different array of arguments then the +//! parent process intended. +//! +//! Two ways of mitigating this are: +//! +//! * Validate untrusted input so that only a safe subset is allowed. +//! * Use [`raw_arg`] to build a custom commandline. This bypasses the escaping +//! rules used by [`arg`] so should be used with due caution. +//! +//! `cmd.exe` and `.bat` use non-standard argument parsing and are especially +//! vulnerable to malicious input as they may be used to run arbitrary shell +//! commands. Untrusted arguments should be restricted as much as possible. +//! For examples on handling this see [`raw_arg`]. +//! +//! ### Bat file special handling +//! +//! On Windows, `Command` uses the Windows API function [`CreateProcessW`] to +//! spawn new processes. An undocumented feature of this function is that, +//! when given a `.bat` file as the application to run, it will automatically +//! convert that into running `cmd.exe /c` with the bat file as the next argument. +//! +//! For historical reasons Rust currently preserves this behaviour when using +//! [`Command::new`], and escapes the arguments according to `cmd.exe` rules. +//! Due to the complexity of `cmd.exe` argument handling, it might not be +//! possible to safely escape some special chars, and using them will result +//! in an error being returned at process spawn. The set of unescapeable +//! special chars might change between releases. +//! +//! Also note that running `.bat` scripts in this way may be removed in the +//! future and so should not be relied upon. +//! //! [`spawn`]: Command::spawn //! [`output`]: Command::output //! @@ -97,6 +138,12 @@ //! //! [`Write`]: io::Write //! [`Read`]: io::Read +//! +//! [`arg`]: Command::arg +//! [`args`]: Command::args +//! [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg +//! +//! [`CreateProcessW`]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw #![stable(feature = "process", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] @@ -611,6 +658,22 @@ impl Command { /// escaped characters, word splitting, glob patterns, variable substitution, etc. /// have no effect. /// + ///

+ /// /// # Examples /// /// Basic usage: @@ -641,6 +704,22 @@ impl Command { /// escaped characters, word splitting, glob patterns, variable substitution, etc. /// have no effect. /// + ///
+ /// + /// On Windows use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to use with `args`. + /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments + /// and are therefore vulnerable to malicious input. + /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// + /// See [Windows argument splitting][windows-args] for more details + /// or [`raw_arg`] for manually implementing non-standard argument encoding. + /// + /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg + /// [windows-args]: crate::process#windows-argument-splitting + /// + ///
+ /// /// # Examples /// /// Basic usage: @@ -1786,7 +1865,8 @@ impl ExitStatusError { /// # Examples /// /// ``` - /// #![feature(exit_status_error, generic_nonzero)] + /// #![feature(exit_status_error)] + /// /// # if cfg!(unix) { /// use std::num::NonZero; /// use std::process::Command; diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 335944845ae0b..59e118f81ab1b 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -16,16 +16,13 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(unused_macros)] -use crate::ffi::CString; - // Re-export some of our utilities which are expected by other crates. pub use crate::panicking::{begin_panic, panic_count}; pub use core::panicking::{panic_display, panic_fmt}; use crate::sync::Once; use crate::sys; -use crate::sys_common::thread_info; -use crate::thread::Thread; +use crate::thread::{self, Thread}; // Prints to the "panic output", depending on the platform this may be: // - the standard error output @@ -96,13 +93,9 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { unsafe { sys::init(argc, argv, sigpipe); - let main_guard = sys::thread::guard::init(); - // Next, set up the current Thread with the guard information we just - // created. Note that this isn't necessary in general for new threads, - // but we just do this to name the main thread and to give it correct - // info about the stack bounds. - let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main")))); - thread_info::set(main_guard, thread); + // Set up the current thread to give it the right name. + let thread = Thread::new_main(); + thread::set_current(thread); } } diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 764fa284794e1..b4bac081e7ab7 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -81,7 +81,7 @@ impl Barrier { /// let barrier = Barrier::new(10); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_barrier", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_barrier", since = "1.78.0")] #[must_use] #[inline] pub const fn new(n: usize) -> Barrier { diff --git a/library/std/src/sync/condvar/tests.rs b/library/std/src/sync/condvar/tests.rs index 24f467f0b03d7..12d13a6b20be3 100644 --- a/library/std/src/sync/condvar/tests.rs +++ b/library/std/src/sync/condvar/tests.rs @@ -170,14 +170,14 @@ fn wait_timeout_wake() { let t = thread::spawn(move || { let _g = m2.lock().unwrap(); thread::sleep(Duration::from_millis(1)); - notified_copy.store(true, Ordering::SeqCst); + notified_copy.store(true, Ordering::Relaxed); c2.notify_one(); }); let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); assert!(!timeout_res.timed_out()); // spurious wakeups mean this isn't necessarily true // so execute test again, if not notified - if !notified.load(Ordering::SeqCst) { + if !notified.load(Ordering::Relaxed) { t.join().unwrap(); continue; } diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 895fcbd6b7ed8..d417034f5af85 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -396,7 +396,7 @@ impl Mutex { self.poison.get() } - /// Clear the poisoned state from a mutex + /// Clear the poisoned state from a mutex. /// /// If the mutex is poisoned, it will remain poisoned until this function is called. This /// allows recovering from a poisoned state and marking that it has recovered. For example, if diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 6d068613f8f30..fc830baccedd2 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -252,6 +252,46 @@ impl OnceLock { } } + /// Gets the mutable reference of the contents of the cell, initializing + /// it with `f` if the cell was empty. + /// + /// Many threads may call `get_mut_or_init` concurrently with different + /// initializing functions, but it is guaranteed that only one function + /// will be executed. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell_get_mut)] + /// + /// use std::sync::OnceLock; + /// + /// let mut cell = OnceLock::new(); + /// let value = cell.get_mut_or_init(|| 92); + /// assert_eq!(*value, 92); + /// + /// *value += 2; + /// assert_eq!(*value, 94); + /// + /// let value = cell.get_mut_or_init(|| unreachable!()); + /// assert_eq!(*value, 94); + /// ``` + #[inline] + #[unstable(feature = "once_cell_get_mut", issue = "121641")] + pub fn get_mut_or_init(&mut self, f: F) -> &mut T + where + F: FnOnce() -> T, + { + match self.get_mut_or_try_init(|| Ok::(f())) { + Ok(val) => val, + } + } + /// Gets the contents of the cell, initializing it with `f` if /// the cell was empty. If the cell was empty and `f` failed, an /// error is returned. @@ -303,6 +343,47 @@ impl OnceLock { Ok(unsafe { self.get_unchecked() }) } + /// Gets the mutable reference of the contents of the cell, initializing + /// it with `f` if the cell was empty. If the cell was empty and `f` failed, + /// an error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and + /// the cell remains uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell_get_mut)] + /// + /// use std::sync::OnceLock; + /// + /// let mut cell: OnceLock = OnceLock::new(); + /// + /// // Failed initializers do not change the value + /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); + /// assert!(cell.get().is_none()); + /// + /// let value = cell.get_mut_or_try_init(|| "1234".parse()); + /// assert_eq!(value, Ok(&mut 1234)); + /// *value.unwrap() += 2; + /// assert_eq!(cell.get(), Some(&1236)) + /// ``` + #[inline] + #[unstable(feature = "once_cell_get_mut", issue = "121641")] + pub fn get_mut_or_try_init(&mut self, f: F) -> Result<&mut T, E> + where + F: FnOnce() -> Result, + { + if self.get().is_none() { + self.initialize(f)?; + } + debug_assert!(self.is_initialized()); + // SAFETY: The inner value has been initialized + Ok(unsafe { self.get_unchecked_mut() }) + } + /// Consumes the `OnceLock`, returning the wrapped value. Returns /// `None` if the cell was empty. /// diff --git a/library/std/src/sync/reentrant_lock/tests.rs b/library/std/src/sync/reentrant_lock/tests.rs index d4c1d440c6109..aeef0289d28f8 100644 --- a/library/std/src/sync/reentrant_lock/tests.rs +++ b/library/std/src/sync/reentrant_lock/tests.rs @@ -1,4 +1,4 @@ -use super::{ReentrantLock, ReentrantLockGuard}; +use super::ReentrantLock; use crate::cell::RefCell; use crate::sync::Arc; use crate::thread; @@ -51,10 +51,3 @@ fn trylock_works() { .unwrap(); let _lock3 = l.try_lock(); } - -pub struct Answer<'a>(pub ReentrantLockGuard<'a, RefCell>); -impl Drop for Answer<'_> { - fn drop(&mut self) { - *self.0.borrow_mut() = 42; - } -} diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index f7f098c082a0f..e0a8a7603d71a 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -31,13 +31,14 @@ use crate::sys::sync as sys; ///
Potential deadlock example /// /// ```text -/// // Thread 1 | // Thread 2 -/// let _rg = lock.read(); | -/// | // will block -/// | let _wg = lock.write(); -/// // may deadlock | -/// let _rg = lock.read(); | +/// // Thread 1 | // Thread 2 +/// let _rg1 = lock.read(); | +/// | // will block +/// | let _wg = lock.write(); +/// // may deadlock | +/// let _rg2 = lock.read(); | /// ``` +/// ///
/// /// The type parameter `T` represents the data that this lock protects. It is @@ -439,7 +440,7 @@ impl RwLock { self.poison.get() } - /// Clear the poisoned state from a lock + /// Clear the poisoned state from a lock. /// /// If the lock is poisoned, it will remain poisoned until this function is called. This allows /// recovering from a poisoned state and marking that it has recovered. For example, if the diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 4ca3f1cd1853a..18b969bca85a6 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -11,8 +11,6 @@ use crate::str; use crate::sync::Arc; use crate::sys_common::{AsInner, IntoInner}; -use core::str::Utf8Chunks; - #[cfg(test)] mod tests; @@ -29,7 +27,7 @@ pub struct Slice { impl fmt::Debug for Slice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&Utf8Chunks::new(&self.inner).debug(), f) + fmt::Debug::fmt(&self.inner.utf8_chunks().debug(), f) } } @@ -41,7 +39,7 @@ impl fmt::Display for Slice { return "".fmt(f); } - for chunk in Utf8Chunks::new(&self.inner) { + for chunk in self.inner.utf8_chunks() { let valid = chunk.valid(); // If we successfully decoded the whole chunk as a valid string then // we can return a direct formatting of the string which will also @@ -198,6 +196,12 @@ impl Buf { pub fn into_rc(&self) -> Rc { self.as_slice().into_rc() } + + /// Part of a hack to make PathBuf::push/pop more efficient. + #[inline] + pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { + &mut self.inner + } } impl Slice { diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 352bd7359033a..b3ceb55802dc5 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -158,6 +158,12 @@ impl Buf { pub fn into_rc(&self) -> Rc { self.as_slice().into_rc() } + + /// Part of a hack to make PathBuf::push/pop more efficient. + #[inline] + pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { + self.inner.as_mut_vec_for_path_buf() + } } impl Slice { diff --git a/library/std/src/sys/pal/common/alloc.rs b/library/std/src/sys/pal/common/alloc.rs index 8cf9ef6804724..598b6db71f5de 100644 --- a/library/std/src/sys/pal/common/alloc.rs +++ b/library/std/src/sys/pal/common/alloc.rs @@ -23,6 +23,7 @@ pub const MIN_ALIGN: usize = 8; #[cfg(any( target_arch = "x86_64", target_arch = "aarch64", + target_arch = "arm64ec", target_arch = "loongarch64", target_arch = "mips64", target_arch = "mips64r6", diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index 5eb828fea1f9e..962577bb1ed83 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -48,6 +48,11 @@ impl FileDesc { pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { unsupported() } + + pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> { + cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?; + Ok(()) + } } impl<'a> Read for &'a FileDesc { diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index d4da53fd3df9e..6519cc22f1f6f 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,12 +1,17 @@ -use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; +use super::abi::{ + self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, + O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, +}; use super::fd::FileDesc; -use crate::ffi::{CStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; -use crate::hash::{Hash, Hasher}; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; +use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; use crate::sys::time::SystemTime; @@ -14,16 +19,53 @@ use crate::sys::unsupported; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; pub use crate::sys_common::fs::{copy, try_exists}; -//pub use crate::sys_common::fs::remove_dir_all; #[derive(Debug)] pub struct File(FileDesc); +#[derive(Clone)] +pub struct FileAttr { + stat_val: stat_struct, +} -pub struct FileAttr(!); +impl FileAttr { + fn from_stat(stat_val: stat_struct) -> Self { + Self { stat_val } + } +} -pub struct ReadDir(!); +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + root: PathBuf, + dir: Vec, +} -pub struct DirEntry(!); +impl InnerReadDir { + pub fn new(root: PathBuf, dir: Vec) -> Self { + Self { root, dir } + } +} + +pub struct ReadDir { + inner: Arc, + pos: i64, +} + +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), pos: 0 } + } +} + +pub struct DirEntry { + /// path to the entry + root: PathBuf, + /// 64-bit inode number + ino: u64, + /// File type + type_: u32, + /// name of the entry + name: OsString, +} #[derive(Clone, Debug)] pub struct OpenOptions { @@ -41,72 +83,87 @@ pub struct OpenOptions { #[derive(Copy, Clone, Debug, Default)] pub struct FileTimes {} -pub struct FilePermissions(!); - -pub struct FileType(!); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: u32, +} -#[derive(Debug)] -pub struct DirBuilder {} +#[derive(Copy, Clone, Eq, Debug)] +pub struct FileType { + mode: u32, +} -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.mode == other.mode } +} - pub fn perm(&self) -> FilePermissions { - self.0 +impl core::hash::Hash for FileType { + fn hash(&self, state: &mut H) { + self.mode.hash(state); } +} - pub fn file_type(&self) -> FileType { - self.0 - } +#[derive(Debug)] +pub struct DirBuilder { + mode: u32, +} +impl FileAttr { pub fn modified(&self) -> io::Result { - self.0 + Ok(SystemTime::new( + self.stat_val.st_mtime.try_into().unwrap(), + self.stat_val.st_mtime_nsec.try_into().unwrap(), + )) } pub fn accessed(&self) -> io::Result { - self.0 + Ok(SystemTime::new( + self.stat_val.st_atime.try_into().unwrap(), + self.stat_val.st_atime_nsec.try_into().unwrap(), + )) } pub fn created(&self) -> io::Result { - self.0 + Ok(SystemTime::new( + self.stat_val.st_ctime.try_into().unwrap(), + self.stat_val.st_ctime_nsec.try_into().unwrap(), + )) } -} -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 + pub fn size(&self) -> u64 { + self.stat_val.st_size as u64 } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat_val.st_mode) } } - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 + pub fn file_type(&self) -> FileType { + let masked_mode = self.stat_val.st_mode & S_IFMT; + let mode = match masked_mode { + S_IFDIR => DT_DIR, + S_IFLNK => DT_LNK, + S_IFREG => DT_REG, + _ => DT_UNKNOWN, + }; + FileType { mode: mode } } } -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 } -} -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 + pub fn set_readonly(&mut self, _readonly: bool) { + unimplemented!() } -} - -impl Eq for FilePermissions {} -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + #[allow(dead_code)] + pub fn mode(&self) -> u32 { + self.mode as u32 } } @@ -117,49 +174,21 @@ impl FileTimes { impl FileType { pub fn is_dir(&self) -> bool { - self.0 + self.mode == DT_DIR } - pub fn is_file(&self) -> bool { - self.0 + self.mode == DT_REG } - pub fn is_symlink(&self) -> bool { - self.0 - } -} - -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 - } -} - -impl Copy for FileType {} - -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} - -impl Eq for FileType {} - -impl Hash for FileType { - fn hash(&self, _h: &mut H) { - self.0 - } -} - -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + self.mode == DT_LNK } } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e.g. 'ReadDir("/home")' + fmt::Debug::fmt(&*self.inner.root, f) } } @@ -167,25 +196,78 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - self.0 + let mut counter: usize = 0; + let mut offset: i64 = 0; + + // loop over all directory entries and search the entry for the current position + loop { + // leave function, if the loop reaches the of the buffer (with all entries) + if offset >= self.inner.dir.len().try_into().unwrap() { + return None; + } + + let dir = unsafe { + &*(self.inner.dir.as_ptr().offset(offset.try_into().unwrap()) as *const dirent64) + }; + + if counter == self.pos.try_into().unwrap() { + self.pos += 1; + + // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 + // plus the length of the file name. Consequently, file name has a size of d_reclen minus + // the size of dirent64. The file name is always a C string and terminated by `\0`. + // Consequently, we are able to ignore the last byte. + let name_bytes = unsafe { + core::slice::from_raw_parts( + &dir.d_name as *const _ as *const u8, + dir.d_reclen as usize - core::mem::size_of::() - 1, + ) + .to_vec() + }; + let entry = DirEntry { + root: self.inner.root.clone(), + ino: dir.d_ino, + type_: dir.d_type as u32, + name: OsString::from_vec(name_bytes), + }; + + return Some(Ok(entry)); + } + + counter += 1; + + // move to the next dirent64, which is directly stored after the previous one + offset = offset + dir.d_off; + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.0 + self.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { - self.0 + self.file_name_os_str().to_os_string() } pub fn metadata(&self) -> io::Result { - self.0 + let mut path = self.path(); + path.set_file_name(self.file_name_os_str()); + lstat(&path) } pub fn file_type(&self) -> io::Result { - self.0 + Ok(FileType { mode: self.type_ as u32 }) + } + + #[allow(dead_code)] + pub fn ino(&self) -> u64 { + self.ino + } + + pub fn file_name_os_str(&self) -> &OsStr { + self.name.as_os_str() } } @@ -288,7 +370,9 @@ impl File { } pub fn file_attr(&self) -> io::Result { - Err(Error::from_raw_os_error(22)) + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + self.0.fstat(&mut stat_val)?; + Ok(FileAttr::from_stat(stat_val)) } pub fn fsync(&self) -> io::Result<()> { @@ -357,11 +441,18 @@ impl File { impl DirBuilder { pub fn new() -> DirBuilder { - DirBuilder {} + DirBuilder { mode: 0o777 } } - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| { + cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + }) + } + + #[allow(dead_code)] + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as u32; } } @@ -416,8 +507,43 @@ impl FromRawFd for File { } } -pub fn readdir(_p: &Path) -> io::Result { - unsupported() +pub fn readdir(path: &Path) -> io::Result { + let fd_raw = run_path_with_cstr(path, &|path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; + let root = path.to_path_buf(); + + // read all director entries + let mut vec: Vec = Vec::new(); + let mut sz = 512; + loop { + // reserve memory to receive all directory entries + vec.resize(sz, 0); + + let readlen = + unsafe { abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) }; + if readlen > 0 { + // shrink down to the minimal size + vec.resize(readlen.try_into().unwrap(), 0); + break; + } + + // if the buffer is too small, getdents64 returns EINVAL + // otherwise, getdents64 returns an error number + if readlen != (-abi::errno::EINVAL).into() { + return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); + } + + // we don't have enough memory => try to increase the vector size + sz = sz * 2; + + // 1 MB for directory entries should be enough + // stop here to avoid an endless loop + if sz > 0x100000 { + return Err(Error::from(ErrorKind::Uncategorized)); + } + } + + Ok(ReadDir::new(InnerReadDir::new(root, vec))) } pub fn unlink(path: &Path) -> io::Result<()> { @@ -428,17 +554,16 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, &|path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - //unsupported() - Ok(()) + unsupported() } pub fn readlink(_p: &Path) -> io::Result { @@ -453,12 +578,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { unsupported() } -pub fn stat(_p: &Path) -> io::Result { - unsupported() +pub fn stat(path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } -pub fn lstat(_p: &Path) -> io::Result { - unsupported() +pub fn lstat(path: &Path) -> io::Result { + run_path_with_cstr(path, &|path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } pub fn canonicalize(_p: &Path) -> io::Result { diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 1c53796f5d49c..23ac71cb9f29e 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/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index cf45b9c23962c..4fe6b12a95b09 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -2,7 +2,7 @@ use super::abi; use super::thread_local_dtor::run_dtors; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::mem; use crate::num::NonZero; @@ -29,7 +29,7 @@ impl Thread { let p = Box::into_raw(Box::new(p)); let tid = abi::spawn2( thread_start, - p.expose_addr(), + p.expose_provenance(), abi::Priority::into(abi::NORMAL_PRIO), stack, core_id, @@ -47,7 +47,7 @@ impl Thread { extern "C" fn thread_start(main: usize) { unsafe { // Finally, let's run some code. - Box::from_raw(ptr::from_exposed_addr::>(main).cast_mut())(); + Box::from_raw(ptr::with_exposed_provenance::>(main).cast_mut())(); // run all destructors run_dtors(); @@ -71,10 +71,6 @@ impl Thread { // nope } - pub fn get_name() -> Option { - None - } - #[inline] pub fn sleep(dur: Duration) { unsafe { @@ -104,13 +100,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsafe { Ok(NonZero::new_unchecked(abi::get_processor_count())) } } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 319b835a76879..d6f9e4c1476b0 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -18,6 +18,12 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } + } + fn sub_timespec(&self, other: &Timespec) -> Result { if self >= other { Ok(if self.t.tv_nsec >= other.t.tv_nsec { @@ -195,6 +201,10 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime(Timespec::new(tv_sec, tv_nsec)) + } + pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 814a102dd09ae..205226ce1da80 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -8,7 +8,7 @@ use super::{ }; use crate::{ cell::UnsafeCell, - ffi::{CStr, CString}, + ffi::CStr, hint, io, mem::ManuallyDrop, num::NonZero, @@ -98,7 +98,7 @@ impl Thread { }); unsafe extern "C" fn trampoline(exinf: isize) { - let p_inner: *mut ThreadInner = crate::ptr::from_exposed_addr_mut(exinf as usize); + let p_inner: *mut ThreadInner = crate::ptr::with_exposed_provenance_mut(exinf as usize); // Safety: `ThreadInner` is alive at this point let inner = unsafe { &*p_inner }; @@ -181,7 +181,7 @@ impl Thread { abi::acre_tsk(&abi::T_CTSK { // Activate this task immediately tskatr: abi::TA_ACT, - exinf: p_inner.as_ptr().expose_addr() as abi::EXINF, + exinf: p_inner.as_ptr().expose_provenance() as abi::EXINF, // The entry point task: Some(trampoline), // Inherit the calling task's base priority @@ -204,10 +204,6 @@ impl Thread { // nope } - pub fn get_name() -> Option { - None - } - pub fn sleep(dur: Duration) { for timeout in dur2reltims(dur) { expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk"); @@ -312,16 +308,6 @@ impl Drop for Thread { } } -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - /// Terminate and delete the specified task. /// /// This function will abort if `deleted_task` refers to the calling task. diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 7c87deed3715a..8c75ac652998b 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -37,12 +37,12 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "hermit")] { mod hermit; pub use self::hermit::*; - } else if #[cfg(target_os = "wasi")] { - mod wasi; - pub use self::wasi::*; } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { mod wasip2; pub use self::wasip2::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use self::wasi::*; } else if #[cfg(target_family = "wasm")] { mod wasm; pub use self::wasm::*; diff --git a/library/std/src/sys/pal/sgx/libunwind_integration.rs b/library/std/src/sys/pal/sgx/libunwind_integration.rs new file mode 100644 index 0000000000000..debfd324c864e --- /dev/null +++ b/library/std/src/sys/pal/sgx/libunwind_integration.rs @@ -0,0 +1,46 @@ +//! The functions in this module are needed by libunwind. These symbols are named +//! in pre-link args for the target specification, so keep that in sync. + +#![cfg(not(test))] + +use crate::sys::sync::RwLock; + +// Verify that the byte pattern libunwind uses to initialize an RwLock is +// equivalent to the value of RwLock::new(). If the value changes, +// `src/UnwindRustSgx.h` in libunwind needs to be changed too. +const _: () = unsafe { + let bits_rust: usize = crate::mem::transmute(RwLock::new()); + assert!(bits_rust == 0); +}; + +const EINVAL: i32 = 22; + +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 { + if p.is_null() { + return EINVAL; + } + + // We cannot differentiate between reads an writes in unlock and therefore + // always use a write-lock. Unwinding isn't really in the hot path anyway. + unsafe { (*p).write() }; + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 { + if p.is_null() { + return EINVAL; + } + unsafe { (*p).write() }; + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 { + if p.is_null() { + return EINVAL; + } + unsafe { (*p).write_unlock() }; + return 0; +} diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 76f930b86f2ec..d30976ec15149 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -17,6 +17,7 @@ pub mod fd; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; +mod libunwind_integration; pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs index c4d5da1627cb2..68a2d5eded2da 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 { 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) -> 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) -> 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(()), } @@ -524,6 +515,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -536,6 +528,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 77f68bf73348f..e2df57b1a1f53 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -1,6 +1,6 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? use super::unsupported; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::time::Duration; @@ -133,10 +133,6 @@ impl Thread { // which succeeds as-is with the SGX target. } - pub fn get_name() -> Option { - None - } - pub fn sleep(dur: Duration) { usercalls::wait_timeout(0, dur, || true); } @@ -149,13 +145,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/sgx/waitqueue/mod.rs b/library/std/src/sys/pal/sgx/waitqueue/mod.rs index 2d952b7ebbca3..f5668a9493fb5 100644 --- a/library/std/src/sys/pal/sgx/waitqueue/mod.rs +++ b/library/std/src/sys/pal/sgx/waitqueue/mod.rs @@ -52,10 +52,6 @@ impl WaitVariable { WaitVariable { queue: WaitQueue::new(), lock: var } } - pub fn queue_empty(&self) -> bool { - self.queue.is_empty() - } - pub fn lock_var(&self) -> &T { &self.lock } @@ -68,7 +64,7 @@ impl WaitVariable { #[derive(Copy, Clone)] pub enum NotifiedTcs { Single(Tcs), - All { count: NonZero }, + All { _count: NonZero }, } /// An RAII guard that will notify a set of target threads as well as unlock @@ -98,19 +94,6 @@ impl Default for WaitQueue { } } -impl<'a, T> WaitGuard<'a, T> { - /// Returns which TCSes will be notified when this guard drops. - pub fn notified_tcs(&self) -> NotifiedTcs { - self.notified_tcs - } - - /// Drop this `WaitGuard`, after dropping another `guard`. - pub fn drop_after(self, guard: U) { - drop(guard); - drop(self); - } -} - impl<'a, T> Deref for WaitGuard<'a, T> { type Target = SpinMutexGuard<'a, WaitVariable>; @@ -141,10 +124,6 @@ impl WaitQueue { WaitQueue { inner: UnsafeList::new() } } - pub fn is_empty(&self) -> bool { - self.inner.is_empty() - } - /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait /// until a wakeup event. /// @@ -253,7 +232,10 @@ impl WaitQueue { } if let Some(count) = NonZero::new(count) { - Ok(WaitGuard { mutex_guard: Some(guard), notified_tcs: NotifiedTcs::All { count } }) + Ok(WaitGuard { + mutex_guard: Some(guard), + notified_tcs: NotifiedTcs::All { _count: count }, + }) } else { Err(guard) } diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 109ee1a0ab666..3f6ff37903ac6 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() -> crate::io::Result { } 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 6ea874e509e20..5bd339849e9dc 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 b76bcf9bbb0af..f4723b2ea46bf 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -1,7 +1,7 @@ use core::convert::TryInto; use crate::cmp; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::mem; use crate::num::NonZero; @@ -101,10 +101,6 @@ impl Thread { // contact the teeos rustzone team. } - pub fn get_name() -> Option { - None - } - /// only main thread could wait for sometime in teeos pub fn sleep(dur: Duration) { let sleep_millis = dur.as_millis(); @@ -145,22 +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> { - Err(io::Error::new( - io::ErrorKind::NotFound, - "The number of hardware threads is not known for the target platform", - )) -} - -// stub -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } + Err(io::Error::UNKNOWN_THREAD_COUNT) } fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index ba53ed88f3765..23aa4da14a763 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,14 +10,16 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; +use r_efi::protocols::{device_path, device_path_to_text}; +use crate::ffi::OsString; +use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; -use crate::os::uefi; +use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; use crate::ptr::NonNull; -use crate::{ - io::{self, const_io_error}, - os::uefi::env::boot_services, -}; +use crate::slice; +use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sys_common::wstr::WStrUnits; const BOOT_SERVICES_UNAVAILABLE: io::Error = const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); @@ -142,9 +144,74 @@ pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result /// Get the Protocol for current system handle. /// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. -pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> Option> { - let system_handle = uefi::env::try_image_handle()?; - open_protocol(system_handle, protocol_guid).ok() +pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> io::Result> { + let system_handle = uefi::env::try_image_handle().ok_or(io::const_io_error!( + io::ErrorKind::NotFound, + "Protocol not found in Image handle" + ))?; + open_protocol(system_handle, protocol_guid) +} + +pub(crate) fn device_path_to_text(path: NonNull) -> io::Result { + fn path_to_text( + protocol: NonNull, + path: NonNull, + ) -> io::Result { + let path_ptr: *mut r_efi::efi::Char16 = unsafe { + ((*protocol.as_ptr()).convert_device_path_to_text)( + path.as_ptr(), + // DisplayOnly + r_efi::efi::Boolean::FALSE, + // AllowShortcuts + r_efi::efi::Boolean::FALSE, + ) + }; + + // SAFETY: `convert_device_path_to_text` returns a pointer to a null-terminated UTF-16 + // string, and that string cannot be deallocated prior to dropping the `WStrUnits`, so + // it's safe for `WStrUnits` to use. + let path_len = unsafe { + WStrUnits::new(path_ptr) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))? + .count() + }; + + let path = OsString::from_wide(unsafe { slice::from_raw_parts(path_ptr.cast(), path_len) }); + + if let Some(boot_services) = crate::os::uefi::env::boot_services() { + let boot_services: NonNull = boot_services.cast(); + unsafe { + ((*boot_services.as_ptr()).free_pool)(path_ptr.cast()); + } + } + + Ok(path) + } + + static LAST_VALID_HANDLE: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + return path_to_text(protocol, path); + } + } + + let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?; + for handle in device_path_to_text_handles { + if let Ok(protocol) = open_protocol::( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return path_to_text(protocol, path); + } + } + + Err(io::const_io_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } /// Get RuntimeServices diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index e6693db68e641..58838c5876ebd 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,4 +1,4 @@ -use super::{unsupported, RawOsError}; +use super::{helpers, unsupported, RawOsError}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::fmt; @@ -7,6 +7,7 @@ use crate::marker::PhantomData; use crate::os::uefi; use crate::path::{self, PathBuf}; use crate::ptr::NonNull; +use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use r_efi::efi::Status; pub fn errno() -> RawOsError { @@ -164,7 +165,10 @@ impl fmt::Display for JoinPathsError { impl StdError for JoinPathsError {} pub fn current_exe() -> io::Result { - unsupported() + let protocol = helpers::image_handle_protocol::( + loaded_image_device_path::PROTOCOL_GUID, + )?; + helpers::device_path_to_text(protocol).map(PathBuf::from) } pub struct Env(!); diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index b3a4f9c53e36c..edc736978a123 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -1,5 +1,5 @@ use super::unsupported; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::ptr::NonNull; @@ -23,10 +23,6 @@ impl Thread { // nope } - pub fn get_name() -> Option { - None - } - pub fn sleep(dur: Duration) { let boot_services: NonNull = crate::os::uefi::env::boot_services().expect("can't sleep").cast(); @@ -52,13 +48,3 @@ pub fn available_parallelism() -> io::Result> { // UEFI is single threaded Ok(NonZero::new(1).unwrap()) } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 78e82d9c194c4..2a3298e8b4c9d 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -98,12 +98,10 @@ mod imp { } #[inline(always)] - pub unsafe fn init(_argc: isize, _argv: *const *const u8) { - // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize - // `ARGC` and `ARGV`. But in Miri that does not actually happen so we - // still initialize here. - #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))] - really_init(_argc, _argv); + pub unsafe fn init(argc: isize, argv: *const *const u8) { + // on GNU/Linux if we are main then we will init argv and argc twice, it "duplicates work" + // BUT edge-cases are real: only using .init_array can break most emulators, dlopen, etc. + really_init(argc, argv); } /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. @@ -170,7 +168,7 @@ mod imp { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(target_vendor = "apple")] mod imp { use super::Args; use crate::ffi::CStr; @@ -211,7 +209,7 @@ mod imp { // for i in (0..[args count]) // res.push([args objectAtIndex:i]) // res - #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] + #[cfg(not(target_os = "macos"))] pub fn args() -> Args { use crate::ffi::{c_char, c_void, OsString}; use crate::mem; diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs index 3d4ba509829d3..fb1f868644d48 100644 --- a/library/std/src/sys/pal/unix/env.rs +++ b/library/std/src/sys/pal/unix/env.rs @@ -53,6 +53,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "visionos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "visionos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "freebsd")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index a1c0321876fa8..203c7180001f5 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -45,12 +45,9 @@ const READ_LIMIT: usize = libc::ssize_t::MAX as usize; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", - target_os = "macos", target_os = "netbsd", target_os = "openbsd", - target_os = "watchos", + target_vendor = "apple", ))] const fn max_iov() -> usize { libc::IOV_MAX as usize @@ -71,16 +68,13 @@ const fn max_iov() -> usize { target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", target_os = "linux", - target_os = "macos", target_os = "netbsd", target_os = "nto", target_os = "openbsd", target_os = "horizon", target_os = "vita", - target_os = "watchos", + target_vendor = "apple", )))] const fn max_iov() -> usize { 16 // The minimum value required by POSIX. @@ -167,6 +161,8 @@ impl FileDesc { } #[cfg(any( + target_os = "aix", + target_os = "dragonfly", // DragonFly 1.5 target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", @@ -174,6 +170,7 @@ impl FileDesc { target_os = "illumos", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", // OpenBSD 2.7 ))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { let ret = cvt(unsafe { @@ -188,17 +185,18 @@ impl FileDesc { } #[cfg(not(any( + target_os = "aix", target_os = "android", + target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", target_os = "hurd", target_os = "illumos", - target_os = "ios", - target_os = "tvos", target_os = "linux", - target_os = "macos", target_os = "netbsd", + target_os = "openbsd", + target_vendor = "apple", )))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { io::default_read_vectored(|b| self.read_at(b, offset), bufs) @@ -232,14 +230,7 @@ impl FileDesc { Ok(ret as usize) } - // We support old MacOS and iOS versions that do not have `preadv`. There is - // no `syscall` possible in these platform. - #[cfg(any( - all(target_os = "android", target_pointer_width = "32"), - target_os = "ios", - target_os = "tvos", - target_os = "macos", - ))] + #[cfg(all(target_os = "android", target_pointer_width = "32"))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); @@ -259,6 +250,35 @@ impl FileDesc { } } + // We support old MacOS, iOS, watchOS, tvOS and visionOS. `preadv` was added in the following + // Apple OS versions: + // ios 14.0 + // tvos 14.0 + // macos 11.0 + // watchos 7.0 + // + // These versions may be newer than the minimum supported versions of OS's we support so we must + // use "weak" linking. + #[cfg(target_vendor = "apple")] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + super::weak::weak!(fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match preadv.get() { + Some(preadv) => { + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), + } + } + pub fn write(&self, buf: &[u8]) -> io::Result { let ret = cvt(unsafe { libc::write( @@ -318,6 +338,8 @@ impl FileDesc { } #[cfg(any( + target_os = "aix", + target_os = "dragonfly", // DragonFly 1.5 target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", @@ -325,6 +347,7 @@ impl FileDesc { target_os = "illumos", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", // OpenBSD 2.7 ))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { let ret = cvt(unsafe { @@ -339,17 +362,18 @@ impl FileDesc { } #[cfg(not(any( + target_os = "aix", target_os = "android", + target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", target_os = "hurd", target_os = "illumos", - target_os = "ios", - target_os = "tvos", target_os = "linux", - target_os = "macos", target_os = "netbsd", + target_os = "openbsd", + target_vendor = "apple", )))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { io::default_write_vectored(|b| self.write_at(b, offset), bufs) @@ -383,14 +407,7 @@ impl FileDesc { Ok(ret as usize) } - // We support old MacOS and iOS versions that do not have `pwritev`. There is - // no `syscall` possible in these platform. - #[cfg(any( - all(target_os = "android", target_pointer_width = "32"), - target_os = "ios", - target_os = "tvos", - target_os = "macos", - ))] + #[cfg(all(target_os = "android", target_pointer_width = "32"))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); @@ -410,6 +427,35 @@ impl FileDesc { } } + // We support old MacOS, iOS, watchOS, tvOS and visionOS. `pwritev` was added in the following + // Apple OS versions: + // ios 14.0 + // tvos 14.0 + // macos 11.0 + // watchos 7.0 + // + // These versions may be newer than the minimum supported versions of OS's we support so we must + // use "weak" linking. + #[cfg(target_vendor = "apple")] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + super::weak::weak!(fn pwritev(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match pwritev.get() { + Some(pwritev) => { + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), + } + } + #[cfg(not(any( target_env = "newlib", target_os = "solaris", diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index b968f8df34c23..d2006dfc12b55 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -1,10 +1,13 @@ // miri has some special hacks here that make things unused. #![cfg_attr(miri, allow(unused))] +#[cfg(test)] +mod tests; + use crate::os::unix::prelude::*; use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; +use crate::fmt::{self, Write as _}; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; @@ -17,13 +20,7 @@ use crate::sys::time::SystemTime; use crate::sys::{cvt, cvt_r}; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -#[cfg(any( - all(target_os = "linux", target_env = "gnu"), - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", -))] +#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_vendor = "apple"))] use crate::sys::weak::syscall; #[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))] use crate::sys::weak::weak; @@ -31,12 +28,9 @@ use crate::sys::weak::weak; use libc::{c_int, mode_t}; #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "solaris", - all(target_os = "linux", target_env = "gnu") + all(target_os = "linux", target_env = "gnu"), + target_vendor = "apple", ))] use libc::c_char; #[cfg(any( @@ -193,23 +187,16 @@ cfg_has_statx! {{ return Some(Err(err)); } - // Availability not checked yet. + // We're not yet entirely sure whether `statx` is usable on this kernel + // or not. Syscalls can return errors from things other than the kernel + // per se, e.g. `EPERM` can be returned if seccomp is used to block the + // syscall, or `ENOSYS` might be returned from a faulty FUSE driver. // - // First try the cheap way. - if err.raw_os_error() == Some(libc::ENOSYS) { - STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed); - return None; - } - - // Error other than `ENOSYS` is not a good enough indicator -- it is - // known that `EPERM` can be returned as a result of using seccomp to - // block the syscall. // Availability is checked by performing a call which expects `EFAULT` // if the syscall is usable. + // // See: https://github.com/rust-lang/rust/issues/65662 - // FIXME this can probably just do the call if `EPERM` was received, but - // previous iteration of the code checked it for all errors and for now - // this is retained. + // // FIXME what about transient conditions like `ENOMEM`? let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) .err() @@ -354,7 +341,7 @@ pub struct DirEntry { entry: dirent64, } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct OpenOptions { // generic read: bool, @@ -368,7 +355,7 @@ pub struct OpenOptions { mode: mode_t, } -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq)] pub struct FilePermissions { mode: mode_t, } @@ -377,11 +364,11 @@ pub struct FilePermissions { pub struct FileTimes { accessed: Option, modified: Option, - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] + #[cfg(target_vendor = "apple")] created: Option, } -#[derive(Copy, Clone, Eq, Debug)] +#[derive(Copy, Clone, Eq)] pub struct FileType { mode: mode_t, } @@ -398,11 +385,13 @@ impl core::hash::Hash for FileType { } } -#[derive(Debug)] pub struct DirBuilder { mode: mode_t, } +#[derive(Copy, Clone)] +struct Mode(mode_t); + cfg_has_statx! {{ impl FileAttr { fn from_stat64(stat: stat64) -> Self { @@ -517,7 +506,7 @@ impl FileAttr { #[cfg(any(target_os = "horizon", target_os = "hurd"))] pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(self.stat.st_mtim)) + SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64) } #[cfg(not(any( @@ -545,17 +534,10 @@ impl FileAttr { #[cfg(any(target_os = "horizon", target_os = "hurd"))] pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(self.stat.st_atim)) + SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64) } - #[cfg(any( - target_os = "freebsd", - target_os = "openbsd", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - ))] + #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_vendor = "apple"))] pub fn created(&self) -> io::Result { SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64) } @@ -563,11 +545,8 @@ impl FileAttr { #[cfg(not(any( target_os = "freebsd", target_os = "openbsd", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "vita", + target_vendor = "apple", )))] pub fn created(&self) -> io::Result { cfg_has_statx! { @@ -647,7 +626,7 @@ impl FileTimes { self.modified = Some(t); } - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] + #[cfg(target_vendor = "apple")] pub fn set_created(&mut self, t: SystemTime) { self.created = Some(t); } @@ -673,12 +652,26 @@ impl FileType { } } +impl fmt::Debug for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let FileType { mode } = self; + f.debug_struct("FileType").field("mode", &Mode(*mode)).finish() + } +} + impl FromInner for FilePermissions { fn from_inner(mode: u32) -> FilePermissions { FilePermissions { mode: mode as mode_t } } } +impl fmt::Debug for FilePermissions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let FilePermissions { mode } = self; + f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish() + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. @@ -836,8 +829,42 @@ impl Iterator for ReadDir { } } +/// Aborts the process if a file desceriptor is not open, if debug asserts are enabled +/// +/// Many IO syscalls can't be fully trusted about EBADF error codes because those +/// might get bubbled up from a remote FUSE server rather than the file descriptor +/// in the current process being invalid. +/// +/// So we check file flags instead which live on the file descriptor and not the underlying file. +/// The downside is that it costs an extra syscall, so we only do it for debug. +#[inline] +pub(crate) fn debug_assert_fd_is_open(fd: RawFd) { + use crate::sys::os::errno; + + // this is similar to assert_unsafe_precondition!() but it doesn't require const + if core::ub_checks::check_library_ub() { + if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF { + rtabort!("IO Safety violation: owned file descriptor already closed"); + } + } +} + impl Drop for Dir { fn drop(&mut self) { + // dirfd isn't supported everywhere + #[cfg(not(any( + miri, + target_os = "redox", + target_os = "nto", + target_os = "vita", + target_os = "hurd", + target_os = "espidf", + target_os = "fuchsia", + )))] + { + let fd = unsafe { libc::dirfd(self.0) }; + debug_assert_fd_is_open(fd); + } let r = unsafe { libc::closedir(self.0) }; assert!( r == 0 || crate::io::Error::last_os_error().is_interrupted(), @@ -934,10 +961,6 @@ impl DirEntry { } #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "linux", target_os = "emscripten", target_os = "android", @@ -954,6 +977,7 @@ impl DirEntry { target_os = "aix", target_os = "nto", target_os = "hurd", + target_vendor = "apple", ))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 @@ -970,14 +994,11 @@ impl DirEntry { } #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "netbsd", target_os = "openbsd", target_os = "freebsd", - target_os = "dragonfly" + target_os = "dragonfly", + target_vendor = "apple", ))] fn name_bytes(&self) -> &[u8] { use crate::slice; @@ -989,14 +1010,11 @@ impl DirEntry { } } #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "netbsd", target_os = "openbsd", target_os = "freebsd", - target_os = "dragonfly" + target_os = "dragonfly", + target_vendor = "apple", )))] fn name_bytes(&self) -> &[u8] { self.name_cstr().to_bytes() @@ -1116,6 +1134,23 @@ impl OpenOptions { } } +impl fmt::Debug for OpenOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } = + self; + f.debug_struct("OpenOptions") + .field("read", read) + .field("write", write) + .field("append", append) + .field("truncate", truncate) + .field("create", create) + .field("create_new", create_new) + .field("custom_flags", custom_flags) + .field("mode", &Mode(*mode)) + .finish() + } +} + impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { run_path_with_cstr(path, &|path| File::open_c(path, opts)) @@ -1157,21 +1192,11 @@ impl File { cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?; return Ok(()); - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - ))] + #[cfg(target_vendor = "apple")] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } - #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - )))] + #[cfg(not(target_vendor = "apple"))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fsync(fd) } @@ -1181,12 +1206,7 @@ impl File { cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; return Ok(()); - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - ))] + #[cfg(target_vendor = "apple")] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } @@ -1205,15 +1225,12 @@ impl File { #[cfg(not(any( target_os = "android", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", target_os = "linux", - target_os = "macos", target_os = "netbsd", target_os = "openbsd", - target_os = "watchos", target_os = "nto", target_os = "hurd", + target_vendor = "apple", )))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) @@ -1322,7 +1339,7 @@ impl File { io::ErrorKind::Unsupported, "setting file times not supported", )) - } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] { + } else if #[cfg(target_vendor = "apple")] { let mut buf = [mem::MaybeUninit::::uninit(); 3]; let mut num_times = 0; let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; @@ -1402,6 +1419,13 @@ impl DirBuilder { } } +impl fmt::Debug for DirBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let DirBuilder { mode } = self; + f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish() + } +} + impl AsInner for File { #[inline] fn as_inner(&self) -> &FileDesc { @@ -1574,6 +1598,73 @@ impl fmt::Debug for File { } } +// Format in octal, followed by the mode format used in `ls -l`. +// +// References: +// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html +// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html +// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html +// +// Example: +// 0o100664 (-rw-rw-r--) +impl fmt::Debug for Mode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(mode) = *self; + write!(f, "0o{mode:06o}")?; + + let entry_type = match mode & libc::S_IFMT { + libc::S_IFDIR => 'd', + libc::S_IFBLK => 'b', + libc::S_IFCHR => 'c', + libc::S_IFLNK => 'l', + libc::S_IFIFO => 'p', + libc::S_IFREG => '-', + _ => return Ok(()), + }; + + f.write_str(" (")?; + f.write_char(entry_type)?; + + // Owner permissions + f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?; + let owner_executable = mode & libc::S_IXUSR != 0; + let setuid = mode as c_int & libc::S_ISUID as c_int != 0; + f.write_char(match (owner_executable, setuid) { + (true, true) => 's', // executable and setuid + (false, true) => 'S', // setuid + (true, false) => 'x', // executable + (false, false) => '-', + })?; + + // Group permissions + f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?; + let group_executable = mode & libc::S_IXGRP != 0; + let setgid = mode as c_int & libc::S_ISGID as c_int != 0; + f.write_char(match (group_executable, setgid) { + (true, true) => 's', // executable and setgid + (false, true) => 'S', // setgid + (true, false) => 'x', // executable + (false, false) => '-', + })?; + + // Other permissions + f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?; + let other_executable = mode & libc::S_IXOTH != 0; + let sticky = mode as c_int & libc::S_ISVTX as c_int != 0; + f.write_char(match (entry_type, other_executable, sticky) { + ('d', true, true) => 't', // searchable and restricted deletion + ('d', false, true) => 'T', // restricted deletion + (_, true, _) => 'x', // executable + (_, false, _) => '-', + })?; + + f.write_char(')') + } +} + pub fn readdir(path: &Path) -> io::Result { let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; if ptr.is_null() { @@ -1780,14 +1871,7 @@ fn open_to_and_set_permissions( Ok((writer, writer_metadata)) } -#[cfg(not(any( - target_os = "linux", - target_os = "android", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", -)))] +#[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))] pub fn copy(from: &Path, to: &Path) -> io::Result { let (mut reader, reader_metadata) = open_from(from)?; let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; @@ -1813,43 +1897,13 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(target_vendor = "apple")] pub fn copy(from: &Path, to: &Path) -> io::Result { use crate::sync::atomic::{AtomicBool, Ordering}; - const COPYFILE_ACL: u32 = 1 << 0; - const COPYFILE_STAT: u32 = 1 << 1; - const COPYFILE_XATTR: u32 = 1 << 2; - const COPYFILE_DATA: u32 = 1 << 3; - - const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL; - const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR; - const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA; - - const COPYFILE_STATE_COPIED: u32 = 8; - - #[allow(non_camel_case_types)] - type copyfile_state_t = *mut libc::c_void; - #[allow(non_camel_case_types)] - type copyfile_flags_t = u32; - - extern "C" { - fn fcopyfile( - from: libc::c_int, - to: libc::c_int, - state: copyfile_state_t, - flags: copyfile_flags_t, - ) -> libc::c_int; - fn copyfile_state_alloc() -> copyfile_state_t; - fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int; - fn copyfile_state_get( - state: copyfile_state_t, - flag: u32, - dst: *mut libc::c_void, - ) -> libc::c_int; - } - - struct FreeOnDrop(copyfile_state_t); + const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA; + + struct FreeOnDrop(libc::copyfile_state_t); impl Drop for FreeOnDrop { fn drop(&mut self) { // The code below ensures that `FreeOnDrop` is never a null pointer @@ -1857,7 +1911,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { // `copyfile_state_free` returns -1 if the `to` or `from` files // cannot be closed. However, this is not considered this an // error. - copyfile_state_free(self.0); + libc::copyfile_state_free(self.0); } } } @@ -1866,6 +1920,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { // We store the availability in a global to avoid unnecessary syscalls static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); syscall! { + // Mirrors `libc::fclonefileat` fn fclonefileat( srcfd: libc::c_int, dst_dirfd: libc::c_int, @@ -1902,22 +1957,22 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { // We ensure that `FreeOnDrop` never contains a null pointer so it is // always safe to call `copyfile_state_free` let state = unsafe { - let state = copyfile_state_alloc(); + let state = libc::copyfile_state_alloc(); if state.is_null() { return Err(crate::io::Error::last_os_error()); } FreeOnDrop(state) }; - let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA }; + let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA }; - cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; + cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; let mut bytes_copied: libc::off_t = 0; cvt(unsafe { - copyfile_state_get( + libc::copyfile_state_get( state.0, - COPYFILE_STATE_COPIED, + libc::COPYFILE_STATE_COPIED as u32, core::ptr::addr_of_mut!(bytes_copied) as *mut libc::c_void, ) })?; diff --git a/library/std/src/sys/pal/unix/fs/tests.rs b/library/std/src/sys/pal/unix/fs/tests.rs new file mode 100644 index 0000000000000..71be3472148b0 --- /dev/null +++ b/library/std/src/sys/pal/unix/fs/tests.rs @@ -0,0 +1,71 @@ +use crate::sys::pal::unix::fs::FilePermissions; + +#[test] +fn test_debug_permissions() { + for (expected, mode) in [ + // typical directory + ("FilePermissions { mode: 0o040775 (drwxrwxr-x) }", 0o04_0775), + // typical text file + ("FilePermissions { mode: 0o100664 (-rw-rw-r--) }", 0o10_0664), + // setuid executable (/usr/bin/doas) + ("FilePermissions { mode: 0o104755 (-rwsr-xr-x) }", 0o10_4755), + // char device (/dev/zero) + ("FilePermissions { mode: 0o020666 (crw-rw-rw-) }", 0o02_0666), + // block device (/dev/vda) + ("FilePermissions { mode: 0o060660 (brw-rw----) }", 0o06_0660), + // symbolic link + ("FilePermissions { mode: 0o120777 (lrwxrwxrwx) }", 0o12_0777), + // fifo + ("FilePermissions { mode: 0o010664 (prw-rw-r--) }", 0o01_0664), + // none + ("FilePermissions { mode: 0o100000 (----------) }", 0o10_0000), + // unrecognized + ("FilePermissions { mode: 0o000001 }", 1), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode }), expected); + } + + for (expected, mode) in [ + // owner readable + ("FilePermissions { mode: 0o100400 (-r--------) }", libc::S_IRUSR), + // owner writable + ("FilePermissions { mode: 0o100200 (--w-------) }", libc::S_IWUSR), + // owner executable + ("FilePermissions { mode: 0o100100 (---x------) }", libc::S_IXUSR), + // setuid + ("FilePermissions { mode: 0o104000 (---S------) }", libc::S_ISUID), + // owner executable and setuid + ("FilePermissions { mode: 0o104100 (---s------) }", libc::S_IXUSR | libc::S_ISUID), + // group readable + ("FilePermissions { mode: 0o100040 (----r-----) }", libc::S_IRGRP), + // group writable + ("FilePermissions { mode: 0o100020 (-----w----) }", libc::S_IWGRP), + // group executable + ("FilePermissions { mode: 0o100010 (------x---) }", libc::S_IXGRP), + // setgid + ("FilePermissions { mode: 0o102000 (------S---) }", libc::S_ISGID), + // group executable and setgid + ("FilePermissions { mode: 0o102010 (------s---) }", libc::S_IXGRP | libc::S_ISGID), + // other readable + ("FilePermissions { mode: 0o100004 (-------r--) }", libc::S_IROTH), + // other writeable + ("FilePermissions { mode: 0o100002 (--------w-) }", libc::S_IWOTH), + // other executable + ("FilePermissions { mode: 0o100001 (---------x) }", libc::S_IXOTH), + // sticky + ("FilePermissions { mode: 0o101000 (----------) }", libc::S_ISVTX), + // other executable and sticky + ("FilePermissions { mode: 0o101001 (---------x) }", libc::S_IXOTH | libc::S_ISVTX), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFREG | mode }), expected); + } + + for (expected, mode) in [ + // restricted deletion ("sticky") flag is set, and search permission is not granted to others + ("FilePermissions { mode: 0o041000 (d--------T) }", libc::S_ISVTX), + // sticky and searchable + ("FilePermissions { mode: 0o041001 (d--------t) }", libc::S_ISVTX | libc::S_IXOTH), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFDIR | mode }), expected); + } +} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index f608ae47a21fb..01d7fb31d7d6c 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -78,15 +78,12 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "emscripten", target_os = "fuchsia", target_os = "vxworks", - // The poll on Darwin doesn't set POLLNVAL for closed fds. - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "redox", target_os = "l4re", target_os = "horizon", target_os = "vita", + // The poll on Darwin doesn't set POLLNVAL for closed fds. + target_vendor = "apple", )))] 'poll: { use crate::sys::os::errno; @@ -405,7 +402,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "macos")] { #[link(name = "System")] extern "C" {} - } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] { + } else if #[cfg(all(target_vendor = "apple", not(target_os = "macos")))] { #[link(name = "System")] #[link(name = "objc")] #[link(name = "Foundation", kind = "framework")] @@ -432,6 +429,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 09750b6ffc897..7237989c9059b 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 { @@ -453,6 +447,37 @@ impl Socket { Ok(raw as u32) } + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + if !name.to_bytes().is_empty() { + const AF_NAME_MAX: usize = 16; + let mut buf = [0; AF_NAME_MAX]; + for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { + *dst = *src as libc::c_char; + } + let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; + arg.af_name = buf; + setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) + } else { + setsockopt( + self, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + core::ptr::null_mut() as *mut c_void, + ) + } + } + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn acceptfilter(&self) -> io::Result<&CStr> { + let arg: libc::accept_filter_arg = + getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; + let s: &[u8] = + unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; + let name = CStr::from_bytes_with_nul(s).unwrap(); + Ok(name) + } + #[cfg(any(target_os = "android", target_os = "linux",))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 0b9c8027e6fc3..3a281525f8df3 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -63,16 +63,7 @@ extern "C" { )] #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] - #[cfg_attr( - any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "freebsd", - target_os = "watchos" - ), - link_name = "__error" - )] + #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")] #[cfg_attr(target_os = "haiku", link_name = "_errnop")] #[cfg_attr(target_os = "aix", link_name = "_Errno")] fn errno_location() -> *mut c_int; @@ -430,7 +421,7 @@ pub fn current_exe() -> io::Result { Ok(PathBuf::from(OsString::from_vec(e))) } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] +#[cfg(target_vendor = "apple")] pub fn current_exe() -> io::Result { unsafe { let mut sz: u32 = 0; @@ -696,30 +687,26 @@ pub fn home_dir() -> Option { #[cfg(any( target_os = "android", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "emscripten", target_os = "redox", target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita", + all(target_vendor = "apple", not(target_os = "macos")), ))] unsafe fn fallback() -> Option { None } #[cfg(not(any( target_os = "android", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "emscripten", target_os = "redox", target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita", + all(target_vendor = "apple", not(target_os = "macos")), )))] unsafe fn fallback() -> Option { let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index f017d39d804aa..f2947161cd56d 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -11,17 +11,6 @@ use crate::os::linux::process::PidFd; #[cfg(target_os = "linux")] use crate::os::unix::io::AsRawFd; -#[cfg(any( - target_os = "macos", - target_os = "watchos", - target_os = "tvos", - target_os = "freebsd", - all(target_os = "linux", target_env = "gnu"), - all(target_os = "linux", target_env = "musl"), - target_os = "nto", -))] -use crate::sys::weak::weak; - #[cfg(target_os = "vxworks")] use libc::RTP_ID as pid_t; @@ -431,13 +420,11 @@ impl Command { } #[cfg(not(any( - target_os = "macos", - target_os = "tvos", - target_os = "watchos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), target_os = "nto", + target_vendor = "apple", )))] fn posix_spawn( &mut self, @@ -450,14 +437,11 @@ impl Command { // Only support platforms for which posix_spawn() can return ENOENT // directly. #[cfg(any( - target_os = "macos", - // FIXME: `target_os = "ios"`? - target_os = "tvos", - target_os = "watchos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), target_os = "nto", + target_vendor = "apple", ))] fn posix_spawn( &mut self, @@ -465,6 +449,7 @@ impl Command { envp: Option<&CStringArray>, ) -> io::Result> { use crate::mem::MaybeUninit; + use crate::sys::weak::weak; use crate::sys::{self, cvt_nz, unix_sigpipe_attr_specified}; if self.get_gid().is_some() @@ -540,7 +525,7 @@ impl Command { } let addchdir = match self.get_cwd() { Some(cwd) => { - if cfg!(any(target_os = "macos", target_os = "tvos", target_os = "watchos")) { + if cfg!(target_vendor = "apple") { // There is a bug in macOS where a relative executable // path like "../myprogram" will cause `posix_spawn` to // successfully launch the program, but erroneously return @@ -1050,24 +1035,20 @@ fn signal_string(signal: i32) -> &'static str { #[cfg(any(target_os = "linux", target_os = "nto"))] libc::SIGPWR => " (SIGPWR)", #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", target_os = "nto", + target_vendor = "apple", ))] libc::SIGEMT => " (SIGEMT)", #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "dragonfly" + target_os = "dragonfly", + target_vendor = "apple", ))] libc::SIGINFO => " (SIGINFO)", #[cfg(target_os = "hurd")] diff --git a/library/std/src/sys/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs index c9ed6825f6c21..d8f227b4ef444 100644 --- a/library/std/src/sys/pal/unix/rand.rs +++ b/library/std/src/sys/pal/unix/rand.rs @@ -12,10 +12,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { #[cfg(all( unix, - not(target_os = "macos"), - not(target_os = "ios"), - not(target_os = "tvos"), - not(target_os = "watchos"), not(target_os = "openbsd"), not(target_os = "netbsd"), not(target_os = "fuchsia"), @@ -23,6 +19,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { not(target_os = "vxworks"), not(target_os = "emscripten"), not(target_os = "vita"), + not(target_vendor = "apple"), ))] mod imp { use crate::fs::File; @@ -62,17 +59,23 @@ mod imp { unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } } - #[cfg(any( - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - netbsd10 - ))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd", netbsd10))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } } + #[cfg(target_os = "dragonfly")] + fn getrandom(buf: &mut [u8]) -> libc::ssize_t { + extern "C" { + fn getrandom( + buf: *mut libc::c_void, + buflen: libc::size_t, + flags: libc::c_uint, + ) -> libc::ssize_t; + } + unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } + } + #[cfg(not(any( target_os = "linux", target_os = "android", diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 78a599077c758..26c49257ad00d 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -11,7 +11,7 @@ pub struct Handler { impl Handler { pub unsafe fn new() -> Handler { - make_handler() + make_handler(false) } fn null() -> Handler { @@ -29,34 +29,41 @@ impl Drop for Handler { #[cfg(any( target_os = "linux", - target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", - target_os = "solaris", - target_os = "illumos", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris" ))] mod imp { use super::Handler; + use crate::cell::Cell; use crate::io; use crate::mem; + use crate::ops::Range; use crate::ptr; + use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + use crate::sys::pal::unix::os; use crate::thread; - use libc::MAP_FAILED; #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, munmap}; + use libc::{mmap as mmap64, mprotect, munmap}; #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, munmap}; - use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; + use libc::{mmap64, mprotect, munmap}; + use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV, SIG_DFL}; use libc::{sigaltstack, SS_DISABLE}; - use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV}; + use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; - use crate::sys::pal::unix::os::page_size; - use crate::sys_common::thread_info; + // We use a TLS variable to store the address of the guard page. While TLS + // variables are not guaranteed to be signal-safe, this works out in practice + // since we make sure to write to the variable before the signal stack is + // installed, thereby ensuring that the variable is always allocated when + // the signal handler is called. + thread_local! { + // FIXME: use `Range` once that implements `Copy`. + static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; + } // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages // (unmapped pages) at the end of every thread's stack, so if a thread ends @@ -84,12 +91,12 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let guard = thread_info::stack_guard().unwrap_or(0..0); + let (start, end) = GUARD.get(); let addr = (*info).si_addr() as usize; // If the faulting address is within the guard page, then we print a // message saying so and abort. - if guard.start <= addr && addr < guard.end { + if start <= addr && addr < end { rtprintpanic!( "\nthread '{}' has overflowed its stack\n", thread::current().name().unwrap_or("") @@ -105,10 +112,17 @@ mod imp { } } + static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); pub unsafe fn init() { + PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); + + // Always write to GUARD to ensure the TLS variable is allocated. + let guard = install_main_guard().unwrap_or(0..0); + GUARD.set((guard.start, guard.end)); + let mut action: sigaction = mem::zeroed(); for &signal in &[SIGSEGV, SIGBUS] { sigaction(signal, ptr::null_mut(), &mut action); @@ -121,7 +135,7 @@ mod imp { } } - let handler = make_handler(); + let handler = make_handler(true); MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); } @@ -150,7 +164,7 @@ mod imp { let flags = MAP_PRIVATE | MAP_ANON; let sigstack_size = sigstack_size(); - let page_size = page_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); let stackp = mmap64( ptr::null_mut(), @@ -172,10 +186,17 @@ mod imp { libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } } - pub unsafe fn make_handler() -> Handler { + pub unsafe fn make_handler(main_thread: bool) -> Handler { if !NEED_ALTSTACK.load(Ordering::Relaxed) { return Handler::null(); } + + if !main_thread { + // Always write to GUARD to ensure the TLS variable is allocated. + let guard = current_guard().unwrap_or(0..0); + GUARD.set((guard.start, guard.end)); + } + let mut stack = mem::zeroed(); sigaltstack(ptr::null(), &mut stack); // Configure alternate signal stack, if one is not already set. @@ -191,7 +212,7 @@ mod imp { pub unsafe fn drop_handler(data: *mut libc::c_void) { if !data.is_null() { let sigstack_size = sigstack_size(); - let page_size = page_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); let stack = libc::stack_t { ss_sp: ptr::null_mut(), ss_flags: SS_DISABLE, @@ -225,25 +246,266 @@ mod imp { fn sigstack_size() -> usize { libc::SIGSTKSZ } + + #[cfg(target_os = "solaris")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::stack_getbounds(&mut current_stack), 0); + Some(current_stack.ss_sp) + } + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let th = libc::pthread_self(); + let stackptr = libc::pthread_get_stackaddr_np(th); + Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) + } + + #[cfg(target_os = "openbsd")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); + + let stack_ptr = current_stack.ss_sp; + let stackaddr = if libc::pthread_main_np() == 1 { + // main thread + stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) + } else { + // new thread + stack_ptr.addr() - current_stack.ss_size + }; + Some(stack_ptr.with_addr(stackaddr)) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", + target_os = "hurd", + target_os = "linux", + target_os = "l4re" + ))] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + #[cfg(target_os = "freebsd")] + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut stackaddr = crate::ptr::null_mut(); + let mut stacksize = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); + ret = Some(stackaddr); + } + if e == 0 || cfg!(target_os = "freebsd") { + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + } + ret + } + + unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + + // Ensure stackaddr is page aligned! A parent process might + // have reset RLIMIT_STACK to be non-page aligned. The + // pthread_attr_getstack() reports the usable stack area + // stackaddr < stackaddr + stacksize, so if stackaddr is not + // page-aligned, calculate the fix such that stackaddr < + // new_page_aligned_stackaddr < stackaddr + stacksize + let remainder = stackaddr % page_size; + Some(if remainder == 0 { + stackptr + } else { + stackptr.with_addr(stackaddr + page_size - remainder) + }) + } + + unsafe fn install_main_guard() -> Option> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + // Linux doesn't allocate the whole stack right away, and + // the kernel has its own stack-guard mechanism to fault + // when growing too close to an existing mapping. If we map + // our own guard, then the kernel starts enforcing a rather + // large gap above that, rendering much of the possible + // stack space useless. See #43052. + // + // Instead, we'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + // For the main thread, the musl's pthread_attr_getstack + // returns the current stack size, rather than maximum size + // it can eventually grow to. It cannot be used to determine + // the position of kernel's stack guard. + None + } else if cfg!(target_os = "freebsd") { + // FreeBSD's stack autogrows, and optionally includes a guard page + // at the bottom. If we try to remap the bottom of the stack + // ourselves, FreeBSD's guard page moves upwards. So we'll just use + // the builtin guard page. + let stackptr = get_stack_start_aligned()?; + let guardaddr = stackptr.addr(); + // Technically the number of guard pages is tunable and controlled + // by the security.bsd.stack_guard_page sysctl. + // By default it is 1, checking once is enough since it is + // a boot time config value. + static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new(); + + let pages = PAGES.get_or_init(|| { + use crate::sys::weak::dlsym; + dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); + let mut guard: usize = 0; + let mut size = crate::mem::size_of_val(&guard); + let oid = crate::ffi::CStr::from_bytes_with_nul( + b"security.bsd.stack_guard_page\0", + ) + .unwrap(); + match sysctlbyname.get() { + Some(fcn) => { + if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { + guard + } else { + 1 + } + }, + _ => 1, + } + }); + Some(guardaddr..guardaddr + pages * page_size) + } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { + // OpenBSD stack already includes a guard page, and stack is + // immutable. + // NetBSD stack includes the guard page. + // + // We'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = get_stack_start_aligned()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } else { + // Reallocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + // Systems which enforce strict PAX MPROTECT do not allow + // to mprotect() a mapping with less restrictive permissions + // than the initial mmap() used, so we mmap() here with + // read/write permissions and only then mprotect() it to + // no permissions at all. See issue #50313. + let stackptr = get_stack_start_aligned()?; + let result = mmap64( + stackptr, + page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0, + ); + if result != stackptr || result == MAP_FAILED { + panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); + } + + let result = mprotect(stackptr, page_size, PROT_NONE); + if result != 0 { + panic!("failed to protect the guard page: {}", io::Error::last_os_error()); + } + + let guardaddr = stackptr.addr(); + + Some(guardaddr..guardaddr + page_size) + } + } + + #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + unsafe fn current_guard() -> Option> { + let stackptr = get_stack_start()?; + let stackaddr = stackptr.addr(); + Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) + } + + #[cfg(any( + target_os = "android", + target_os = "freebsd", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "l4re" + ))] + unsafe fn current_guard() -> Option> { + let mut ret = None; + let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); + #[cfg(target_os = "freebsd")] + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + #[cfg(target_os = "freebsd")] + let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); + #[cfg(not(target_os = "freebsd"))] + let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); + if e == 0 { + let mut guardsize = 0; + assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); + if guardsize == 0 { + if cfg!(all(target_os = "linux", target_env = "musl")) { + // musl versions before 1.1.19 always reported guard + // size obtained from pthread_attr_get_np as zero. + // Use page size as a fallback. + guardsize = PAGE_SIZE.load(Ordering::Relaxed); + } else { + panic!("there is no guard page"); + } + } + let mut stackptr = crate::ptr::null_mut::(); + let mut size = 0; + assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); + + let stackaddr = stackptr.addr(); + ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + Some(stackaddr - guardsize..stackaddr) + } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) + { + // glibc used to include the guard area within the stack, as noted in the BUGS + // section of `man pthread_attr_getguardsize`. This has been corrected starting + // with glibc 2.27, and in some distro backports, so the guard is now placed at the + // end (below) the stack. There's no easy way for us to know which we have at + // runtime, so we'll just match any fault in the range right above or below the + // stack base to call that fault a stack overflow. + Some(stackaddr - guardsize..stackaddr + guardsize) + } else { + Some(stackaddr..stackaddr + guardsize) + }; + } + if e == 0 || cfg!(target_os = "freebsd") { + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + } + ret + } } #[cfg(not(any( target_os = "linux", - target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", - target_os = "solaris", - target_os = "illumos", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_os = "solaris" )))] mod imp { pub unsafe fn init() {} pub unsafe fn cleanup() {} - pub unsafe fn make_handler() -> super::Handler { + pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { super::Handler::null() } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 6520ca9fc48ef..853ef8736de24 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -1,5 +1,5 @@ use crate::cmp; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::mem; use crate::num::NonZero; @@ -150,7 +150,7 @@ impl Thread { } } - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] + #[cfg(target_vendor = "apple")] pub fn set_name(name: &CStr) { unsafe { let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); @@ -182,8 +182,11 @@ impl Thread { if let Some(f) = pthread_setname_np.get() { #[cfg(target_os = "nto")] - let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name); + const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + const THREAD_NAME_MAX: usize = 32; + let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; debug_assert_eq!(res, 0); } @@ -225,44 +228,6 @@ impl Thread { // Newlib, Emscripten, and VxWorks have no way to set a thread name. } - #[cfg(target_os = "linux")] - pub fn get_name() -> Option { - const TASK_COMM_LEN: usize = 16; - let mut name = vec![0u8; TASK_COMM_LEN]; - let res = unsafe { - libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) - }; - if res != 0 { - return None; - } - name.truncate(name.iter().position(|&c| c == 0)?); - CString::new(name).ok() - } - - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] - pub fn get_name() -> Option { - let mut name = vec![0u8; libc::MAXTHREADNAMESIZE]; - let res = unsafe { - libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) - }; - if res != 0 { - return None; - } - name.truncate(name.iter().position(|&c| c == 0)?); - CString::new(name).ok() - } - - #[cfg(not(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos" - )))] - pub fn get_name() -> Option { - None - } - #[cfg(not(target_os = "espidf"))] pub fn sleep(dur: Duration) { let mut secs = dur.as_secs(); @@ -330,11 +295,10 @@ impl Drop for Thread { #[cfg(any( target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "nto", + target_os = "solaris", + target_os = "illumos", + target_vendor = "apple", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -351,13 +315,9 @@ pub fn available_parallelism() -> io::Result> { target_os = "emscripten", target_os = "fuchsia", target_os = "hurd", - target_os = "ios", - target_os = "tvos", target_os = "linux", - target_os = "macos", - target_os = "solaris", - target_os = "illumos", target_os = "aix", + target_vendor = "apple", ))] { #[allow(unused_assignments)] #[allow(unused_mut)] @@ -384,7 +344,7 @@ pub fn available_parallelism() -> io::Result> { } 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 @@ -426,7 +386,7 @@ pub fn available_parallelism() -> io::Result> { if !set.is_null() { let mut count: usize = 0; if libc::pthread_getaffinity_np(libc::pthread_self(), libc::_cpuset_size(set), set) == 0 { - for i in 0..u64::MAX { + for i in 0..libc::cpuid_t::MAX { match libc::_cpuset_isset(i, set) { -1 => break, 0 => continue, @@ -467,7 +427,7 @@ pub fn available_parallelism() -> io::Result> { 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); } } @@ -480,9 +440,15 @@ pub fn available_parallelism() -> io::Result> { } 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::Error::UNKNOWN_THREAD_COUNT); + } + Ok(unsafe { NonZero::new_unchecked(cpus as usize) }) } else if #[cfg(target_os = "haiku")] { // system_info cpu_count field gets the static data set at boot time with `smp_set_num_cpus` // `get_system_info` calls then `smp_get_num_cpus` @@ -491,7 +457,7 @@ pub fn available_parallelism() -> io::Result> { 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)) @@ -725,309 +691,13 @@ mod cgroups { } } -#[cfg(all( - not(target_os = "linux"), - not(target_os = "freebsd"), - not(target_os = "hurd"), - not(target_os = "macos"), - not(target_os = "netbsd"), - not(target_os = "openbsd"), - not(target_os = "solaris") -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} - -#[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris" -))] -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, mprotect}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, mprotect}; - use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; - - use crate::io; - use crate::ops::Range; - use crate::sync::atomic::{AtomicUsize, Ordering}; - use crate::sys::os; - - // This is initialized in init() and only read from after - static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); - - pub type Guard = Range; - - #[cfg(target_os = "solaris")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::stack_getbounds(&mut current_stack), 0); - Some(current_stack.ss_sp) - } - - #[cfg(target_os = "macos")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let th = libc::pthread_self(); - let stackptr = libc::pthread_get_stackaddr_np(th); - Some(stackptr.map_addr(|addr| addr - libc::pthread_get_stacksize_np(th))) - } - - #[cfg(target_os = "openbsd")] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut current_stack: libc::stack_t = crate::mem::zeroed(); - assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); - - let stack_ptr = current_stack.ss_sp; - let stackaddr = if libc::pthread_main_np() == 1 { - // main thread - stack_ptr.addr() - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) - } else { - // new thread - stack_ptr.addr() - current_stack.ss_size - }; - Some(stack_ptr.with_addr(stackaddr)) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", - target_os = "hurd", - target_os = "linux", - target_os = "l4re" - ))] - unsafe fn get_stack_start() -> Option<*mut libc::c_void> { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut stackaddr = crate::ptr::null_mut(); - let mut stacksize = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0); - ret = Some(stackaddr); - } - if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - } - ret - } - - // Precondition: PAGE_SIZE is initialized. - unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - assert!(page_size != 0); - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - - // Ensure stackaddr is page aligned! A parent process might - // have reset RLIMIT_STACK to be non-page aligned. The - // pthread_attr_getstack() reports the usable stack area - // stackaddr < stackaddr + stacksize, so if stackaddr is not - // page-aligned, calculate the fix such that stackaddr < - // new_page_aligned_stackaddr < stackaddr + stacksize - let remainder = stackaddr % page_size; - Some(if remainder == 0 { - stackptr - } else { - stackptr.with_addr(stackaddr + page_size - remainder) - }) - } - - pub unsafe fn init() -> Option { - let page_size = os::page_size(); - PAGE_SIZE.store(page_size, Ordering::Relaxed); - - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { - // Linux doesn't allocate the whole stack right away, and - // the kernel has its own stack-guard mechanism to fault - // when growing too close to an existing mapping. If we map - // our own guard, then the kernel starts enforcing a rather - // large gap above that, rendering much of the possible - // stack space useless. See #43052. - // - // Instead, we'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - // For the main thread, the musl's pthread_attr_getstack - // returns the current stack size, rather than maximum size - // it can eventually grow to. It cannot be used to determine - // the position of kernel's stack guard. - None - } else if cfg!(target_os = "freebsd") { - // FreeBSD's stack autogrows, and optionally includes a guard page - // at the bottom. If we try to remap the bottom of the stack - // ourselves, FreeBSD's guard page moves upwards. So we'll just use - // the builtin guard page. - let stackptr = get_stack_start_aligned()?; - let guardaddr = stackptr.addr(); - // Technically the number of guard pages is tunable and controlled - // by the security.bsd.stack_guard_page sysctl. - // By default it is 1, checking once is enough since it is - // a boot time config value. - static LOCK: crate::sync::OnceLock = crate::sync::OnceLock::new(); - let guard = guardaddr - ..guardaddr - + *LOCK.get_or_init(|| { - use crate::sys::weak::dlsym; - dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); - let mut guard: usize = 0; - let mut size = crate::mem::size_of_val(&guard); - let oid = crate::ffi::CStr::from_bytes_with_nul( - b"security.bsd.stack_guard_page\0", - ) - .unwrap(); - match sysctlbyname.get() { - Some(fcn) => { - if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { - return guard; - } - return 1; - }, - _ => { return 1; } - } - }) * page_size; - Some(guard) - } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { - // OpenBSD stack already includes a guard page, and stack is - // immutable. - // NetBSD stack includes the guard page. - // - // We'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else { - // Reallocate the last page of the stack. - // This ensures SIGBUS will be raised on - // stack overflow. - // Systems which enforce strict PAX MPROTECT do not allow - // to mprotect() a mapping with less restrictive permissions - // than the initial mmap() used, so we mmap() here with - // read/write permissions and only then mprotect() it to - // no permissions at all. See issue #50313. - let stackptr = get_stack_start_aligned()?; - let result = mmap64( - stackptr, - page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, - 0, - ); - if result != stackptr || result == MAP_FAILED { - panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); - } - - let result = mprotect(stackptr, page_size, PROT_NONE); - if result != 0 { - panic!("failed to protect the guard page: {}", io::Error::last_os_error()); - } - - let guardaddr = stackptr.addr(); - - Some(guardaddr..guardaddr + page_size) - } - } - - #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] - pub unsafe fn current() -> Option { - let stackptr = get_stack_start()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..stackaddr) - } - - #[cfg(any( - target_os = "android", - target_os = "freebsd", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "l4re" - ))] - pub unsafe fn current() -> Option { - let mut ret = None; - let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); - #[cfg(target_os = "freebsd")] - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - #[cfg(target_os = "freebsd")] - let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr); - #[cfg(not(target_os = "freebsd"))] - let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr); - if e == 0 { - let mut guardsize = 0; - assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0); - if guardsize == 0 { - if cfg!(all(target_os = "linux", target_env = "musl")) { - // musl versions before 1.1.19 always reported guard - // size obtained from pthread_attr_get_np as zero. - // Use page size as a fallback. - guardsize = PAGE_SIZE.load(Ordering::Relaxed); - } else { - panic!("there is no guard page"); - } - } - let mut stackptr = crate::ptr::null_mut::(); - let mut size = 0; - assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); - - let stackaddr = stackptr.addr(); - ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - Some(stackaddr - guardsize..stackaddr) - } else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc"))) - { - // glibc used to include the guard area within the stack, as noted in the BUGS - // section of `man pthread_attr_getguardsize`. This has been corrected starting - // with glibc 2.27, and in some distro backports, so the guard is now placed at the - // end (below) the stack. There's no easy way for us to know which we have at - // runtime, so we'll just match any fault in the range right above or below the - // stack base to call that fault a stack overflow. - Some(stackaddr - guardsize..stackaddr + guardsize) - } else { - Some(stackaddr..stackaddr + guardsize) - }; - } - if e == 0 || cfg!(target_os = "freebsd") { - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - } - ret - } -} - // glibc >= 2.15 has a __pthread_get_minstack() function that returns // PTHREAD_STACK_MIN plus bytes needed for thread-local storage. // We need that information to avoid blowing up when a small stack // is created in an application with big thread-local storage requirements. // See #6233 for rationale and details. #[cfg(all(target_os = "linux", target_env = "gnu"))] -fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { +unsafe fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { // We use dlsym to avoid an ELF version dependency on GLIBC_PRIVATE. (#23628) // We shouldn't really be using such an internal symbol, but there's currently // no other way to account for the TLS size. @@ -1041,11 +711,11 @@ fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { // No point in looking up __pthread_get_minstack() on non-glibc platforms. #[cfg(all(not(all(target_os = "linux", target_env = "gnu")), not(target_os = "netbsd")))] -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { +unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { libc::PTHREAD_STACK_MIN } #[cfg(target_os = "netbsd")] -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { +unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { 2048 // just a guess } diff --git a/library/std/src/sys/pal/unix/thread_local_dtor.rs b/library/std/src/sys/pal/unix/thread_local_dtor.rs index 79b152cece945..75db6e112ed35 100644 --- a/library/std/src/sys/pal/unix/thread_local_dtor.rs +++ b/library/std/src/sys/pal/unix/thread_local_dtor.rs @@ -35,7 +35,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { #[cfg(not(sanitizer_cfi_normalize_integers))] #[cfi_encoding = "i"] #[repr(transparent)] - pub struct c_int(pub libc::c_int); + pub struct c_int(#[allow(dead_code)] pub libc::c_int); extern "C" { #[linkage = "extern_weak"] @@ -76,7 +76,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { // workaround below is to register, via _tlv_atexit, a custom DTOR list once per // thread. thread_local dtors are pushed to the DTOR list without calling // _tlv_atexit. -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] +#[cfg(target_vendor = "apple")] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::cell::{Cell, RefCell}; use crate::ptr; diff --git a/library/std/src/sys/pal/unix/thread_parking/mod.rs b/library/std/src/sys/pal/unix/thread_parking/mod.rs index 185333c072f49..c7fa39f07b640 100644 --- a/library/std/src/sys/pal/unix/thread_parking/mod.rs +++ b/library/std/src/sys/pal/unix/thread_parking/mod.rs @@ -11,15 +11,7 @@ )))] cfg_if::cfg_if! { - if #[cfg(all( - any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos", - ), - not(miri), - ))] { + if #[cfg(all(target_vendor = "apple", not(miri)))] { mod darwin; pub use darwin::Parker; } else if #[cfg(target_os = "netbsd")] { diff --git a/library/std/src/sys/pal/unix/thread_parking/pthread.rs b/library/std/src/sys/pal/unix/thread_parking/pthread.rs index ae805d8439945..8e295453d7675 100644 --- a/library/std/src/sys/pal/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/pal/unix/thread_parking/pthread.rs @@ -5,7 +5,7 @@ use crate::marker::PhantomPinned; use crate::pin::Pin; use crate::ptr::addr_of_mut; use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; #[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; #[cfg(target_os = "nto")] @@ -43,14 +43,7 @@ unsafe fn wait_timeout( ) { // Use the system clock on systems that do not support pthread_condattr_setclock. // This unfortunately results in problems when the system time changes. - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "espidf", - target_os = "horizon", - ))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_vendor = "apple"))] let (now, dur) = { use crate::cmp::min; use crate::sys::time::SystemTime; @@ -71,14 +64,7 @@ unsafe fn wait_timeout( (now, dur) }; // Use the monotonic clock on other systems. - #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", - target_os = "espidf", - target_os = "horizon", - )))] + #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_vendor = "apple")))] let (now, dur) = { use crate::sys::time::Timespec; @@ -120,14 +106,11 @@ impl Parker { cfg_if::cfg_if! { if #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "l4re", target_os = "android", target_os = "redox", target_os = "vita", + target_vendor = "apple", ))] { addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { @@ -150,16 +133,18 @@ impl Parker { // This implementation doesn't require `unsafe`, but other implementations // may assume this is only called by the thread that owns the Parker. + // + // For memory ordering, see std/src/sys_common/thread_parking/futex.rs pub unsafe fn park(self: Pin<&Self>) { // If we were previously notified then we consume this notification and // return quickly. - if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { + if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed).is_ok() { return; } // Otherwise we need to coordinate going to sleep lock(self.lock.get()); - match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + match self.state.compare_exchange(EMPTY, PARKED, Relaxed, Relaxed) { Ok(_) => {} Err(NOTIFIED) => { // We must read here, even though we know it will be `NOTIFIED`. @@ -168,7 +153,7 @@ impl Parker { // acquire operation that synchronizes with that `unpark` to observe // any writes it made before the call to unpark. To do that we must // read from the write it made to `state`. - let old = self.state.swap(EMPTY, SeqCst); + let old = self.state.swap(EMPTY, Acquire); unlock(self.lock.get()); @@ -185,7 +170,7 @@ impl Parker { loop { wait(self.cvar.get(), self.lock.get()); - match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) { + match self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed) { Ok(_) => break, // got a notification Err(_) => {} // spurious wakeup, go back to sleep } @@ -201,16 +186,16 @@ impl Parker { // Like `park` above we have a fast path for an already-notified thread, and // afterwards we start coordinating for a sleep. // return quickly. - if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { + if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed).is_ok() { return; } lock(self.lock.get()); - match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + match self.state.compare_exchange(EMPTY, PARKED, Relaxed, Relaxed) { Ok(_) => {} Err(NOTIFIED) => { // We must read again here, see `park`. - let old = self.state.swap(EMPTY, SeqCst); + let old = self.state.swap(EMPTY, Acquire); unlock(self.lock.get()); assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); @@ -228,7 +213,7 @@ impl Parker { // parked. wait_timeout(self.cvar.get(), self.lock.get(), dur); - match self.state.swap(EMPTY, SeqCst) { + match self.state.swap(EMPTY, Acquire) { NOTIFIED => unlock(self.lock.get()), // got a notification, hurray! PARKED => unlock(self.lock.get()), // no notification, alas n => { @@ -245,7 +230,7 @@ impl Parker { // `state` is already `NOTIFIED`. That is why this must be a swap // rather than a compare-and-swap that returns if it reads `NOTIFIED` // on failure. - match self.state.swap(NOTIFIED, SeqCst) { + match self.state.swap(NOTIFIED, Release) { EMPTY => return, // no one was waiting NOTIFIED => return, // already unparked PARKED => {} // gotta go wake someone up diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 0440f33ded139..535fe6b27d91e 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -86,12 +86,7 @@ impl Timespec { // Please note that Apple OS nonetheless accepts the standard unix format when // setting file times, which makes this compensation round-trippable and generally // transparent. - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos" - ))] + #[cfg(target_vendor = "apple")] let (tv_sec, tv_nsec) = if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) { (tv_sec - 1, tv_nsec + 1_000_000_000) @@ -274,19 +269,9 @@ impl Instant { // // Instant on macos was historically implemented using mach_absolute_time; // we preserve this value domain out of an abundance of caution. - #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos" - ))] + #[cfg(target_vendor = "apple")] const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW; - #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "watchos", - target_os = "tvos" - )))] + #[cfg(not(target_vendor = "apple"))] const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC; Instant { t: Timespec::now(clock_id) } } diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index 48cc8633e93d2..4765a286e6b9e 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -28,7 +28,7 @@ use crate::ptr; use crate::sync::atomic::{self, AtomicPtr, Ordering}; // We can use true weak linkage on ELF targets. -#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos")))] +#[cfg(all(unix, not(target_vendor = "apple")))] pub(crate) macro weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( let ref $name: ExternWeak $ret> = { @@ -43,7 +43,7 @@ pub(crate) macro weak { } // On non-ELF targets, use the dlsym approximation of weak linkage. -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] +#[cfg(target_vendor = "apple")] pub(crate) use self::dlsym as weak; pub(crate) struct ExternWeak { diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs index 4f44db610af15..76f80291f0ea8 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() -> std_io::Result { } 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/unsupported/net.rs b/library/std/src/sys/pal/unsupported/net.rs index 931fe9ba24655..87e6106468fdb 100644 --- a/library/std/src/sys/pal/unsupported/net.rs +++ b/library/std/src/sys/pal/unsupported/net.rs @@ -346,6 +346,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -358,6 +359,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/unsupported/process.rs b/library/std/src/sys/pal/unsupported/process.rs index 6a989dd3e76bb..2445d9073dbb9 100644 --- a/library/std/src/sys/pal/unsupported/process.rs +++ b/library/std/src/sys/pal/unsupported/process.rs @@ -1,7 +1,6 @@ -use crate::ffi::OsStr; +use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; -use crate::marker::PhantomData; use crate::num::NonZero; use crate::path::Path; use crate::sys::fs::File; @@ -16,7 +15,14 @@ pub use crate::ffi::OsString as EnvKey; //////////////////////////////////////////////////////////////////////////////// pub struct Command { + program: OsString, + args: Vec, env: CommandEnv, + + cwd: Option, + stdin: Option, + stdout: Option, + stderr: Option, } // passed back to std::process with the pipes connected to the child, if any @@ -27,39 +33,62 @@ pub struct StdioPipes { pub stderr: Option, } -// FIXME: This should be a unit struct, so we can always construct it -// The value here should be never used, since we cannot spawn processes. +#[derive(Debug)] pub enum Stdio { Inherit, Null, MakePipe, + ParentStdout, + ParentStderr, + #[allow(dead_code)] // This variant exists only for the Debug impl + InheritFile(File), } impl Command { - pub fn new(_program: &OsStr) -> Command { - Command { env: Default::default() } + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_owned(), + args: vec![program.to_owned()], + env: Default::default(), + cwd: None, + stdin: None, + stdout: None, + stderr: None, + } } - pub fn arg(&mut self, _arg: &OsStr) {} + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_owned()); + } pub fn env_mut(&mut self) -> &mut CommandEnv { &mut self.env } - pub fn cwd(&mut self, _dir: &OsStr) {} + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_owned()); + } - pub fn stdin(&mut self, _stdin: Stdio) {} + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } - pub fn stdout(&mut self, _stdout: Stdio) {} + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } - pub fn stderr(&mut self, _stderr: Stdio) {} + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } pub fn get_program(&self) -> &OsStr { - panic!("unsupported") + &self.program } pub fn get_args(&self) -> CommandArgs<'_> { - CommandArgs { _p: PhantomData } + let mut iter = self.args.iter(); + iter.next(); + CommandArgs { iter } } pub fn get_envs(&self) -> CommandEnvs<'_> { @@ -67,7 +96,7 @@ impl Command { } pub fn get_current_dir(&self) -> Option<&Path> { - None + self.cwd.as_ref().map(|cs| Path::new(cs)) } pub fn spawn( @@ -91,31 +120,83 @@ impl From for Stdio { impl From for Stdio { fn from(_: io::Stdout) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + Stdio::ParentStdout } } impl From for Stdio { fn from(_: io::Stderr) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + Stdio::ParentStderr } } impl From for Stdio { - fn from(_file: File) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + fn from(file: File) -> Stdio { + Stdio::InheritFile(file) } } impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) + // show all attributes + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let mut debug_command = f.debug_struct("Command"); + debug_command.field("program", &self.program).field("args", &self.args); + if !self.env.is_unchanged() { + debug_command.field("env", &self.env); + } + + if self.cwd.is_some() { + debug_command.field("cwd", &self.cwd); + } + + if self.stdin.is_some() { + debug_command.field("stdin", &self.stdin); + } + if self.stdout.is_some() { + debug_command.field("stdout", &self.stdout); + } + if self.stderr.is_some() { + debug_command.field("stderr", &self.stderr); + } + + debug_command.finish() + } else { + if let Some(ref cwd) = self.cwd { + write!(f, "cd {cwd:?} && ")?; + } + if self.env.does_clear() { + write!(f, "env -i ")?; + // Altered env vars will be printed next, that should exactly work as expected. + } else { + // Removed env vars need the command to be wrapped in `env`. + let mut any_removed = false; + for (key, value_opt) in self.get_envs() { + if value_opt.is_none() { + if !any_removed { + write!(f, "env ")?; + any_removed = true; + } + write!(f, "-u {} ", key.to_string_lossy())?; + } + } + } + // Altered env vars can just be added in front of the program. + for (key, value_opt) in self.get_envs() { + if let Some(value) = value_opt { + write!(f, "{}={value:?} ", key.to_string_lossy())?; + } + } + if self.program != self.args[0] { + write!(f, "[{:?}] ", self.program)?; + } + write!(f, "{:?}", self.args[0])?; + + for arg in &self.args[1..] { + write!(f, " {:?}", arg)?; + } + Ok(()) + } } } @@ -217,23 +298,30 @@ impl Process { } pub struct CommandArgs<'a> { - _p: PhantomData<&'a ()>, + iter: crate::slice::Iter<'a, OsString>, } impl<'a> Iterator for CommandArgs<'a> { type Item = &'a OsStr; fn next(&mut self) -> Option<&'a OsStr> { - None + self.iter.next().map(|os| &**os) } fn size_hint(&self) -> (usize, Option) { - (0, Some(0)) + self.iter.size_hint() } } -impl<'a> ExactSizeIterator for CommandArgs<'a> {} +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} impl<'a> fmt::Debug for CommandArgs<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().finish() + f.debug_list().entries(self.iter.clone()).finish() } } diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index b3a91ee1d4cb6..ea939247199c2 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -1,5 +1,5 @@ use super::unsupported; -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::time::Duration; @@ -22,10 +22,6 @@ impl Thread { // nope } - pub fn get_name() -> Option { - None - } - pub fn sleep(_dur: Duration) { panic!("can't sleep"); } @@ -38,13 +34,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/pal/wasi/net.rs index 2098d05db0bc7..b4cf94c8781ec 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/pal/wasi/net.rs @@ -520,6 +520,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -532,6 +533,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 4b116052f8f8e..d45fb28b67e63 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -1,4 +1,4 @@ -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::mem; use crate::num::NonZero; @@ -134,10 +134,6 @@ impl Thread { // nope } - pub fn get_name() -> Option { - None - } - pub fn sleep(dur: Duration) { let nanos = dur.as_nanos(); assert!(nanos <= u64::MAX as u128); @@ -193,13 +189,3 @@ impl Thread { pub fn available_parallelism() -> io::Result> { unsupported() } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/wasip2/cabi_realloc.rs b/library/std/src/sys/pal/wasip2/cabi_realloc.rs new file mode 100644 index 0000000000000..820063173d657 --- /dev/null +++ b/library/std/src/sys/pal/wasip2/cabi_realloc.rs @@ -0,0 +1,65 @@ +//! This module contains a canonical definition of the `cabi_realloc` function +//! for the component model. +//! +//! The component model's canonical ABI for representing datatypes in memory +//! makes use of this function when transferring lists and strings, for example. +//! This function behaves like C's `realloc` but also takes alignment into +//! account. +//! +//! Components are notably not required to export this function, but nearly +//! all components end up doing so currently. This definition in the standard +//! library removes the need for all compilations to define this themselves. +//! +//! More information about the canonical ABI can be found at +//! +//! +//! Note that the name of this function is not standardized in the canonical ABI +//! at this time. Instead it's a convention of the "componentization process" +//! where a core wasm module is converted to a component to use this name. +//! Additionally this is not the only possible definition of this function, so +//! this is defined as a "weak" symbol. This means that other definitions are +//! allowed to overwrite it if they are present in a compilation. + +use crate::alloc::{self, Layout}; +use crate::ptr; + +#[used] +static FORCE_CODEGEN_OF_CABI_REALLOC: unsafe extern "C" fn( + *mut u8, + usize, + usize, + usize, +) -> *mut u8 = cabi_realloc; + +#[linkage = "weak"] +#[no_mangle] +pub unsafe extern "C" fn cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + let layout; + let ptr = if old_len == 0 { + if new_len == 0 { + return ptr::without_provenance_mut(align); + } + layout = Layout::from_size_align_unchecked(new_len, align); + alloc::alloc(layout) + } else { + debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!"); + layout = Layout::from_size_align_unchecked(old_len, align); + alloc::realloc(old_ptr, layout, new_len) + }; + if ptr.is_null() { + // Print a nice message in debug mode, but in release mode don't + // pull in so many dependencies related to printing so just emit an + // `unreachable` instruction. + if cfg!(debug_assertions) { + alloc::handle_alloc_error(layout); + } else { + super::abort_internal(); + } + } + return ptr; +} diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index d1d444d7b798b..94aa458d2f90e 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -10,8 +10,6 @@ pub mod alloc; #[path = "../wasi/args.rs"] pub mod args; -#[path = "../unix/cmath.rs"] -pub mod cmath; #[path = "../wasi/env.rs"] pub mod env; #[path = "../wasi/fd.rs"] @@ -28,10 +26,6 @@ pub mod io; pub mod net; #[path = "../wasi/os.rs"] pub mod os; -#[path = "../unix/os_str.rs"] -pub mod os_str; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] @@ -72,3 +66,5 @@ pub use helpers::decode_error_kind; use helpers::err2io; pub use helpers::hashmap_random_keys; pub use helpers::is_interrupted; + +mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasm/alloc.rs b/library/std/src/sys/pal/wasm/alloc.rs index 6dceb1689a8b7..b74ce0d47425a 100644 --- a/library/std/src/sys/pal/wasm/alloc.rs +++ b/library/std/src/sys/pal/wasm/alloc.rs @@ -57,7 +57,10 @@ unsafe impl GlobalAlloc for System { #[cfg(target_feature = "atomics")] mod lock { - use crate::sync::atomic::{AtomicI32, Ordering::SeqCst}; + use crate::sync::atomic::{ + AtomicI32, + Ordering::{Acquire, Release}, + }; static LOCKED: AtomicI32 = AtomicI32::new(0); @@ -65,7 +68,7 @@ mod lock { pub fn lock() -> DropLock { loop { - if LOCKED.swap(1, SeqCst) == 0 { + if LOCKED.swap(1, Acquire) == 0 { return DropLock; } // Ok so here's where things get a little depressing. At this point @@ -143,7 +146,7 @@ mod lock { impl Drop for DropLock { fn drop(&mut self) { - let r = LOCKED.swap(0, SeqCst); + let r = LOCKED.swap(0, Release); debug_assert_eq!(r, 1); // Note that due to the above logic we don't actually need to wake diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index 3923ff821d9d3..49f936f14498c 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -1,5 +1,4 @@ use crate::ffi::CStr; -use crate::ffi::CString; use crate::io; use crate::num::NonZero; use crate::sys::unsupported; @@ -18,9 +17,6 @@ impl Thread { pub fn yield_now() {} pub fn set_name(_name: &CStr) {} - pub fn get_name() -> Option { - None - } pub fn sleep(dur: Duration) { use crate::arch::wasm32; diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 90e1bff52a365..555ad581b8568 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -34,6 +34,102 @@ use core::ptr::addr_of; use super::c; +/// Creates a null-terminated UTF-16 string from a str. +pub macro wide_str($str:literal) {{ + const _: () = { + if core::slice::memchr::memchr(0, $str.as_bytes()).is_some() { + panic!("null terminated strings cannot contain interior nulls"); + } + }; + crate::sys::pal::windows::api::utf16!(concat!($str, '\0')) +}} + +/// Creates a UTF-16 string from a str without null termination. +pub macro utf16($str:expr) {{ + const UTF8: &str = $str; + const UTF16_LEN: usize = crate::sys::pal::windows::api::utf16_len(UTF8); + const UTF16: [u16; UTF16_LEN] = crate::sys::pal::windows::api::to_utf16(UTF8); + &UTF16 +}} + +#[cfg(test)] +mod tests; + +/// Gets the UTF-16 length of a UTF-8 string, for use in the wide_str macro. +pub const fn utf16_len(s: &str) -> usize { + let s = s.as_bytes(); + let mut i = 0; + let mut len = 0; + while i < s.len() { + // the length of a UTF-8 encoded code-point is given by the number of + // leading ones, except in the case of ASCII. + let utf8_len = match s[i].leading_ones() { + 0 => 1, + n => n as usize, + }; + i += utf8_len; + // Note that UTF-16 surrogates (U+D800 to U+DFFF) are not encodable as UTF-8, + // so (unlike with WTF-8) we don't have to worry about how they'll get re-encoded. + len += if utf8_len < 4 { 1 } else { 2 }; + } + len +} + +/// Const convert UTF-8 to UTF-16, for use in the wide_str macro. +/// +/// Note that this is designed for use in const contexts so is not optimized. +pub const fn to_utf16(s: &str) -> [u16; UTF16_LEN] { + let mut output = [0_u16; UTF16_LEN]; + let mut pos = 0; + let s = s.as_bytes(); + let mut i = 0; + while i < s.len() { + match s[i].leading_ones() { + // Decode UTF-8 based on its length. + // See https://en.wikipedia.org/wiki/UTF-8 + 0 => { + // ASCII is the same in both encodings + output[pos] = s[i] as u16; + i += 1; + pos += 1; + } + 2 => { + // Bits: 110xxxxx 10xxxxxx + output[pos] = ((s[i] as u16 & 0b11111) << 6) | (s[i + 1] as u16 & 0b111111); + i += 2; + pos += 1; + } + 3 => { + // Bits: 1110xxxx 10xxxxxx 10xxxxxx + output[pos] = ((s[i] as u16 & 0b1111) << 12) + | ((s[i + 1] as u16 & 0b111111) << 6) + | (s[i + 2] as u16 & 0b111111); + i += 3; + pos += 1; + } + 4 => { + // Bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + let mut c = ((s[i] as u32 & 0b111) << 18) + | ((s[i + 1] as u32 & 0b111111) << 12) + | ((s[i + 2] as u32 & 0b111111) << 6) + | (s[i + 3] as u32 & 0b111111); + // re-encode as UTF-16 (see https://en.wikipedia.org/wiki/UTF-16) + // - Subtract 0x10000 from the code point + // - For the high surrogate, shift right by 10 then add 0xD800 + // - For the low surrogate, take the low 10 bits then add 0xDC00 + c -= 0x10000; + output[pos] = ((c >> 10) + 0xD800) as u16; + output[pos + 1] = ((c & 0b1111111111) + 0xDC00) as u16; + i += 4; + pos += 2; + } + // valid UTF-8 cannot have any other values + _ => unreachable!(), + } + } + output +} + /// Helper method for getting the size of `T` as a u32. /// Errors at compile time if the size would overflow. /// diff --git a/library/std/src/sys/pal/windows/api/tests.rs b/library/std/src/sys/pal/windows/api/tests.rs new file mode 100644 index 0000000000000..fab022c7b93d8 --- /dev/null +++ b/library/std/src/sys/pal/windows/api/tests.rs @@ -0,0 +1,16 @@ +use crate::sys::pal::windows::api::{utf16, wide_str}; + +macro_rules! check_utf16 { + ($str:literal) => {{ + assert!(wide_str!($str).iter().copied().eq($str.encode_utf16().chain([0]))); + assert!(utf16!($str).iter().copied().eq($str.encode_utf16())); + }}; +} + +#[test] +fn test_utf16_macros() { + check_utf16!("hello world"); + check_utf16!("€4.50"); + check_utf16!("𨉟呐㗂越"); + check_utf16!("Pchnąć w tę łódź jeża lub ośm skrzyń fig"); +} diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs index 2ecfe088d107b..5098c05196e25 100644 --- a/library/std/src/sys/pal/windows/args.rs +++ b/library/std/src/sys/pal/windows/args.rs @@ -7,7 +7,7 @@ mod tests; use super::os::current_exe; -use crate::ffi::OsString; +use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; use crate::num::NonZero; @@ -17,6 +17,7 @@ use crate::sys::path::get_long_path; use crate::sys::process::ensure_no_nuls; use crate::sys::{c, to_u16s}; use crate::sys_common::wstr::WStrUnits; +use crate::sys_common::AsInner; use crate::vec; use crate::iter; @@ -262,16 +263,92 @@ pub(crate) fn append_arg(cmd: &mut Vec, arg: &Arg, force_quotes: bool) -> i Ok(()) } +fn append_bat_arg(cmd: &mut Vec, arg: &OsStr, mut quote: bool) -> io::Result<()> { + ensure_no_nuls(arg)?; + // If an argument has 0 characters then we need to quote it to ensure + // that it actually gets passed through on the command line or otherwise + // it will be dropped entirely when parsed on the other end. + // + // We also need to quote the argument if it ends with `\` to guard against + // bat usage such as `"%~2"` (i.e. force quote arguments) otherwise a + // trailing slash will escape the closing quote. + if arg.is_empty() || arg.as_encoded_bytes().last() == Some(&b'\\') { + quote = true; + } + for cp in arg.as_inner().inner.code_points() { + if let Some(cp) = cp.to_char() { + // Rather than trying to find every ascii symbol that must be quoted, + // we assume that all ascii symbols must be quoted unless they're known to be good. + // We also quote Unicode control blocks for good measure. + // Note an unquoted `\` is fine so long as the argument isn't otherwise quoted. + static UNQUOTED: &str = r"#$*+-./:?@\_"; + let ascii_needs_quotes = + cp.is_ascii() && !(cp.is_ascii_alphanumeric() || UNQUOTED.contains(cp)); + if ascii_needs_quotes || cp.is_control() { + quote = true; + } + } + } + + if quote { + cmd.push('"' as u16); + } + // Loop through the string, escaping `\` only if followed by `"`. + // And escaping `"` by doubling them. + let mut backslashes: usize = 0; + for x in arg.encode_wide() { + if x == '\\' as u16 { + backslashes += 1; + } else { + if x == '"' as u16 { + // Add n backslashes to total 2n before internal `"`. + cmd.extend((0..backslashes).map(|_| '\\' as u16)); + // Appending an additional double-quote acts as an escape. + cmd.push(b'"' as u16) + } else if x == '%' as u16 || x == '\r' as u16 { + // yt-dlp hack: replaces `%` with `%%cd:~,%` to stop %VAR% being expanded as an environment variable. + // + // # Explanation + // + // cmd supports extracting a substring from a variable using the following syntax: + // %variable:~start_index,end_index% + // + // In the above command `cd` is used as the variable and the start_index and end_index are left blank. + // `cd` is a built-in variable that dynamically expands to the current directory so it's always available. + // Explicitly omitting both the start and end index creates a zero-length substring. + // + // Therefore it all resolves to nothing. However, by doing this no-op we distract cmd.exe + // from potentially expanding %variables% in the argument. + cmd.extend_from_slice(&[ + '%' as u16, '%' as u16, 'c' as u16, 'd' as u16, ':' as u16, '~' as u16, + ',' as u16, + ]); + } + backslashes = 0; + } + cmd.push(x); + } + if quote { + // Add n backslashes to total 2n before ending `"`. + cmd.extend((0..backslashes).map(|_| '\\' as u16)); + cmd.push('"' as u16); + } + Ok(()) +} + pub(crate) fn make_bat_command_line( script: &[u16], args: &[Arg], force_quotes: bool, ) -> io::Result> { + const INVALID_ARGUMENT_ERROR: io::Error = + io::const_io_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); // Set the start of the command line to `cmd.exe /c "` // It is necessary to surround the command in an extra pair of quotes, // hence the trailing quote here. It will be closed after all arguments // have been added. - let mut cmd: Vec = "cmd.exe /d /c \"".encode_utf16().collect(); + // Using /e:ON enables "command extensions" which is essential for the `%` hack to work. + let mut cmd: Vec = "cmd.exe /e:ON /v:OFF /d /c \"".encode_utf16().collect(); // Push the script name surrounded by its quote pair. cmd.push(b'"' as u16); @@ -291,18 +368,22 @@ pub(crate) fn make_bat_command_line( // reconstructed by the batch script by default. for arg in args { cmd.push(' ' as u16); - // Make sure to always quote special command prompt characters, including: - // * Characters `cmd /?` says require quotes. - // * `%` for environment variables, as in `%TMP%`. - // * `|<>` pipe/redirect characters. - const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; - let force_quotes = match arg { - Arg::Regular(arg) if !force_quotes => { - arg.as_encoded_bytes().iter().any(|c| SPECIAL.contains(c)) + match arg { + Arg::Regular(arg_os) => { + let arg_bytes = arg_os.as_encoded_bytes(); + // Disallow \r and \n as they may truncate the arguments. + const DISALLOWED: &[u8] = b"\r\n"; + if arg_bytes.iter().any(|c| DISALLOWED.contains(c)) { + return Err(INVALID_ARGUMENT_ERROR); + } + append_bat_arg(&mut cmd, arg_os, force_quotes)?; + } + _ => { + // Raw arguments are passed on as-is. + // It's the user's responsibility to properly handle arguments in this case. + append_arg(&mut cmd, arg, force_quotes)?; } - _ => force_quotes, }; - append_arg(&mut cmd, arg, force_quotes)?; } // Close the quote we left opened earlier. diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 1c828bac4b662..9d58ce05f018b 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -357,7 +357,19 @@ compat_fn_with_fallback! { } #[cfg(not(target_vendor = "win7"))] -#[link(name = "synchronization")] +// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library. +#[cfg_attr( + target_arch = "x86", + link( + name = "api-ms-win-core-synch-l1-2-0", + kind = "raw-dylib", + import_name_type = "undecorated" + ) +)] +#[cfg_attr( + not(target_arch = "x86"), + link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib") +)] extern "system" { pub fn WaitOnAddress( address: *const c_void, diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index d180122d735f3..1da8871ae44eb 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -1,4 +1,4 @@ -// Bindings generated by `windows-bindgen` 0.55.0 +// Bindings generated by `windows-bindgen` 0.56.0 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] #[link(name = "advapi32")] @@ -345,7 +345,7 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { - pub fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime: *mut FILETIME) -> (); + pub fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime: *mut FILETIME); } #[link(name = "kernel32")] extern "system" { @@ -1018,7 +1018,7 @@ impl Clone for CONTEXT_0_0 { } } #[repr(C)] -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] pub struct CONTEXT { pub P1Home: u64, pub P2Home: u64, @@ -1067,30 +1067,30 @@ pub struct CONTEXT { pub LastExceptionToRip: u64, pub LastExceptionFromRip: u64, } -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] impl Copy for CONTEXT {} -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] impl Clone for CONTEXT { fn clone(&self) -> Self { *self } } #[repr(C)] -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] pub union CONTEXT_0 { pub FltSave: XSAVE_FORMAT, pub Anonymous: CONTEXT_0_0, } -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] impl Copy for CONTEXT_0 {} -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] impl Clone for CONTEXT_0 { fn clone(&self) -> Self { *self } } #[repr(C)] -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] pub struct CONTEXT_0_0 { pub Header: [M128A; 2], pub Legacy: [M128A; 8], @@ -1111,9 +1111,9 @@ pub struct CONTEXT_0_0 { pub Xmm14: M128A, pub Xmm15: M128A, } -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] impl Copy for CONTEXT_0_0 {} -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] impl Clone for CONTEXT_0_0 { fn clone(&self) -> Self { *self @@ -3339,7 +3339,7 @@ pub const FILE_WRITE_EA: FILE_ACCESS_RIGHTS = 16u32; pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32; pub const FIONBIO: i32 = -2147195266i32; #[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] pub struct FLOATING_SAVE_AREA { pub ControlWord: u32, pub StatusWord: u32, @@ -3351,9 +3351,9 @@ pub struct FLOATING_SAVE_AREA { pub RegisterArea: [u8; 80], pub Cr0NpxState: u32, } -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] impl Copy for FLOATING_SAVE_AREA {} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] impl Clone for FLOATING_SAVE_AREA { fn clone(&self) -> Self { *self @@ -4106,7 +4106,7 @@ impl Clone for WSABUF { } } #[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] pub struct WSADATA { pub wVersion: u16, pub wHighVersion: u16, @@ -4116,9 +4116,9 @@ pub struct WSADATA { pub szDescription: [i8; 257], pub szSystemStatus: [i8; 129], } -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] impl Copy for WSADATA {} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] impl Clone for WSADATA { fn clone(&self) -> Self { *self @@ -4286,7 +4286,7 @@ pub const WSA_SECURE_HOST_NOT_FOUND: WSA_ERROR = 11032i32; pub const WSA_WAIT_EVENT_0: WSA_ERROR = 0i32; pub const WSA_WAIT_IO_COMPLETION: WSA_ERROR = 192i32; #[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] pub struct XSAVE_FORMAT { pub ControlWord: u16, pub StatusWord: u16, @@ -4305,9 +4305,9 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 16], pub Reserved4: [u8; 96], } -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] impl Copy for XSAVE_FORMAT {} -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] impl Clone for XSAVE_FORMAT { fn clone(&self) -> Self { *self diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 6a561518fad65..ff41f6e77be7a 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -5,6 +5,7 @@ use crate::io::ErrorKind; use crate::mem::MaybeUninit; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::path::PathBuf; +use crate::sys::pal::windows::api::wide_str; use crate::time::Duration; pub use self::rand::hashmap_random_keys; @@ -12,6 +13,8 @@ pub use self::rand::hashmap_random_keys; #[macro_use] pub mod compat; +mod api; + pub mod alloc; pub mod args; pub mod c; @@ -41,8 +44,6 @@ cfg_if::cfg_if! { } } -mod api; - /// Map a Result to io::Result. trait IoResult { fn io_result(self) -> crate::io::Result; @@ -60,7 +61,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name(&c"main"); + thread::Thread::set_name_wide(wide_str!("main")); } // SAFETY: must be called only once during runtime cleanup. @@ -200,14 +201,21 @@ pub fn to_u16s>(s: S) -> crate::io::Result> { // currently reside in the buffer. This function is an abstraction over these // functions by making them easier to call. // -// The first callback, `f1`, is yielded a (pointer, len) pair which can be +// The first callback, `f1`, is passed a (pointer, len) pair which can be // passed to a syscall. The `ptr` is valid for `len` items (u16 in this case). -// The closure is expected to return what the syscall returns which will be -// interpreted by this function to determine if the syscall needs to be invoked -// again (with more buffer space). +// The closure is expected to: +// - On success, return the actual length of the written data *without* the null terminator. +// This can be 0. In this case the last_error must be left unchanged. +// - On insufficient buffer space, +// - either return the required length *with* the null terminator, +// - or set the last-error to ERROR_INSUFFICIENT_BUFFER and return `len`. +// - On other failure, return 0 and set last_error. +// +// This is how most but not all syscalls indicate the required buffer space. +// Other syscalls may need translation to match this protocol. // // Once the syscall has completed (errors bail out early) the second closure is -// yielded the data which has been read from the syscall. The return value +// passed the data which has been read from the syscall. The return value // from this closure is then the return value of the function. pub fn fill_utf16_buf(mut f1: F1, f2: F2) -> crate::io::Result where @@ -331,7 +339,7 @@ pub fn abort_internal() -> ! { core::arch::asm!("int $$0x29", in("ecx") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { core::arch::asm!(".inst 0xDEFB", in("r0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } else if #[cfg(target_arch = "aarch64")] { + } else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] { core::arch::asm!("brk 0xF003", in("x0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); } else { core::intrinsics::abort(); diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index 1e6169ea8ece0..9e15b15a3513a 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/os.rs b/library/std/src/sys/pal/windows/os.rs index 374c9845ea4bb..64d8b72aed282 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -326,6 +326,8 @@ fn home_dir_crt() -> Option { super::fill_utf16_buf( |buf, mut sz| { + // GetUserProfileDirectoryW does not quite use the usual protocol for + // negotiating the buffer size, so we have to translate. match c::GetUserProfileDirectoryW( ptr::without_provenance_mut(CURRENT_PROCESS_TOKEN), buf, diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 013f588676ae8..dfa938d4d5769 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -7,7 +7,7 @@ use crate::path::Path; use crate::ptr; use crate::slice; use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::atomic::Ordering::Relaxed; use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; @@ -214,11 +214,11 @@ pub fn spawn_pipe_relay( fn random_number() -> usize { static N: AtomicUsize = AtomicUsize::new(0); loop { - if N.load(SeqCst) != 0 { - return N.fetch_add(1, SeqCst); + if N.load(Relaxed) != 0 { + return N.fetch_add(1, Relaxed); } - N.store(hashmap_random_keys().0 as usize, SeqCst); + N.store(hashmap_random_keys().0 as usize, Relaxed); } } diff --git a/library/std/src/sys/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs index 627763da8561b..f93f31026f818 100644 --- a/library/std/src/sys/pal/windows/stack_overflow.rs +++ b/library/std/src/sys/pal/windows/stack_overflow.rs @@ -3,21 +3,12 @@ use crate::sys::c; use crate::thread; -use super::api; - -pub struct Handler; - -impl Handler { - pub unsafe fn new() -> Handler { - // This API isn't available on XP, so don't panic in that case and just - // pray it works out ok. - if c::SetThreadStackGuarantee(&mut 0x5000) == 0 - && api::get_last_error().code != c::ERROR_CALL_NOT_IMPLEMENTED - { - panic!("failed to reserve stack space for exception handling"); - } - Handler - } +/// Reserve stack space for use in stack overflow exceptions. +pub unsafe fn reserve_stack() { + let result = c::SetThreadStackGuarantee(&mut 0x5000); + // Reserving stack space is not critical so we allow it to fail in the released build of libstd. + // We still use debug assert here so that CI will test that we haven't made a mistake calling the function. + debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling"); } unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> c::LONG { @@ -36,9 +27,10 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN } pub unsafe fn init() { - if c::AddVectoredExceptionHandler(0, Some(vectored_handler)).is_null() { - panic!("failed to install exception handler"); - } + let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler)); + // Similar to the above, adding the stack overflow handler is allowed to fail + // but a debug assert is used so CI will still test that it normally works. + debug_assert!(!result.is_null(), "failed to install exception handler"); // Set the thread stack guarantee for the main thread. - let _h = Handler::new(); + reserve_stack(); } diff --git a/library/std/src/sys/pal/windows/stack_overflow_uwp.rs b/library/std/src/sys/pal/windows/stack_overflow_uwp.rs index afdf7f566ae51..9e9b3efaf1b14 100644 --- a/library/std/src/sys/pal/windows/stack_overflow_uwp.rs +++ b/library/std/src/sys/pal/windows/stack_overflow_uwp.rs @@ -1,11 +1,4 @@ #![cfg_attr(test, allow(dead_code))] -pub struct Handler; - -impl Handler { - pub fn new() -> Handler { - Handler - } -} - +pub unsafe fn reserve_stack() {} pub unsafe fn init() {} diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 970bd9c6ce7e6..70099e0a3b560 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -9,7 +9,6 @@ use crate::sys::handle::Handle; use crate::sys::stack_overflow; use crate::sys_common::FromInner; use crate::time::Duration; -use alloc::ffi::CString; use core::ffi::c_void; use super::time::WaitableTimer; @@ -46,14 +45,11 @@ impl Thread { Err(io::Error::last_os_error()) }; - extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { - unsafe { - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - let _handler = stack_overflow::Handler::new(); - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } + 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)(); 0 } } @@ -62,33 +58,18 @@ impl Thread { if let Ok(utf8) = name.to_str() { if let Ok(utf16) = to_u16s(utf8) { unsafe { - c::SetThreadDescription(c::GetCurrentThread(), utf16.as_ptr()); - }; + // SAFETY: the vec returned by `to_u16s` ends with a zero value + Self::set_name_wide(&utf16) + } }; }; } - pub fn get_name() -> Option { - unsafe { - let mut ptr = core::ptr::null_mut(); - let result = c::GetThreadDescription(c::GetCurrentThread(), &mut ptr); - if result < 0 { - return None; - } - let name = String::from_utf16_lossy({ - let mut len = 0; - while *ptr.add(len) != 0 { - len += 1; - } - core::slice::from_raw_parts(ptr, len) - }) - .into_bytes(); - // Attempt to free the memory. - // This should never fail but if it does then there's not much we can do about it. - let result = c::LocalFree(ptr.cast::()); - debug_assert!(result.is_null()); - if name.is_empty() { None } else { Some(CString::from_vec_unchecked(name)) } - } + /// # 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) { @@ -137,21 +118,7 @@ pub fn available_parallelism() -> io::Result> { 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) }), } } - -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/windows/thread_local_key.rs b/library/std/src/sys/pal/windows/thread_local_key.rs index 1d9cb316a453b..e5ba619fc6ba4 100644 --- a/library/std/src/sys/pal/windows/thread_local_key.rs +++ b/library/std/src/sys/pal/windows/thread_local_key.rs @@ -141,9 +141,15 @@ impl StaticKey { panic!("out of TLS indexes"); } - self.key.store(key + 1, Release); register_dtor(self); + // Release-storing the key needs to be the last thing we do. + // This is because in `fn key()`, other threads will do an acquire load of the key, + // and if that sees this write then it will entirely bypass the `InitOnce`. We thus + // need to establish synchronization through `key`. In particular that acquire load + // must happen-after the register_dtor above, to ensure the dtor actually runs! + self.key.store(key + 1, Release); + let r = c::InitOnceComplete(self.once.get(), 0, ptr::null_mut()); debug_assert_eq!(r, c::TRUE); @@ -276,6 +282,7 @@ unsafe fn register_dtor(key: &'static StaticKey) { // the address of the symbol to ensure it sticks around. #[link_section = ".CRT$XLB"] +#[cfg_attr(miri, used)] // Miri only considers explicitly `#[used]` statics for `lookup_link_section` pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = on_tls_callback; @@ -312,8 +319,22 @@ unsafe fn run_dtors() { // Use acquire ordering to observe key initialization. let mut cur = DTORS.load(Acquire); while !cur.is_null() { - let key = (*cur).key.load(Relaxed) - 1; + let pre_key = (*cur).key.load(Acquire); let dtor = (*cur).dtor.unwrap(); + cur = (*cur).next.load(Relaxed); + + // In StaticKey::init, we register the dtor before setting `key`. + // So if one thread's `run_dtors` races with another thread executing `init` on the same + // `StaticKey`, we can encounter a key of 0 here. That means this key was never + // initialized in this thread so we can safely skip it. + if pre_key == 0 { + continue; + } + // If this is non-zero, then via the `Acquire` load above we synchronized with + // everything relevant for this key. (It's not clear that this is needed, since the + // release-acquire pair on DTORS also establishes synchronization, but better safe than + // sorry.) + let key = pre_key - 1; let ptr = c::TlsGetValue(key); if !ptr.is_null() { @@ -321,8 +342,6 @@ unsafe fn run_dtors() { dtor(ptr as *mut _); any_run = true; } - - cur = (*cur).next.load(Relaxed); } if !any_run { diff --git a/library/std/src/sys/pal/xous/alloc.rs b/library/std/src/sys/pal/xous/alloc.rs index 0d540e9552072..601411173aacb 100644 --- a/library/std/src/sys/pal/xous/alloc.rs +++ b/library/std/src/sys/pal/xous/alloc.rs @@ -46,7 +46,10 @@ unsafe impl GlobalAlloc for System { } mod lock { - use crate::sync::atomic::{AtomicI32, Ordering::SeqCst}; + use crate::sync::atomic::{ + AtomicI32, + Ordering::{Acquire, Release}, + }; static LOCKED: AtomicI32 = AtomicI32::new(0); @@ -54,7 +57,7 @@ mod lock { pub fn lock() -> DropLock { loop { - if LOCKED.swap(1, SeqCst) == 0 { + if LOCKED.swap(1, Acquire) == 0 { return DropLock; } crate::os::xous::ffi::do_yield(); @@ -63,7 +66,7 @@ mod lock { impl Drop for DropLock { fn drop(&mut self) { - let r = LOCKED.swap(0, SeqCst); + let r = LOCKED.swap(0, Release); debug_assert_eq!(r, 1); } } diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/pal/xous/net/tcpstream.rs index 7149678118ab6..0ad8811071111 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) -> 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) -> 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( @@ -406,7 +400,7 @@ impl TcpStream { } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.nonblocking.store(nonblocking, Ordering::SeqCst); + self.nonblocking.store(nonblocking, Ordering::Relaxed); Ok(()) } } diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/pal/xous/net/udp.rs index cafa5b3bde829..3d0522b25f3fb 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) -> 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) -> 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/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index f95ceb7343bd1..da7d722cc7082 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -1,4 +1,4 @@ -use crate::ffi::{CStr, CString}; +use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::os::xous::ffi::{ @@ -113,10 +113,6 @@ impl Thread { // nope } - pub fn get_name() -> Option { - None - } - pub fn sleep(dur: Duration) { // Because the sleep server works on units of `usized milliseconds`, split // the messages up into these chunks. This means we may run into issues @@ -140,13 +136,3 @@ pub fn available_parallelism() -> io::Result> { // We're unicore right now. Ok(unsafe { NonZero::new_unchecked(1) }) } - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/xous/thread_local_key.rs b/library/std/src/sys/pal/xous/thread_local_key.rs index 59a668c3df6ff..6c29813c79dfd 100644 --- a/library/std/src/sys/pal/xous/thread_local_key.rs +++ b/library/std/src/sys/pal/xous/thread_local_key.rs @@ -2,7 +2,7 @@ use crate::mem::ManuallyDrop; use crate::ptr; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use core::arch::asm; use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags}; @@ -49,7 +49,7 @@ fn tls_ptr_addr() -> *mut *mut u8 { out(reg) tp, ); } - core::ptr::from_exposed_addr_mut::<*mut u8>(tp) + core::ptr::with_exposed_provenance_mut::<*mut u8>(tp) } /// Create an area of memory that's unique per thread. This area will @@ -92,7 +92,7 @@ fn tls_table() -> &'static mut [*mut u8] { pub unsafe fn create(dtor: Option) -> Key { // Allocate a new TLS key. These keys are shared among all threads. #[allow(unused_unsafe)] - let key = unsafe { TLS_KEY_INDEX.fetch_add(1, SeqCst) }; + let key = unsafe { TLS_KEY_INDEX.fetch_add(1, Relaxed) }; if let Some(f) = dtor { unsafe { register_dtor(key, f) }; } @@ -154,11 +154,11 @@ unsafe fn register_dtor(key: Key, dtor: Dtor) { let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() })); #[allow(unused_unsafe)] - let mut head = unsafe { DTORS.load(SeqCst) }; + let mut head = unsafe { DTORS.load(Acquire) }; loop { node.next = head; #[allow(unused_unsafe)] - match unsafe { DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) } { + match unsafe { DTORS.compare_exchange(head, &mut **node, Release, Acquire) } { Ok(_) => return, // nothing to drop, we successfully added the node to the list Err(cur) => head = cur, } @@ -199,7 +199,7 @@ unsafe fn run_dtors() { } any_run = false; #[allow(unused_unsafe)] - let mut cur = unsafe { DTORS.load(SeqCst) }; + let mut cur = unsafe { DTORS.load(Acquire) }; while !cur.is_null() { let ptr = unsafe { get((*cur).key) }; diff --git a/library/std/src/sys/pal/zkvm/args.rs b/library/std/src/sys/pal/zkvm/args.rs index 7753cf63840e3..583c16e3a4721 100644 --- a/library/std/src/sys/pal/zkvm/args.rs +++ b/library/std/src/sys/pal/zkvm/args.rs @@ -1,6 +1,7 @@ use super::{abi, WORD_SIZE}; use crate::ffi::OsString; use crate::fmt; +use crate::sys::os_str; use crate::sys_common::FromInner; pub struct Args { @@ -33,7 +34,7 @@ impl Args { // "os_str". let arg_bytes: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, arg_len) }; - OsString::from_inner(super::os_str::Buf { inner: arg_bytes.to_vec() }) + OsString::from_inner(os_str::Buf { inner: arg_bytes.to_vec() }) } } diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 228a976dbabc3..4f79f8c496169 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::(); 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() -> std_io::Result { } 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/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index d8739ee38246e..759beb2d306b9 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -5,6 +5,7 @@ use crate::fmt; use crate::io; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; +use crate::sys::os_str; use crate::sys_common::FromInner; pub fn errno() -> i32 { @@ -111,7 +112,7 @@ pub fn getenv(varname: &OsStr) -> Option { // reimplement "os_str" instead of just using the generic unix // "os_str". let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) }; - Some(OsString::from_inner(super::os_str::Buf { inner: u8s.to_vec() })) + Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) } pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { diff --git a/library/std/src/sys/pal/zkvm/thread_local_key.rs b/library/std/src/sys/pal/zkvm/thread_local_key.rs index 3ffe6247344e8..2f67924c61823 100644 --- a/library/std/src/sys/pal/zkvm/thread_local_key.rs +++ b/library/std/src/sys/pal/zkvm/thread_local_key.rs @@ -9,13 +9,13 @@ pub unsafe fn create(_dtor: Option) -> Key { #[inline] pub unsafe fn set(key: Key, value: *mut u8) { - let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); *key = value; } #[inline] pub unsafe fn get(key: Key) -> *mut u8 { - let key: *mut *mut u8 = core::ptr::from_exposed_addr_mut(key); + let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); *key } diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index a78084de0faef..3f3615ea3e0a4 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -125,7 +125,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. // FIXME(strict provenance) - let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize); + let lpad = ptr::with_exposed_provenance((cs_lpad + 1) as usize); return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); } } diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 6f317131145ae..b0f744dd96605 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -92,7 +92,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c cfg_if::cfg_if! { - if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] { + if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "visionos"), not(target_os = "netbsd")))] { // ARM EHABI personality routine. // https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf // diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index 728371685eeee..a2a96410d932c 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -30,13 +30,10 @@ impl LazyInit for AllocatedCondvar { cfg_if::cfg_if! { if #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "l4re", target_os = "android", - target_os = "redox" + target_os = "redox", + target_vendor = "apple", ))] { // `pthread_condattr_setclock` is unfortunately not supported on these platforms. } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { @@ -123,13 +120,10 @@ impl Condvar { // default system clock). This approach avoids all problems that result // from changes made to the system time. #[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "android", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_vendor = "apple", )))] pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { use crate::sys::time::Timespec; @@ -158,13 +152,10 @@ impl Condvar { // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 #[cfg(any( - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos", target_os = "android", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_vendor = "apple", ))] pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/sync/mutex/xous.rs b/library/std/src/sys/sync/mutex/xous.rs index a8c9518ff0bcf..1426e48f8b7af 100644 --- a/library/std/src/sys/sync/mutex/xous.rs +++ b/library/std/src/sys/sync/mutex/xous.rs @@ -1,6 +1,9 @@ use crate::os::xous::ffi::{blocking_scalar, do_yield}; use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed, Ordering::SeqCst}; +use crate::sync::atomic::{ + AtomicBool, AtomicUsize, + Ordering::{Acquire, Relaxed, Release}, +}; pub struct Mutex { /// The "locked" value indicates how many threads are waiting on this @@ -68,7 +71,7 @@ impl Mutex { #[inline] pub unsafe fn unlock(&self) { - let prev = self.locked.fetch_sub(1, SeqCst); + let prev = self.locked.fetch_sub(1, Release); // If the previous value was 1, then this was a "fast path" unlock, so no // need to involve the Ticktimer server @@ -89,12 +92,12 @@ impl Mutex { #[inline] pub unsafe fn try_lock(&self) -> bool { - self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() + self.locked.compare_exchange(0, 1, Acquire, Relaxed).is_ok() } #[inline] pub unsafe fn try_lock_or_poison(&self) -> bool { - self.locked.fetch_add(1, SeqCst) == 0 + self.locked.fetch_add(1, Acquire) == 0 } } diff --git a/library/std/src/sys/sync/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs index 675931c64bdd3..70ba6bf38ef55 100644 --- a/library/std/src/sys/sync/rwlock/mod.rs +++ b/library/std/src/sys/sync/rwlock/mod.rs @@ -12,24 +12,20 @@ cfg_if::cfg_if! { ))] { mod futex; pub use futex::RwLock; - } else if #[cfg(target_family = "unix")] { + } else if #[cfg(any( + target_family = "unix", + all(target_os = "windows", target_vendor = "win7"), + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "xous", + ))] { mod queue; pub use queue::RwLock; - } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] { - mod windows7; - pub use windows7::RwLock; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { - mod sgx; - pub use sgx::RwLock; } else if #[cfg(target_os = "solid_asp3")] { mod solid; pub use solid::RwLock; } else if #[cfg(target_os = "teeos")] { mod teeos; pub use teeos::RwLock; - } else if #[cfg(target_os = "xous")] { - mod xous; - pub use xous::RwLock; } else { mod no_threads; pub use no_threads::RwLock; diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index dce966086b8ff..337cc6c2ca094 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -115,8 +115,7 @@ use crate::sync::atomic::{ AtomicBool, AtomicPtr, Ordering::{AcqRel, Acquire, Relaxed, Release}, }; -use crate::sys_common::thread_info; -use crate::thread::Thread; +use crate::thread::{self, Thread}; // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the // locking operation will be retried. @@ -203,8 +202,7 @@ impl Node { fn prepare(&mut self) { // Fall back to creating an unnamed `Thread` handle to allow locking in // TLS destructors. - self.thread - .get_or_init(|| thread_info::current_thread().unwrap_or_else(|| Thread::new(None))); + self.thread.get_or_init(|| thread::try_current().unwrap_or_else(Thread::new_unnamed)); self.completed = AtomicBool::new(false); } diff --git a/library/std/src/sys/sync/rwlock/sgx.rs b/library/std/src/sys/sync/rwlock/sgx.rs deleted file mode 100644 index 136dea597bb0c..0000000000000 --- a/library/std/src/sys/sync/rwlock/sgx.rs +++ /dev/null @@ -1,219 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::alloc::Layout; -use crate::num::NonZero; -use crate::sys::pal::waitqueue::{ - try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, -}; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -struct AllocatedRwLock { - readers: SpinMutex>>>, - writer: SpinMutex>, -} - -pub struct RwLock { - inner: LazyBox, -} - -impl LazyInit for AllocatedRwLock { - fn init() -> Box { - Box::new(AllocatedRwLock { - readers: SpinMutex::new(WaitVariable::new(None)), - writer: SpinMutex::new(WaitVariable::new(false)), - }) - } -} - -// Check at compile time that RwLock's size and alignment matches the C definition -// in libunwind (see also `test_c_rwlock_initializer` in `tests`). -const _: () = { - let rust = Layout::new::(); - let c = Layout::new::<*mut ()>(); - assert!(rust.size() == c.size()); - assert!(rust.align() == c.align()); -}; - -impl RwLock { - pub const fn new() -> RwLock { - RwLock { inner: LazyBox::new() } - } - - #[inline] - pub fn read(&self) { - let lock = &*self.inner; - let mut rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - if *wguard.lock_var() || !wguard.queue_empty() { - // Another thread has or is waiting for the write lock, wait - drop(wguard); - WaitQueue::wait(rguard, || {}); - // Another thread has passed the lock to us - } else { - // No waiting writers, acquire the read lock - *rguard.lock_var_mut() = NonZero::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); - } - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - let lock = &*self.inner; - let mut rguard = try_lock_or_false!(lock.readers); - let wguard = try_lock_or_false!(lock.writer); - if *wguard.lock_var() || !wguard.queue_empty() { - // Another thread has or is waiting for the write lock - false - } else { - // No waiting writers, acquire the read lock - *rguard.lock_var_mut() = NonZero::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); - true - } - } - - #[inline] - pub fn write(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let mut wguard = lock.writer.lock(); - if *wguard.lock_var() || rguard.lock_var().is_some() { - // Another thread has the lock, wait - drop(rguard); - WaitQueue::wait(wguard, || {}); - // Another thread has passed the lock to us - } else { - // We are just now obtaining the lock - *wguard.lock_var_mut() = true; - } - } - - #[inline] - pub fn try_write(&self) -> bool { - let lock = &*self.inner; - let rguard = try_lock_or_false!(lock.readers); - let mut wguard = try_lock_or_false!(lock.writer); - if *wguard.lock_var() || rguard.lock_var().is_some() { - // Another thread has the lock - false - } else { - // We are just now obtaining the lock - *wguard.lock_var_mut() = true; - true - } - } - - #[inline] - unsafe fn __read_unlock( - &self, - mut rguard: SpinMutexGuard<'_, WaitVariable>>>, - wguard: SpinMutexGuard<'_, WaitVariable>, - ) { - *rguard.lock_var_mut() = NonZero::new(rguard.lock_var().unwrap().get() - 1); - if rguard.lock_var().is_some() { - // There are other active readers - } else { - if let Ok(mut wguard) = WaitQueue::notify_one(wguard) { - // A writer was waiting, pass the lock - *wguard.lock_var_mut() = true; - wguard.drop_after(rguard); - } else { - // No writers were waiting, the lock is released - rtassert!(rguard.queue_empty()); - } - } - } - - #[inline] - pub unsafe fn read_unlock(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - unsafe { self.__read_unlock(rguard, wguard) }; - } - - #[inline] - unsafe fn __write_unlock( - &self, - rguard: SpinMutexGuard<'_, WaitVariable>>>, - wguard: SpinMutexGuard<'_, WaitVariable>, - ) { - match WaitQueue::notify_one(wguard) { - Err(mut wguard) => { - // No writers waiting, release the write lock - *wguard.lock_var_mut() = false; - if let Ok(mut rguard) = WaitQueue::notify_all(rguard) { - // One or more readers were waiting, pass the lock to them - if let NotifiedTcs::All { count } = rguard.notified_tcs() { - *rguard.lock_var_mut() = Some(count) - } else { - unreachable!() // called notify_all - } - rguard.drop_after(wguard); - } else { - // No readers waiting, the lock is released - } - } - Ok(wguard) => { - // There was a thread waiting for write, just pass the lock - wguard.drop_after(rguard); - } - } - } - - #[inline] - pub unsafe fn write_unlock(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - unsafe { self.__write_unlock(rguard, wguard) }; - } - - // only used by __rust_rwlock_unlock below - #[inline] - #[cfg_attr(test, allow(dead_code))] - unsafe fn unlock(&self) { - let lock = &*self.inner; - let rguard = lock.readers.lock(); - let wguard = lock.writer.lock(); - if *wguard.lock_var() == true { - unsafe { self.__write_unlock(rguard, wguard) }; - } else { - unsafe { self.__read_unlock(rguard, wguard) }; - } - } -} - -// The following functions are needed by libunwind. These symbols are named -// in pre-link args for the target specification, so keep that in sync. -#[cfg(not(test))] -const EINVAL: i32 = 22; - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 { - if p.is_null() { - return EINVAL; - } - unsafe { (*p).read() }; - return 0; -} - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 { - if p.is_null() { - return EINVAL; - } - unsafe { (*p).write() }; - return 0; -} - -#[cfg(not(test))] -#[no_mangle] -pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 { - if p.is_null() { - return EINVAL; - } - unsafe { (*p).unlock() }; - return 0; -} diff --git a/library/std/src/sys/sync/rwlock/sgx/tests.rs b/library/std/src/sys/sync/rwlock/sgx/tests.rs deleted file mode 100644 index 5fd6670afd435..0000000000000 --- a/library/std/src/sys/sync/rwlock/sgx/tests.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::*; -use crate::ptr; - -// Verify that the byte pattern libunwind uses to initialize an RwLock is -// equivalent to the value of RwLock::new(). If the value changes, -// `src/UnwindRustSgx.h` in libunwind needs to be changed too. -#[test] -fn test_c_rwlock_initializer() { - const C_RWLOCK_INIT: *mut () = ptr::null_mut(); - - // For the test to work, we need the padding/unused bytes in RwLock to be - // initialized as 0. In practice, this is the case with statics. - static RUST_RWLOCK_INIT: RwLock = RwLock::new(); - - unsafe { - // If the assertion fails, that not necessarily an issue with the value - // of C_RWLOCK_INIT. It might just be an issue with the way padding - // bytes are initialized in the test code. - assert_eq!(crate::mem::transmute_copy::<_, *mut ()>(&RUST_RWLOCK_INIT), C_RWLOCK_INIT); - }; -} diff --git a/library/std/src/sys/sync/rwlock/windows7.rs b/library/std/src/sys/sync/rwlock/windows7.rs deleted file mode 100644 index e69415baac42b..0000000000000 --- a/library/std/src/sys/sync/rwlock/windows7.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::c; - -pub struct RwLock { - inner: UnsafeCell, -} - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -impl RwLock { - #[inline] - pub const fn new() -> RwLock { - RwLock { inner: UnsafeCell::new(c::SRWLOCK_INIT) } - } - #[inline] - pub fn read(&self) { - unsafe { c::AcquireSRWLockShared(self.inner.get()) } - } - #[inline] - pub fn try_read(&self) -> bool { - unsafe { c::TryAcquireSRWLockShared(self.inner.get()) != 0 } - } - #[inline] - pub fn write(&self) { - unsafe { c::AcquireSRWLockExclusive(self.inner.get()) } - } - #[inline] - pub fn try_write(&self) -> bool { - unsafe { c::TryAcquireSRWLockExclusive(self.inner.get()) != 0 } - } - #[inline] - pub unsafe fn read_unlock(&self) { - c::ReleaseSRWLockShared(self.inner.get()) - } - #[inline] - pub unsafe fn write_unlock(&self) { - c::ReleaseSRWLockExclusive(self.inner.get()) - } -} diff --git a/library/std/src/sys/sync/rwlock/xous.rs b/library/std/src/sys/sync/rwlock/xous.rs deleted file mode 100644 index ab45b33e1f69b..0000000000000 --- a/library/std/src/sys/sync/rwlock/xous.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::sync::atomic::{AtomicIsize, Ordering::Acquire}; -use crate::thread::yield_now; - -pub struct RwLock { - /// The "mode" value indicates how many threads are waiting on this - /// Mutex. Possible values are: - /// -1: The lock is locked for writing - /// 0: The lock is unlocked - /// >=1: The lock is locked for reading - /// - /// This currently spins waiting for the lock to be freed. An - /// optimization would be to involve the ticktimer server to - /// coordinate unlocks. - mode: AtomicIsize, -} - -const RWLOCK_WRITING: isize = -1; -const RWLOCK_FREE: isize = 0; - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -impl RwLock { - #[inline] - #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] - pub const fn new() -> RwLock { - RwLock { mode: AtomicIsize::new(RWLOCK_FREE) } - } - - #[inline] - pub unsafe fn read(&self) { - while !unsafe { self.try_read() } { - yield_now(); - } - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - self.mode - .fetch_update( - Acquire, - Acquire, - |v| if v == RWLOCK_WRITING { None } else { Some(v + 1) }, - ) - .is_ok() - } - - #[inline] - pub unsafe fn write(&self) { - while !unsafe { self.try_write() } { - yield_now(); - } - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - self.mode.compare_exchange(RWLOCK_FREE, RWLOCK_WRITING, Acquire, Acquire).is_ok() - } - - #[inline] - pub unsafe fn read_unlock(&self) { - let previous = self.mode.fetch_sub(1, Acquire); - assert!(previous != RWLOCK_FREE); - assert!(previous != RWLOCK_WRITING); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - assert_eq!( - self.mode.compare_exchange(RWLOCK_WRITING, RWLOCK_FREE, Acquire, Acquire), - Ok(RWLOCK_WRITING) - ); - } -} diff --git a/library/std/src/sys/thread_local/fast_local.rs b/library/std/src/sys/thread_local/fast_local.rs index 69ee70de30c02..49b51a729e428 100644 --- a/library/std/src/sys/thread_local/fast_local.rs +++ b/library/std/src/sys/thread_local/fast_local.rs @@ -1,7 +1,7 @@ use super::lazy::LazyKeyInner; use crate::cell::Cell; use crate::sys::thread_local_dtor::register_dtor; -use crate::{fmt, mem, panic}; +use crate::{fmt, mem, panic, ptr}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)] @@ -237,8 +237,9 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) { // Wrap the call in a catch to ensure unwinding is caught in the event // a panic takes place in a destructor. if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe { - let value = (*ptr).inner.take(); - (*ptr).dtor_state.set(DtorState::RunningOrHasRun); + let Key { inner, dtor_state } = &*ptr; + let value = inner.take(); + dtor_state.set(DtorState::RunningOrHasRun); drop(value); })) { rtabort!("thread local panicked on drop"); diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 8b2c839f837d4..7500c95d8b473 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -91,13 +91,15 @@ mod lazy { } } - /// The other methods hand out references while taking &self. - /// As such, callers of this method must ensure no `&` and `&mut` are - /// available and used at the same time. + /// Watch out: unsynchronized internal mutability! + /// + /// # Safety + /// Causes UB if any reference to the value is used after this. #[allow(unused)] - pub unsafe fn take(&mut self) -> Option { - // SAFETY: See doc comment for this method. - unsafe { (*self.inner.get()).take() } + pub(crate) unsafe fn take(&self) -> Option { + let mutable: *mut _ = UnsafeCell::get(&self.inner); + // SAFETY: That's the caller's problem. + unsafe { mutable.replace(None) } } } } diff --git a/library/std/src/sys/thread_local/static_local.rs b/library/std/src/sys/thread_local/static_local.rs index 4f2b686896228..162c3fbd97a0e 100644 --- a/library/std/src/sys/thread_local/static_local.rs +++ b/library/std/src/sys/thread_local/static_local.rs @@ -11,9 +11,6 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ #[inline] // see comments below #[deny(unsafe_op_in_unsafe_fn)] - // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint - #[cfg_attr(bootstrap, allow(static_mut_ref))] - #[cfg_attr(not(bootstrap), allow(static_mut_refs))] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { @@ -26,7 +23,8 @@ pub macro thread_local_inner { // FIXME(#84224) this should come after the `target_thread_local` // block. static mut VAL: $t = INIT_EXPR; - unsafe { $crate::option::Option::Some(&VAL) } + // SAFETY: we only ever create shared references, so there's no mutable aliasing. + unsafe { $crate::option::Option::Some(&*$crate::ptr::addr_of!(VAL)) } } unsafe { diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 5410f135a73f1..cc21560fff505 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -25,8 +25,6 @@ pub mod fs; pub mod io; pub mod lazy_box; pub mod process; -pub mod thread; -pub mod thread_info; 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 581c46af0eacf..95ca67fc2e0b6 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -17,10 +17,17 @@ use crate::ffi::{c_int, c_void}; cfg_if::cfg_if! { if #[cfg(any( - target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", target_os = "tvos", target_os = "macos", target_os = "watchos", - target_os = "openbsd", target_os = "netbsd", target_os = "illumos", - target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "nto"))] { + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "illumos", + target_os = "solaris", + target_os = "haiku", + target_os = "l4re", + target_os = "nto", + target_vendor = "apple", + ))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { @@ -107,13 +114,13 @@ where pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result { match storage.ss_family as c_int { c::AF_INET => { - assert!(len as usize >= mem::size_of::()); + assert!(len >= mem::size_of::()); Ok(SocketAddr::V4(FromInner::from_inner(unsafe { *(storage as *const _ as *const c::sockaddr_in) }))) } c::AF_INET6 => { - assert!(len as usize >= mem::size_of::()); + assert!(len >= mem::size_of::()); Ok(SocketAddr::V6(FromInner::from_inner(unsafe { *(storage as *const _ as *const c::sockaddr_in6) }))) @@ -417,6 +424,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 8f5624bbce9da..0000000000000 --- 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/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs deleted file mode 100644 index ec1428ea40ec6..0000000000000 --- a/library/std/src/sys_common/thread_info.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![allow(dead_code)] // stack_guard isn't used right now on all platforms - -use crate::cell::OnceCell; -use crate::sys; -use crate::sys::thread::guard::Guard; -use crate::thread::Thread; - -struct ThreadInfo { - stack_guard: OnceCell, - thread: OnceCell, -} - -thread_local! { - static THREAD_INFO: ThreadInfo = const { ThreadInfo { - stack_guard: OnceCell::new(), - thread: OnceCell::new() - } }; -} - -impl ThreadInfo { - fn with(f: F) -> Option - where - F: FnOnce(&Thread, &OnceCell) -> R, - { - THREAD_INFO - .try_with(move |thread_info| { - let thread = - thread_info.thread.get_or_init(|| Thread::new(sys::thread::Thread::get_name())); - f(thread, &thread_info.stack_guard) - }) - .ok() - } -} - -pub fn current_thread() -> Option { - ThreadInfo::with(|thread, _| thread.clone()) -} - -pub fn stack_guard() -> Option { - ThreadInfo::with(|_, guard| guard.get().cloned()).flatten() -} - -/// Set new thread info, panicking if it has already been initialized -#[allow(unreachable_code, unreachable_patterns)] // some platforms don't use stack_guard -pub fn set(stack_guard: Option, thread: Thread) { - THREAD_INFO.with(move |thread_info| { - rtassert!(thread_info.stack_guard.get().is_none() && thread_info.thread.get().is_none()); - if let Some(guard) = stack_guard { - thread_info.stack_guard.set(guard).unwrap(); - } - thread_info.thread.set(thread).unwrap(); - }); -} diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index 204834984a227..a9cd26389cd41 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -1,4 +1,4 @@ -//! OS-based thread local storage +//! OS-based thread local storage for non-Windows systems //! //! This module provides an implementation of OS-based thread local storage, //! using the native OS-provided facilities (think `TlsAlloc` or @@ -11,6 +11,9 @@ //! the OS-TLS key. The other is a type which does implement `Drop` and hence //! has a safe interface. //! +//! Windows doesn't use this module at all; `sys::pal::windows::thread_local_key` +//! gets imported in its stead. +//! //! # Usage //! //! This module should likely not be used directly unless other primitives are @@ -128,7 +131,7 @@ impl StaticKey { #[inline] unsafe fn key(&self) -> imp::Key { - match self.key.load(Ordering::Relaxed) { + match self.key.load(Ordering::Acquire) { KEY_SENTVAL => self.lazy_init() as imp::Key, n => n as imp::Key, } @@ -156,8 +159,8 @@ impl StaticKey { match self.key.compare_exchange( KEY_SENTVAL, key as usize, - Ordering::SeqCst, - Ordering::SeqCst, + Ordering::Release, + Ordering::Acquire, ) { // The CAS succeeded, so we've created the actual key Ok(_) => key as usize, diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 2dbd19d717199..38e15f9f54960 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -468,6 +468,12 @@ impl Wtf8Buf { let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) }; Wtf8Buf { bytes: bytes.into_vec(), is_known_utf8: false } } + + /// Part of a hack to make PathBuf::push/pop more efficient. + #[inline] + pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { + &mut self.bytes + } } /// Creates a new WTF-8 string from an iterator of code points. diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index fbb882e640b64..c1b4440e56088 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 = RefCell::new(1)); +/// thread_local!(static FOO: Cell = 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 fmt::Debug for LocalKey { /// Publicity and attributes for each static are allowed. Example: /// /// ``` -/// use std::cell::RefCell; +/// use std::cell::{Cell, RefCell}; +/// /// thread_local! { -/// pub static FOO: RefCell = RefCell::new(1); +/// pub static FOO: Cell = Cell::new(1); /// -/// static BAR: RefCell = RefCell::new(1.0); +/// static BAR: RefCell> = 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 fmt::Debug for LocalKey { /// track any additional state. /// /// ``` -/// use std::cell::Cell; +/// use std::cell::RefCell; +/// /// thread_local! { -/// pub static FOO: Cell = const { Cell::new(1) }; +/// pub static FOO: RefCell> = 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 LocalKey { 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 LocalKey { where F: FnOnce(Option, &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 LocalKey> { 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 LocalKey> { 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 LocalKey> { 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/local/tests.rs b/library/std/src/thread/local/tests.rs index 964c7fc5b0ca2..25019b554bb6a 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -255,6 +255,9 @@ fn join_orders_after_tls_destructors() { // observe the channel in the `THREAD1_WAITING` state. If this does occur, // we switch to the “poison” state `THREAD2_JOINED` and panic all around. // (This is equivalent to “sending” from an alternate producer thread.) + // + // Relaxed memory ordering is fine because and spawn()/join() already provide all the + // synchronization we need here. const FRESH: u8 = 0; const THREAD2_LAUNCHED: u8 = 1; const THREAD1_WAITING: u8 = 2; @@ -263,7 +266,7 @@ fn join_orders_after_tls_destructors() { static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH); for _ in 0..10 { - SYNC_STATE.store(FRESH, Ordering::SeqCst); + SYNC_STATE.store(FRESH, Ordering::Relaxed); let jh = thread::Builder::new() .name("thread1".into()) @@ -272,7 +275,7 @@ fn join_orders_after_tls_destructors() { impl Drop for TlDrop { fn drop(&mut self) { - let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::SeqCst); + let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::Relaxed); loop { match sync_state { THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(), @@ -282,7 +285,7 @@ fn join_orders_after_tls_destructors() { ), v => unreachable!("sync state: {}", v), } - sync_state = SYNC_STATE.load(Ordering::SeqCst); + sync_state = SYNC_STATE.load(Ordering::Relaxed); } } } @@ -294,7 +297,7 @@ fn join_orders_after_tls_destructors() { TL_DROP.with(|_| {}); loop { - match SYNC_STATE.load(Ordering::SeqCst) { + match SYNC_STATE.load(Ordering::Relaxed) { FRESH => thread::yield_now(), THREAD2_LAUNCHED => break, v => unreachable!("sync state: {}", v), @@ -306,9 +309,9 @@ fn join_orders_after_tls_destructors() { let jh2 = thread::Builder::new() .name("thread2".into()) .spawn(move || { - assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::SeqCst), FRESH); + assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::Relaxed), FRESH); jh.join().unwrap(); - match SYNC_STATE.swap(THREAD2_JOINED, Ordering::SeqCst) { + match SYNC_STATE.swap(THREAD2_JOINED, Ordering::Relaxed) { MAIN_THREAD_RENDEZVOUS => return, THREAD2_LAUNCHED | THREAD1_WAITING => { panic!("Thread 2 running after thread 1 join before main thread rendezvous") @@ -322,8 +325,8 @@ fn join_orders_after_tls_destructors() { match SYNC_STATE.compare_exchange( THREAD1_WAITING, MAIN_THREAD_RENDEZVOUS, - Ordering::SeqCst, - Ordering::SeqCst, + Ordering::Relaxed, + Ordering::Relaxed, ) { Ok(_) => break, Err(FRESH) => thread::yield_now(), diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 85de2980133d5..604eb05040b2d 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -159,7 +159,8 @@ mod tests; use crate::any::Any; -use crate::cell::UnsafeCell; +use crate::cell::{OnceCell, UnsafeCell}; +use crate::env; use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; @@ -171,10 +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_info; use crate::sys_common::thread_parking::Parker; use crate::sys_common::{AsInner, IntoInner}; use crate::time::{Duration, Instant}; @@ -469,11 +469,29 @@ 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); - let my_thread = Thread::new(name.map(|name| { - CString::new(name).expect("thread name may not contain interior null bytes") - })); + 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( + CString::new(name).expect("thread name may not contain interior null bytes"), + ) + }); let their_thread = my_thread.clone(); let my_packet: Arc> = Arc::new(Packet { @@ -518,12 +536,8 @@ impl Builder { crate::io::set_output_capture(output_capture); - // SAFETY: we constructed `f` initialized. let f = f.into_inner(); - // SAFETY: the stack guard passed is the one for the current thread. - // This means the current thread's stack and the new thread's stack - // are properly set and protected from each other. - thread_info::set(unsafe { imp::guard::current() }, their_thread); + set_current(their_thread); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { crate::sys_common::backtrace::__rust_begin_short_backtrace(f) })); @@ -683,6 +697,25 @@ where Builder::new().spawn(f).expect("failed to spawn thread") } +thread_local! { + static CURRENT: OnceCell = const { OnceCell::new() }; +} + +/// Sets the thread handle for the current thread. +/// +/// Panics if the handle has been set already or when called from a TLS destructor. +pub(crate) fn set_current(thread: Thread) { + CURRENT.with(|current| current.set(thread).unwrap()); +} + +/// Gets a handle to the thread that invokes it. +/// +/// In contrast to the public `current` function, this will not panic if called +/// from inside a TLS destructor. +pub(crate) fn try_current() -> Option { + CURRENT.try_with(|current| current.get_or_init(|| Thread::new_unnamed()).clone()).ok() +} + /// Gets a handle to the thread that invokes it. /// /// # Examples @@ -705,7 +738,7 @@ where #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn current() -> Thread { - thread_info::current_thread().expect( + try_current().expect( "use of std::thread::current() is not possible \ after the thread's local data has been destroyed", ) @@ -1177,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, } @@ -1231,9 +1264,16 @@ impl ThreadId { // Thread //////////////////////////////////////////////////////////////////////////////// +/// The internal representation of a `Thread`'s name. +enum ThreadName { + Main, + Other(CString), + Unnamed, +} + /// The internal representation of a `Thread` handle struct Inner { - name: Option, // Guaranteed to be UTF-8 + name: ThreadName, // Guaranteed to be UTF-8 id: ThreadId, parker: Parker, } @@ -1269,9 +1309,26 @@ pub struct Thread { } impl Thread { - // Used only internally to construct a thread object without spawning - // Panics if the name contains nuls. - pub(crate) fn new(name: Option) -> Thread { + /// Used only internally to construct a thread object without spawning. + /// + /// # Safety + /// `name` must be valid UTF-8. + pub(crate) unsafe fn new(name: CString) -> Thread { + unsafe { Self::new_inner(ThreadName::Other(name)) } + } + + pub(crate) fn new_unnamed() -> Thread { + unsafe { Self::new_inner(ThreadName::Unnamed) } + } + + // Used in runtime to construct main thread + pub(crate) fn new_main() -> Thread { + unsafe { Self::new_inner(ThreadName::Main) } + } + + /// # Safety + /// If `name` is `ThreadName::Other(_)`, the contained string must be valid UTF-8. + unsafe fn new_inner(name: ThreadName) -> Thread { // We have to use `unsafe` here to construct the `Parker` in-place, // which is required for the UNIX implementation. // @@ -1398,7 +1455,11 @@ impl Thread { } fn cname(&self) -> Option<&CStr> { - self.inner.name.as_deref() + match &self.inner.name { + ThreadName::Main => Some(c"main"), + ThreadName::Other(other) => Some(&other), + ThreadName::Unnamed => None, + } } } diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 7b11e7c17b187..e2e22e5194f4a 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/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 589a5fdad1d7e..1fb1333be0e45 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -40,10 +40,7 @@ fn test_named_thread() { #[cfg(any( // Note: musl didn't add pthread_getname_np until 1.2.3 all(target_os = "linux", target_env = "gnu"), - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos" + target_vendor = "apple", ))] #[test] fn test_named_thread_truncation() { @@ -69,26 +66,6 @@ fn test_named_thread_truncation() { result.unwrap().join().unwrap(); } -#[cfg(any( - all(target_os = "windows", not(target_vendor = "win7")), - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "tvos", - target_os = "watchos" -))] -#[test] -fn test_get_os_named_thread() { - use crate::sys::thread::Thread; - // Spawn a new thread to avoid interfering with other tests running on this thread. - let handler = thread::spawn(|| { - let name = c"test me please"; - Thread::set_name(name); - assert_eq!(name, Thread::get_name().unwrap().as_c_str()); - }); - handler.join().unwrap(); -} - #[test] #[should_panic] fn test_invalid_named_thread() { diff --git a/library/std/tests/create_dir_all_bare.rs b/library/std/tests/create_dir_all_bare.rs index fe789323f97c0..fd2a7f906f839 100644 --- a/library/std/tests/create_dir_all_bare.rs +++ b/library/std/tests/create_dir_all_bare.rs @@ -31,6 +31,7 @@ impl Drop for CurrentDir { } #[test] +#[cfg_attr(all(miri, windows), ignore)] // File system access on Windows not supported by Miri fn create_dir_all_bare() { let tmpdir = common::tmpdir(); CurrentDir::with(tmpdir.path(), || { diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 59f67f9901ffa..c56c111c37ded 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -8,6 +8,7 @@ use std::str; mod common; #[test] +#[cfg_attr(miri, ignore)] // Process spawning not supported by Miri fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs index 27f3e8a9b96e4..0afe18088fa5f 100644 --- a/library/std/tests/switch-stdout.rs +++ b/library/std/tests/switch-stdout.rs @@ -51,6 +51,7 @@ fn switch_stdout_to(file: OwnedHandle) -> OwnedHandle { } #[test] +#[cfg_attr(miri, ignore)] // dup/SetStdHandle not supported by Miri fn switch_stdout() { let temp = common::tmpdir(); let path = temp.join("switch-stdout-output"); diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 4ce81f2846ea9..79a981d0b0d18 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -5,7 +5,8 @@ use std::time::Duration; #[test] #[cfg_attr(target_os = "emscripten", ignore)] -fn sleep() { +#[cfg_attr(miri, ignore)] // Miri does not like the thread leak +fn sleep_very_long() { let finished = Arc::new(Mutex::new(false)); let t_finished = finished.clone(); thread::spawn(move || { diff --git a/library/stdarch b/library/stdarch index 56087ea170d87..c0257c1660e78 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 56087ea170d878a7a57b3a5725e0c00f5f5cad70 +Subproject commit c0257c1660e78c80ad1b9136fcc5555b14da5b4c diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 4c42e3bae563e..0e2409f63ab1a 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -9,4 +9,6 @@ std = { path = "../std" } core = { path = "../core" } panic_unwind = { path = "../panic_unwind" } panic_abort = { path = "../panic_abort" } + +[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2.150", default-features = false } diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 8096e498263ef..7e224d60d9dc5 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -46,7 +46,6 @@ pub struct ConsoleTestDiscoveryState { pub tests: usize, pub benchmarks: usize, pub ignored: usize, - pub options: Options, } impl ConsoleTestDiscoveryState { @@ -56,13 +55,7 @@ impl ConsoleTestDiscoveryState { None => None, }; - Ok(ConsoleTestDiscoveryState { - log_out, - tests: 0, - benchmarks: 0, - ignored: 0, - options: opts.options, - }) + Ok(ConsoleTestDiscoveryState { log_out, tests: 0, benchmarks: 0, ignored: 0 }) } pub fn write_log(&mut self, msg: F) -> io::Result<()> @@ -322,10 +315,8 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu // Prevent the usage of `Instant` in some cases: // - It's currently not supported for wasm targets. - // - We disable it for miri because it's not available when isolation is enabled. - let is_instant_unsupported = (cfg!(target_family = "wasm") && !cfg!(target_os = "wasi")) - || cfg!(target_os = "zkvm") - || cfg!(miri); + let is_instant_unsupported = + (cfg!(target_family = "wasm") && !cfg!(target_os = "wasi")) || cfg!(target_os = "zkvm"); let start_time = (!is_instant_unsupported).then(Instant::now); run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 728c73d8c502a..f3a26e2593831 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -17,7 +17,6 @@ #![unstable(feature = "test", issue = "50297")] #![doc(test(attr(deny(warnings))))] #![doc(rust_logo)] -#![feature(generic_nonzero)] #![feature(rustdoc_internals)] #![feature(internal_output_capture)] #![feature(staged_api)] @@ -140,7 +139,10 @@ pub fn test_main(args: &[String], tests: Vec, options: Option {} Ok(false) => process::exit(ERROR_EXIT_CODE), Err(e) => { diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index b7418d1189c77..bbd1db8dfa57f 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -15,10 +15,12 @@ doc = false [dependencies] core = { path = "../core" } -libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false } compiler_builtins = "0.1.0" cfg-if = "1.0" +[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] +libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false } + [target.'cfg(target_os = "xous")'.dependencies] unwinding = { version = "0.2.1", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 45aa0ea4fbb8d..544d9fbf1ae0f 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -3,8 +3,8 @@ #![feature(link_cfg)] #![feature(staged_api)] #![feature(c_unwind)] -#![cfg_attr(bootstrap, feature(cfg_target_abi))] #![feature(strict_provenance)] +#![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( all(target_family = "wasm", not(target_os = "emscripten")), @@ -12,6 +12,10 @@ )] #![allow(internal_features)] +// Force libc to be included even if unused. This is required by many platforms. +#[cfg(not(all(windows, target_env = "msvc")))] +extern crate libc as _; + cfg_if::cfg_if! { if #[cfg(target_env = "msvc")] { // Windows MSVC no extra unwinder support needed diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 0a0dc9438b305..e34e73a351637 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -1,6 +1,6 @@ #![allow(nonstandard_style)] -use libc::{c_int, c_void}; +use core::ffi::{c_int, c_void}; #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq)] @@ -123,7 +123,7 @@ extern "C" { } cfg_if::cfg_if! { -if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "netbsd", not(target_arch = "arm")))] { +if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos", target_os = "netbsd", not(target_arch = "arm")))] { // Not ARM EHABI #[repr(C)] #[derive(Copy, Clone, PartialEq)] diff --git a/library/unwind/src/unwinding.rs b/library/unwind/src/unwinding.rs index 95e2eb000ccbd..083acaeb56af2 100644 --- a/library/unwind/src/unwinding.rs +++ b/library/unwind/src/unwinding.rs @@ -1,6 +1,6 @@ #![allow(nonstandard_style)] -use libc::{c_int, c_void}; +use core::ffi::{c_int, c_void}; #[repr(C)] #[derive(Copy, Clone, PartialEq)] diff --git a/library/unwind/src/wasm.rs b/library/unwind/src/wasm.rs index b06671bcb8309..f4ffac1ba16da 100644 --- a/library/unwind/src/wasm.rs +++ b/library/unwind/src/wasm.rs @@ -59,7 +59,10 @@ pub unsafe fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwi wasm_throw(0, exception.cast()) } else { let _ = exception; - core::arch::wasm32::unreachable() + #[cfg(target_arch = "wasm32")] + core::arch::wasm32::unreachable(); + #[cfg(target_arch = "wasm64")] + core::arch::wasm64::unreachable(); } } } diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index e2c90cc8c8474..9c2b7500a20f7 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -236,12 +236,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - [[package]] name = "errno" version = "0.3.8" @@ -448,26 +442,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rayon" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -598,7 +572,6 @@ dependencies = [ "libc", "ntapi", "once_cell", - "rayon", "windows", ] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index b232885c590ee..c7a513d08908a 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -64,7 +64,7 @@ walkdir = "2.4" xz2 = "0.1" # Dependencies needed by the build-metrics feature -sysinfo = { version = "0.30", optional = true } +sysinfo = { version = "0.30", default-features = false, optional = true } [target.'cfg(windows)'.dependencies.junction] version = "1.0.0" diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 6e49bcc974469..e464e444fea20 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -612,8 +612,14 @@ def download_toolchain(self): self.fix_bin_or_dylib("{}/libexec/rust-analyzer-proc-macro-srv".format(bin_root)) lib_dir = "{}/lib".format(bin_root) for lib in os.listdir(lib_dir): - if lib.endswith(".so"): - self.fix_bin_or_dylib(os.path.join(lib_dir, lib)) + # .so is not necessarily the suffix, there can be version numbers afterwards. + if ".so" in lib: + elf_path = os.path.join(lib_dir, lib) + with open(elf_path, "rb") as f: + magic = f.read(4) + # Patchelf will skip non-ELF files, but issue a warning. + if magic == b"\x7fELF": + self.fix_bin_or_dylib(elf_path) with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(key) @@ -725,7 +731,7 @@ def fix_bin_or_dylib(self, fname): os.path.join(os.path.realpath(nix_deps_dir), "lib") ] patchelf_args = ["--set-rpath", ":".join(rpath_entries)] - if not fname.endswith(".so"): + if ".so" not in fname: # Finally, set the correct .interp for binaries with open("{}/nix-support/dynamic-linker".format(nix_deps_dir)) as dynamic_linker: patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()] @@ -1029,13 +1035,8 @@ def check_vendored_status(self): if self.use_vendored_sources: vendor_dir = os.path.join(self.rust_root, 'vendor') if not os.path.exists(vendor_dir): - sync_dirs = "--sync ./src/tools/cargo/Cargo.toml " \ - "--sync ./src/tools/rust-analyzer/Cargo.toml " \ - "--sync ./compiler/rustc_codegen_cranelift/Cargo.toml " \ - "--sync ./src/bootstrap/Cargo.toml " eprint('ERROR: vendoring required, but vendor directory does not exist.') - eprint(' Run `cargo vendor {}` to initialize the ' - 'vendor directory.'.format(sync_dirs)) + eprint(' Run `x.py vendor` to initialize the vendor directory.') eprint(' Alternatively, use the pre-vendored `rustc-src` dist component.') eprint(' To get a stable/beta/nightly version, download it from: ') eprint(' ' diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 818a7daadcab8..768aac912ce47 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -152,9 +152,9 @@ def v(*args): # (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/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 0b67079917c7f..fc433bc5843b5 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -52,6 +52,34 @@ check-aux: src/tools/cargo \ src/tools/cargotest \ $(BOOTSTRAP_ARGS) + # Run standard library tests in Miri. + $(Q)BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(BOOTSTRAP) miri --stage 2 \ + library/core \ + library/alloc \ + --no-doc + # Some doctests have intentional memory leaks. + $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \ + $(BOOTSTRAP) miri --stage 2 \ + library/core \ + library/alloc \ + --doc + # In `std` we cannot test everything. + $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(BOOTSTRAP) miri --stage 2 library/std \ + --no-doc -- \ + --skip fs:: --skip net:: --skip process:: --skip sys::pal:: + $(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(BOOTSTRAP) miri --stage 2 library/std \ + --doc -- \ + --skip fs:: --skip net:: --skip process:: --skip sys::pal:: + # Also test some very target-specific modules on other targets + # (making sure to cover an i686 target as well). + $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ + $(BOOTSTRAP) miri --stage 2 library/std \ + --target aarch64-apple-darwin,i686-pc-windows-msvc \ + --no-doc -- \ + time:: sync:: thread:: env:: dist: $(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS) distcheck: diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 98495f25bc662..44fb1911fc6d3 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -15,7 +15,8 @@ use std::{ }; use bootstrap::{ - find_recent_config_change_ids, t, Build, Config, Subcommand, CONFIG_CHANGE_HISTORY, + find_recent_config_change_ids, human_readable_changes, t, Build, Config, Subcommand, + CONFIG_CHANGE_HISTORY, }; fn main() { @@ -36,6 +37,7 @@ fn main() { build_lock = fd_lock::RwLock::new(t!(fs::OpenOptions::new() .write(true) + .truncate(true) .create(true) .open(&lock_path))); _build_lock_guard = match build_lock.try_write() { @@ -130,10 +132,6 @@ fn main() { fn check_version(config: &Config) -> Option { let mut msg = String::new(); - if config.changelog_seen.is_some() { - msg.push_str("WARNING: The use of `changelog-seen` is deprecated. Please refer to `change-id` option in `config.example.toml` instead.\n"); - } - let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap().change_id; let warned_id_path = config.out.join("bootstrap").join(".last-warned-change-id"); @@ -146,8 +144,8 @@ fn check_version(config: &Config) -> Option { // then use the one from the config.toml. This way we never show the same warnings // more than once. if let Ok(t) = fs::read_to_string(&warned_id_path) { - let last_warned_id = - usize::from_str(&t).expect(&format!("{} is corrupted.", warned_id_path.display())); + let last_warned_id = usize::from_str(&t) + .unwrap_or_else(|_| panic!("{} is corrupted.", warned_id_path.display())); // We only use the last_warned_id if it exists in `CONFIG_CHANGE_HISTORY`. // Otherwise, we may retrieve all the changes if it's not the highest value. @@ -164,14 +162,7 @@ fn check_version(config: &Config) -> Option { } msg.push_str("There have been changes to x.py since you last updated:\n"); - - for change in changes { - msg.push_str(&format!(" [{}] {}\n", change.severity, change.summary)); - msg.push_str(&format!( - " - PR Link https://github.com/rust-lang/rust/pull/{}\n", - change.change_id - )); - } + msg.push_str(&human_readable_changes(&changes)); msg.push_str("NOTE: to silence this warning, "); msg.push_str(&format!( diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 74a924d86c796..4b182a7a693dd 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -150,7 +150,7 @@ fn main() { { cmd.arg("-Ztls-model=initial-exec"); } - } else if std::env::var("MIRI").is_err() { + } else { // Find any host flags that were passed by bootstrap. // The flags are stored in a RUSTC_HOST_FLAGS variable, separated by spaces. if let Ok(flags) = std::env::var("RUSTC_HOST_FLAGS") { @@ -220,6 +220,12 @@ fn main() { } } + if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() { + if let Some("rustc_driver") = crate_name { + cmd.arg("-Clink-args=-Wl,-q"); + } + } + let is_test = args.iter().any(|a| a == "--test"); if verbose > 2 { let rust_env_vars = @@ -244,12 +250,6 @@ fn main() { eprintln!("{prefix} libdir: {libdir:?}"); } - if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() { - if let Some("rustc_driver") = crate_name { - cmd.arg("-Clink-args=-Wl,-q"); - } - } - bin_helpers::maybe_dump(format!("stage{stage}-rustc"), &cmd); let start = Instant::now(); diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index a90139a070ac2..8235d4634b753 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -11,6 +11,16 @@ use crate::core::config::TargetSelection; use crate::{Compiler, Mode, Subcommand}; use std::path::{Path, PathBuf}; +pub fn cargo_subcommand(kind: Kind) -> &'static str { + match kind { + Kind::Check + // We ensure check steps for both std and rustc from build_steps/clippy, so handle `Kind::Clippy` as well. + | Kind::Clippy => "check", + Kind::Fix => "fix", + _ => unreachable!(), + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { pub target: TargetSelection, @@ -22,69 +32,6 @@ pub struct Std { crates: Vec, } -/// Returns args for the subcommand itself (not for cargo) -fn args(builder: &Builder<'_>) -> Vec { - fn strings<'a>(arr: &'a [&str]) -> impl Iterator + 'a { - arr.iter().copied().map(String::from) - } - - if let Subcommand::Clippy { fix, allow_dirty, allow_staged, allow, deny, warn, forbid } = - &builder.config.cmd - { - // disable the most spammy clippy lints - let ignored_lints = [ - "many_single_char_names", // there are a lot in stdarch - "collapsible_if", - "type_complexity", - "missing_safety_doc", // almost 3K warnings - "too_many_arguments", - "needless_lifetimes", // people want to keep the lifetimes - "wrong_self_convention", - ]; - let mut args = vec![]; - if *fix { - #[rustfmt::skip] - args.extend(strings(&[ - "--fix", "-Zunstable-options", - // FIXME: currently, `--fix` gives an error while checking tests for libtest, - // possibly because libtest is not yet built in the sysroot. - // As a workaround, avoid checking tests and benches when passed --fix. - "--lib", "--bins", "--examples", - ])); - - if *allow_dirty { - args.push("--allow-dirty".to_owned()); - } - - if *allow_staged { - args.push("--allow-staged".to_owned()); - } - } - - args.extend(strings(&["--", "--cap-lints", "warn"])); - args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint))); - let mut clippy_lint_levels: Vec = Vec::new(); - allow.iter().for_each(|v| clippy_lint_levels.push(format!("-A{}", v))); - deny.iter().for_each(|v| clippy_lint_levels.push(format!("-D{}", v))); - warn.iter().for_each(|v| clippy_lint_levels.push(format!("-W{}", v))); - forbid.iter().for_each(|v| clippy_lint_levels.push(format!("-F{}", v))); - args.extend(clippy_lint_levels); - args.extend(builder.config.free_args.clone()); - args - } else { - builder.config.free_args.clone() - } -} - -fn cargo_subcommand(kind: Kind) -> &'static str { - match kind { - Kind::Check => "check", - Kind::Clippy => "clippy", - Kind::Fix => "fix", - _ => unreachable!(), - } -} - impl Std { pub fn new(target: TargetSelection) -> Self { Self { target, crates: vec![] } @@ -136,7 +83,7 @@ impl Step for Std { run_cargo( builder, cargo, - args(builder), + builder.config.free_args.clone(), &libstd_stamp(builder, compiler, target), vec![], true, @@ -193,7 +140,7 @@ impl Step for Std { run_cargo( builder, cargo, - args(builder), + builder.config.free_args.clone(), &libstd_test_stamp(builder, compiler, target), vec![], true, @@ -268,7 +215,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 @@ -290,7 +237,7 @@ impl Step for Rustc { run_cargo( builder, cargo, - args(builder), + builder.config.free_args.clone(), &librustc_stamp(builder, compiler, target), vec![], true, @@ -356,7 +303,7 @@ impl Step for CodegenBackend { run_cargo( builder, cargo, - args(builder), + builder.config.free_args.clone(), &codegen_backend_stamp(builder, compiler, target, backend), vec![], true, @@ -422,7 +369,7 @@ impl Step for RustAnalyzer { run_cargo( builder, cargo, - args(builder), + builder.config.free_args.clone(), &stamp(builder, compiler, target), vec![], true, @@ -447,7 +394,7 @@ macro_rules! tool_check_step { impl Step for $name { type Output = (); const ONLY_HOSTS: bool = true; - // don't ever check out-of-tree tools by default, they'll fail when toolstate is broken + /// don't ever check out-of-tree tools by default, they'll fail when toolstate is broken const DEFAULT: bool = matches!($source_type, SourceType::InTree) $( && $default )?; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -485,7 +432,7 @@ macro_rules! tool_check_step { run_cargo( builder, cargo, - args(builder), + builder.config.free_args.clone(), &stamp(builder, compiler, target), vec![], true, diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 9e103a350e654..a81d640301363 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -1,4 +1,4 @@ -//! Implementation of `make clean` in rustbuild. +//! `./x.py clean` //! //! Responsible for cleaning out a build directory of all old and stale //! artifacts to prepare for a fresh build. Currently doesn't remove the @@ -181,39 +181,33 @@ fn rm_rf(path: &Path) { } Ok(metadata) => { if metadata.file_type().is_file() || metadata.file_type().is_symlink() { - do_op(path, "remove file", |p| { - fs::remove_file(p).or_else(|e| { - // Work around the fact that we cannot - // delete an executable while it runs on Windows. - #[cfg(windows)] + do_op(path, "remove file", |p| match fs::remove_file(p) { + #[cfg(windows)] + Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied && p.file_name().and_then(std::ffi::OsStr::to_str) - == Some("bootstrap.exe") - { - eprintln!("WARNING: failed to delete '{}'.", p.display()); - return Ok(()); - } - Err(e) - }) + == Some("bootstrap.exe") => + { + eprintln!("WARNING: failed to delete '{}'.", p.display()); + Ok(()) + } + r => r, }); + return; } for file in t!(fs::read_dir(path)) { rm_rf(&t!(file).path()); } - do_op(path, "remove dir", |p| { - fs::remove_dir(p).or_else(|e| { - // Check for dir not empty on Windows - // FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized, - // match on `e.kind()` instead. - #[cfg(windows)] - if e.raw_os_error() == Some(145) { - return Ok(()); - } - Err(e) - }) + do_op(path, "remove dir", |p| match fs::remove_dir(p) { + // Check for dir not empty on Windows + // FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized, + // match on `e.kind()` instead. + #[cfg(windows)] + Err(e) if e.raw_os_error() == Some(145) => Ok(()), + r => r, }); } }; @@ -228,14 +222,14 @@ where // On windows we can't remove a readonly file, and git will often clone files as readonly. // As a result, we have some special logic to remove readonly files on windows. // This is also the reason that we can't use things like fs::remove_dir_all(). - Err(ref e) if cfg!(windows) && e.kind() == ErrorKind::PermissionDenied => { + #[cfg(windows)] + Err(ref e) if e.kind() == ErrorKind::PermissionDenied => { let m = t!(path.symlink_metadata()); let mut p = m.permissions(); p.set_readonly(false); t!(fs::set_permissions(path, p)); f(path).unwrap_or_else(|e| { // Delete symlinked directories on Windows - #[cfg(windows)] if m.file_type().is_symlink() && path.is_dir() && fs::remove_dir(path).is_ok() { return; } diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs new file mode 100644 index 0000000000000..01b5e99116ff2 --- /dev/null +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -0,0 +1,330 @@ +//! Implementation of running clippy on the compiler, standard library and various tools. + +use std::path::Path; + +use crate::builder::Builder; +use crate::builder::ShouldRun; +use crate::core::builder; +use crate::core::builder::crate_description; +use crate::core::builder::Alias; +use crate::core::builder::Kind; +use crate::core::builder::RunConfig; +use crate::core::builder::Step; +use crate::Mode; +use crate::Subcommand; +use crate::TargetSelection; + +use super::check; +use super::compile; +use super::compile::librustc_stamp; +use super::compile::libstd_stamp; +use super::compile::run_cargo; +use super::compile::rustc_cargo; +use super::compile::std_cargo; +use super::tool::prepare_tool_cargo; +use super::tool::SourceType; + +/// Disable the most spammy clippy lints +const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[ + "many_single_char_names", // there are a lot in stdarch + "collapsible_if", + "type_complexity", + "missing_safety_doc", // almost 3K warnings + "too_many_arguments", + "needless_lifetimes", // people want to keep the lifetimes + "wrong_self_convention", +]; + +fn lint_args(builder: &Builder<'_>, ignored_rules: &[&str]) -> Vec { + fn strings<'a>(arr: &'a [&str]) -> impl Iterator + 'a { + arr.iter().copied().map(String::from) + } + + let Subcommand::Clippy { fix, allow_dirty, allow_staged, allow, deny, warn, forbid } = + &builder.config.cmd + else { + unreachable!("clippy::lint_args can only be called from `clippy` subcommands."); + }; + + let mut args = vec![]; + if *fix { + #[rustfmt::skip] + args.extend(strings(&[ + "--fix", "-Zunstable-options", + // FIXME: currently, `--fix` gives an error while checking tests for libtest, + // possibly because libtest is not yet built in the sysroot. + // As a workaround, avoid checking tests and benches when passed --fix. + "--lib", "--bins", "--examples", + ])); + + if *allow_dirty { + args.push("--allow-dirty".to_owned()); + } + + if *allow_staged { + args.push("--allow-staged".to_owned()); + } + } + + args.extend(strings(&["--"])); + + if deny.is_empty() && forbid.is_empty() { + args.extend(strings(&["--cap-lints", "warn"])); + } + + let all_args = std::env::args().collect::>(); + args.extend(get_clippy_rules_in_order(&all_args, allow, deny, warn, forbid)); + + args.extend(ignored_rules.iter().map(|lint| format!("-Aclippy::{}", lint))); + args.extend(builder.config.free_args.clone()); + args +} + +/// We need to keep the order of the given clippy lint rules before passing them. +/// Since clap doesn't offer any useful interface for this purpose out of the box, +/// we have to handle it manually. +pub(crate) fn get_clippy_rules_in_order( + all_args: &[String], + allow_rules: &[String], + deny_rules: &[String], + warn_rules: &[String], + forbid_rules: &[String], +) -> Vec { + let mut result = vec![]; + + for (prefix, item) in + [("-A", allow_rules), ("-D", deny_rules), ("-W", warn_rules), ("-F", forbid_rules)] + { + item.iter().for_each(|v| { + let rule = format!("{prefix}{v}"); + let position = all_args.iter().position(|t| t == &rule).unwrap(); + result.push((position, rule)); + }); + } + + result.sort_by_key(|&(position, _)| position); + result.into_iter().map(|v| v.1).collect() +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Std { + pub target: TargetSelection, + /// Whether to lint only a subset of crates. + crates: Vec, +} + +impl Step for Std { + type Output = (); + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.crate_or_deps("sysroot").path("library") + } + + fn make_run(run: RunConfig<'_>) { + let crates = run.make_run_crates(Alias::Library); + run.builder.ensure(Std { target: run.target, crates }); + } + + fn run(self, builder: &Builder<'_>) { + builder.update_submodule(&Path::new("library").join("stdarch")); + + let target = self.target; + let compiler = builder.compiler(builder.top_stage, builder.config.build); + + let mut cargo = + builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, "clippy"); + + std_cargo(builder, target, compiler.stage, &mut cargo); + + for krate in &*self.crates { + cargo.arg("-p").arg(krate); + } + + let _guard = + builder.msg_clippy(format_args!("library{}", crate_description(&self.crates)), target); + + run_cargo( + builder, + cargo, + lint_args(builder, IGNORED_RULES_FOR_STD_AND_RUSTC), + &libstd_stamp(builder, compiler, target), + vec![], + true, + false, + ); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Rustc { + pub target: TargetSelection, + /// Whether to lint only a subset of crates. + crates: Vec, +} + +impl Step for Rustc { + type Output = (); + const ONLY_HOSTS: bool = true; + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.crate_or_deps("rustc-main").path("compiler") + } + + fn make_run(run: RunConfig<'_>) { + let crates = run.make_run_crates(Alias::Compiler); + run.builder.ensure(Rustc { target: run.target, crates }); + } + + /// Lints the compiler. + /// + /// This will lint the compiler for a particular stage of the build using + /// the `compiler` targeting the `target` architecture. + fn run(self, builder: &Builder<'_>) { + let compiler = builder.compiler(builder.top_stage, builder.config.build); + let target = self.target; + + if compiler.stage != 0 { + // If we're not in stage 0, then we won't have a std from the beta + // compiler around. That means we need to make sure there's one in + // the sysroot for the compiler to find. Otherwise, we're going to + // fail when building crates that need to generate code (e.g., build + // scripts and their dependencies). + builder.ensure(compile::Std::new(compiler, compiler.host)); + builder.ensure(compile::Std::new(compiler, target)); + } else { + builder.ensure(check::Std::new(target)); + } + + let mut cargo = builder::Cargo::new( + builder, + compiler, + Mode::Rustc, + SourceType::InTree, + target, + "clippy", + ); + + rustc_cargo(builder, &mut cargo, target, &compiler); + + // Explicitly pass -p for all compiler crates -- this will force cargo + // to also lint the tests/benches/examples for these crates, rather + // than just the leaf crate. + for krate in &*self.crates { + cargo.arg("-p").arg(krate); + } + + let _guard = + builder.msg_clippy(format_args!("compiler{}", crate_description(&self.crates)), target); + + run_cargo( + builder, + cargo, + lint_args(builder, IGNORED_RULES_FOR_STD_AND_RUSTC), + &librustc_stamp(builder, compiler, target), + vec![], + true, + false, + ); + } +} + +macro_rules! lint_any { + ($( + $name:ident, $path:expr, $readable_name:expr + $(,lint_by_default = $lint_by_default:expr)* + ; + )+) => { + $( + + #[derive(Debug, Clone, Hash, PartialEq, Eq)] + pub struct $name { + pub target: TargetSelection, + } + + impl Step for $name { + type Output = (); + const DEFAULT: bool = if false $(|| $lint_by_default)* { true } else { false }; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path($path) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure($name { + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let compiler = builder.compiler(builder.top_stage, builder.config.build); + let target = self.target; + + builder.ensure(check::Rustc::new(target, builder)); + + let cargo = prepare_tool_cargo( + builder, + compiler, + Mode::ToolRustc, + target, + "clippy", + $path, + SourceType::InTree, + &[], + ); + + let _guard = builder.msg_tool( + Kind::Clippy, + Mode::ToolRustc, + $readable_name, + compiler.stage, + &compiler.host, + &target, + ); + + let stamp = builder + .cargo_out(compiler, Mode::ToolRustc, target) + .join(format!(".{}-check.stamp", stringify!($name).to_lowercase())); + + run_cargo( + builder, + cargo, + lint_args(builder, &[]), + &stamp, + vec![], + true, + false, + ); + } + } + )+ + } +} + +lint_any!( + Bootstrap, "src/bootstrap", "bootstrap"; + BuildHelper, "src/tools/build_helper", "build_helper"; + BuildManifest, "src/tools/build-manifest", "build-manifest"; + CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri"; + Clippy, "src/tools/clippy", "clippy"; + CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata"; + Compiletest, "src/tools/compiletest", "compiletest"; + CoverageDump, "src/tools/coverage-dump", "coverage-dump"; + Jsondocck, "src/tools/jsondocck", "jsondocck"; + Jsondoclint, "src/tools/jsondoclint", "jsondoclint"; + LintDocs, "src/tools/lint-docs", "lint-docs"; + LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker"; + Miri, "src/tools/miri", "miri"; + MiroptTestTools, "src/tools/miropt-test-tools", "miropt-test-tools"; + OptDist, "src/tools/opt-dist", "opt-dist"; + RemoteTestClient, "src/tools/remote-test-client", "remote-test-client"; + RemoteTestServer, "src/tools/remote-test-server", "remote-test-server"; + Rls, "src/tools/rls", "rls"; + RustAnalyzer, "src/tools/rust-analyzer", "rust-analyzer"; + RustDemangler, "src/tools/rust-demangler", "rust-demangler"; + Rustdoc, "src/tools/rustdoc", "clippy"; + Rustfmt, "src/tools/rustfmt", "rustfmt"; + RustInstaller, "src/tools/rust-installer", "rust-installer"; + Tidy, "src/tools/tidy", "tidy"; +); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index e927b491c71ea..1d46a158f9ed2 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -43,7 +43,7 @@ pub struct Std { /// This shouldn't be used from other steps; see the comment on [`Rustc`]. crates: Vec, /// When using download-rustc, we need to use a new build of `std` for running unit tests of Std itself, - /// but we need to use the downloaded copy of std for linking to rustdoc. Allow this to be overriden by `builder.ensure` from other steps. + /// but we need to use the downloaded copy of std for linking to rustdoc. Allow this to be overridden by `builder.ensure` from other steps. force_recompile: bool, extra_rust_args: &'static [&'static str], is_for_mir_opt_tests: bool, @@ -394,16 +394,13 @@ fn copy_self_contained_objects( target_deps.push((libunwind_path, DependencyType::TargetSelfContained)); } } else if target.contains("-wasi") { - let srcdir = builder - .wasi_root(target) - .unwrap_or_else(|| { - panic!( - "Target {:?} does not have a \"wasi-root\" key in Config.toml", - target.triple - ) - }) - .join("lib") - .join(target.to_string().replace("-preview1", "").replace("p2", "").replace("p1", "")); + let srcdir = builder.wasi_libdir(target).unwrap_or_else(|| { + panic!( + "Target {:?} does not have a \"wasi-root\" key in Config.toml \ + or `$WASI_SDK_PATH` set", + target.triple + ) + }); for &obj in &["libc.a", "crt1-command.o", "crt1-reactor.o"] { copy_and_stamp( builder, @@ -514,12 +511,8 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car } if target.contains("-wasi") { - if let Some(p) = builder.wasi_root(target) { - let root = format!( - "native={}/lib/{}", - p.to_str().unwrap(), - target.to_string().replace("-preview1", "") - ); + if let Some(dir) = builder.wasi_libdir(target) { + let root = format!("native={}", dir.to_str().unwrap()); cargo.rustflag("-L").rustflag(&root); } } @@ -550,6 +543,10 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car cargo.rustflag("-Cforce-unwind-tables=yes"); } + // Enable frame pointers by default for the library. Note that they are still controlled by a + // separate setting for the compiler. + cargo.rustflag("-Cforce-frame-pointers=yes"); + let html_root = format!("-Zcrate-attr=doc(html_root_url=\"{}/\")", builder.doc_rust_lang_org_channel(),); cargo.rustflag(&html_root); @@ -639,13 +636,13 @@ impl Step for StdLink { t!(fs::create_dir_all(&sysroot_bin_dir)); builder.cp_link_r(&stage0_bin_dir, &sysroot_bin_dir); - // Copy all *.so files from stage0/lib to stage0-sysroot/lib + // Copy all files from stage0/lib to stage0-sysroot/lib let stage0_lib_dir = builder.out.join(host).join("stage0/lib"); if let Ok(files) = fs::read_dir(stage0_lib_dir) { for file in files { let file = t!(file); let path = file.path(); - if path.is_file() && is_dylib(&file.file_name().into_string().unwrap()) { + if path.is_file() { builder .copy_link(&path, &sysroot.join("lib").join(path.file_name().unwrap())); } @@ -821,6 +818,7 @@ fn cp_rustc_component_to_ci_sysroot( #[derive(Debug, PartialOrd, Ord, Clone, PartialEq, Eq, Hash)] pub struct Rustc { pub target: TargetSelection, + /// The **previous** compiler used to compile this compiler. pub compiler: Compiler, /// Whether to build a subset of crates, rather than the whole compiler. /// @@ -837,13 +835,13 @@ impl Rustc { } impl Step for Rustc { - // We return the stage of the "actual" compiler (not the uplifted one). - // - // By "actual" we refer to the uplifting logic where we may not compile the requested stage; - // instead, we uplift it from the previous stages. Which can lead to bootstrap failures in - // specific situations where we request stage X from other steps. However we may end up - // uplifting it from stage Y, causing the other stage to fail when attempting to link with - // stage X which was never actually built. + /// We return the stage of the "actual" compiler (not the uplifted one). + /// + /// By "actual" we refer to the uplifting logic where we may not compile the requested stage; + /// instead, we uplift it from the previous stages. Which can lead to bootstrap failures in + /// specific situations where we request stage X from other steps. However we may end up + /// uplifting it from stage Y, causing the other stage to fail when attempting to link with + /// stage X which was never actually built. type Output = u32; const ONLY_HOSTS: bool = true; const DEFAULT: bool = false; @@ -940,55 +938,10 @@ impl Step for Rustc { "build", ); - rustc_cargo(builder, &mut cargo, target, compiler.stage); - - 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"); - } + rustc_cargo(builder, &mut cargo, target, &compiler); - // 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); @@ -1039,7 +992,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)) @@ -1050,7 +1008,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, @@ -1076,7 +1034,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( @@ -1126,12 +1129,6 @@ pub fn rustc_cargo_env( cargo.env("CFG_DEFAULT_LINKER", s); } - if builder.config.rustc_parallel { - // keep in sync with `bootstrap/lib.rs:Build::rustc_features` - // `cfg` option for rustc, `features` option for cargo, for conditional compilation - cargo.rustflag("--cfg=parallel_compiler"); - cargo.rustdocflag("--cfg=parallel_compiler"); - } if builder.config.rust_verify_llvm_ir { cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); } @@ -1142,7 +1139,7 @@ pub fn rustc_cargo_env( // busting caches (e.g. like #71152). if builder.config.llvm_enabled(target) { let building_is_expensive = - crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target).is_err(); + crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target).should_build(); // `top_stage == stage` might be false for `check --stage 1`, if we are building the stage 1 compiler let can_skip_build = builder.kind == Kind::Check && builder.top_stage == stage; let should_skip_build = building_is_expensive && can_skip_build; @@ -1285,11 +1282,7 @@ fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { if path.path.to_str().unwrap().contains(CODEGEN_BACKEND_PREFIX) { let mut needs_codegen_backend_config = true; for backend in run.builder.config.codegen_backends(run.target) { - if path - .path - .to_str() - .unwrap() - .ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + &backend)) + if path.path.to_str().unwrap().ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend)) { needs_codegen_backend_config = false; } @@ -1309,7 +1302,7 @@ fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { impl Step for CodegenBackend { type Output = (); const ONLY_HOSTS: bool = true; - // Only the backends specified in the `codegen-backends` entry of `config.toml` are built. + /// Only the backends specified in the `codegen-backends` entry of `config.toml` are built. const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1514,12 +1507,9 @@ impl Step for Sysroot { run.never() } - /// Returns the sysroot for the `compiler` specified that *this build system - /// generates*. - /// - /// That is, the sysroot for the stage0 compiler is not what the compiler - /// thinks it is by default, but it's the same as the default for stages - /// 1-3. + /// Returns the sysroot that `compiler` is supposed to use. + /// For the stage0 compiler, this is stage0-sysroot (because of the initial std build). + /// For all other stages, it's the same stage directory that the compiler lives in. fn run(self, builder: &Builder<'_>) -> PathBuf { let compiler = self.compiler; let host_dir = builder.out.join(compiler.host.triple); @@ -1852,7 +1842,7 @@ impl Step for Assemble { extra_features: vec![], }); let tool_exe = exe("llvm-bitcode-linker", target_compiler.host); - builder.copy_link(&src_path, &libdir_bin.join(&tool_exe)); + builder.copy_link(&src_path, &libdir_bin.join(tool_exe)); } // Ensure that `libLLVM.so` ends up in the newly build compiler directory, @@ -2136,18 +2126,9 @@ pub struct CargoTarget<'a> { #[derive(Deserialize)] #[serde(tag = "reason", rename_all = "kebab-case")] pub enum CargoMessage<'a> { - CompilerArtifact { - package_id: Cow<'a, str>, - features: Vec>, - filenames: Vec>, - target: CargoTarget<'a>, - }, - BuildScriptExecuted { - package_id: Cow<'a, str>, - }, - BuildFinished { - success: bool, - }, + CompilerArtifact { filenames: Vec>, target: CargoTarget<'a> }, + BuildScriptExecuted, + BuildFinished, } pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path) { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 012d64e534439..5bc9d7615e2bc 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -25,7 +25,7 @@ use crate::core::build_steps::llvm; use crate::core::build_steps::tool::{self, Tool}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; -use crate::utils::channel; +use crate::utils::channel::{self, Info}; use crate::utils::helpers::{exe, is_dylib, output, t, target_supports_cranelift_backend, timeit}; use crate::utils::tarball::{GeneratedTarball, OverlayKind, Tarball}; use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS}; @@ -107,7 +107,7 @@ impl Step for JsonDocs { builder.top_stage, host, builder, - DocumentationFormat::JSON, + DocumentationFormat::Json, )); let dest = "share/doc/rust/json"; @@ -830,6 +830,12 @@ fn copy_src_dirs( return false; } + // Cargo tests use some files like `.gitignore` that we would otherwise exclude. + const CARGO_TESTS: &[&str] = &["tools/cargo/tests", "tools\\cargo\\tests"]; + if CARGO_TESTS.iter().any(|path| spath.contains(path)) { + return true; + } + let full_path = Path::new(dir).join(path); if exclude_dirs.iter().any(|excl| full_path == Path::new(excl)) { return false; @@ -985,24 +991,32 @@ impl Step for PlainSourceTarball { // Create the version file builder.create(&plain_dst_src.join("version"), &builder.rust_version()); - if let Some(info) = builder.rust_info().info() { - channel::write_commit_hash_file(plain_dst_src, &info.sha); - channel::write_commit_info_file(plain_dst_src, info); - } + + // Create the files containing git info, to ensure --version outputs the same. + let write_git_info = |info: Option<&Info>, path: &Path| { + if let Some(info) = info { + t!(std::fs::create_dir_all(path)); + channel::write_commit_hash_file(path, &info.sha); + channel::write_commit_info_file(path, info); + } + }; + write_git_info(builder.rust_info().info(), plain_dst_src); + write_git_info(builder.cargo_info.info(), &plain_dst_src.join("./src/tools/cargo")); // If we're building from git or tarball sources, we need to vendor // a complete distribution. if builder.rust_info().is_managed_git_subrepository() || builder.rust_info().is_from_tarball() { - if builder.rust_info().is_managed_git_subrepository() { - // Ensure we have the submodules checked out. - builder.update_submodule(Path::new("src/tools/cargo")); + // Ensure we have all submodules from src and other directories checked out. + for submodule in builder.get_all_submodules() { + builder.update_submodule(Path::new(submodule)); } // Vendor all Cargo dependencies let mut cmd = Command::new(&builder.initial_cargo); cmd.arg("vendor") + .arg("--versioned-dirs") .arg("--sync") .arg(builder.src.join("./src/tools/cargo/Cargo.toml")) .arg("--sync") @@ -1010,6 +1024,8 @@ impl Step for PlainSourceTarball { .arg("--sync") .arg(builder.src.join("./compiler/rustc_codegen_cranelift/Cargo.toml")) .arg("--sync") + .arg(builder.src.join("./compiler/rustc_codegen_gcc/Cargo.toml")) + .arg("--sync") .arg(builder.src.join("./src/bootstrap/Cargo.toml")) // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. @@ -1027,6 +1043,20 @@ impl Step for PlainSourceTarball { builder.create(&cargo_config_dir.join("config.toml"), &config); } + // Delete extraneous directories + // FIXME: if we're managed by git, we should probably instead ask git if the given path + // is managed by it? + for entry in walkdir::WalkDir::new(tarball.image_dir()) + .follow_links(true) + .into_iter() + .filter_map(|e| e.ok()) + { + if entry.path().is_dir() && entry.path().file_name() == Some(OsStr::new("__pycache__")) + { + t!(fs::remove_dir_all(entry.path())); + } + } + tarball.bare() } } @@ -1114,7 +1144,7 @@ impl Step for Rls { let rls = builder.ensure(tool::Rls { compiler, target, extra_features: Vec::new() }); let mut tarball = Tarball::new(builder, "rls", &target.triple); - tarball.set_overlay(OverlayKind::RLS); + tarball.set_overlay(OverlayKind::Rls); tarball.is_preview(true); tarball.add_file(rls, "bin", 0o755); tarball.add_legal_and_readme_to("share/doc/rls"); @@ -1531,6 +1561,7 @@ impl Step for Extended { compiler: builder.compiler(stage, target), backend: "cranelift".to_string(), }); + add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker {compiler, target}); let etc = builder.src.join("src/etc/installer"); @@ -2041,7 +2072,7 @@ fn install_llvm_file( if install_symlink { // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a // symlink is fine here, as this is not a rustup component. - builder.copy_link(&source, &full_dest); + builder.copy_link(source, &full_dest); } else { // Otherwise, replace the symlink with an equivalent linker script. This is used when // projects like miri link against librustc_driver.so. We don't use a symlink, as @@ -2058,7 +2089,7 @@ fn install_llvm_file( } } } else { - builder.install(&source, destination, 0o644); + builder.install(source, destination, 0o644); } } @@ -2103,7 +2134,7 @@ fn maybe_install_llvm( builder.install(&llvm_dylib_path, dst_libdir, 0o644); } !builder.config.dry_run() - } else if let Ok(llvm::LlvmResult { llvm_config, .. }) = + } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) = llvm::prebuilt_llvm_config(builder, target) { let mut cmd = Command::new(llvm_config); @@ -2184,7 +2215,7 @@ impl Step for LlvmTools { builder.ensure(crate::core::build_steps::llvm::Llvm { target }); let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple); - tarball.set_overlay(OverlayKind::LLVM); + tarball.set_overlay(OverlayKind::Llvm); tarball.is_preview(true); if builder.config.llvm_tools_enabled { @@ -2207,9 +2238,56 @@ impl Step for LlvmTools { } } -// Tarball intended for internal consumption to ease rustc/std development. -// -// Should not be considered stable by end users. +#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] +pub struct LlvmBitcodeLinker { + pub compiler: Compiler, + pub target: TargetSelection, +} + +impl Step for LlvmBitcodeLinker { + type Output = Option; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let default = should_build_extended_tool(run.builder, "llvm-bitcode-linker"); + run.alias("llvm-bitcode-linker").default_condition(default) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(LlvmBitcodeLinker { + compiler: run.builder.compiler_for( + run.builder.top_stage, + run.builder.config.build, + run.target, + ), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> Option { + let compiler = self.compiler; + let target = self.target; + + let llbc_linker = + builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] }); + + let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple); + + // Prepare the image directory + let mut tarball = Tarball::new(builder, "llvm-bitcode-linker", &target.triple); + tarball.set_overlay(OverlayKind::LlvmBitcodeLinker); + tarball.is_preview(true); + + tarball.add_file(llbc_linker, self_contained_bin_dir, 0o755); + + Some(tarball.generate()) + } +} + +/// Tarball intended for internal consumption to ease rustc/std development. +/// +/// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct RustDev { pub target: TargetSelection, @@ -2240,7 +2318,7 @@ impl Step for RustDev { } let mut tarball = Tarball::new(builder, "rust-dev", &target.triple); - tarball.set_overlay(OverlayKind::LLVM); + tarball.set_overlay(OverlayKind::Llvm); // LLVM requires a shared object symlink to exist on some platforms. tarball.permit_symlinks(true); @@ -2291,9 +2369,9 @@ impl Step for RustDev { } } -// Tarball intended for internal consumption to ease rustc/std development. -// -// Should not be considered stable by end users. +/// Tarball intended for internal consumption to ease rustc/std development. +/// +/// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Bootstrap { pub target: TargetSelection, diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 51b5cdc056577..38c48bd9570d9 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -506,7 +506,7 @@ impl Step for SharedAssets { run.never() } - // Generate shared resources used by other pieces of documentation. + /// Generate shared resources used by other pieces of documentation. fn run(self, builder: &Builder<'_>) -> Self::Output { let out = builder.doc_out(self.target); @@ -567,9 +567,9 @@ impl Step for Std { stage: run.builder.top_stage, target: run.target, format: if run.builder.config.cmd.json() { - DocumentationFormat::JSON + DocumentationFormat::Json } else { - DocumentationFormat::HTML + DocumentationFormat::Html }, crates: run.make_run_crates(Alias::Library), }); @@ -583,13 +583,13 @@ impl Step for Std { let stage = self.stage; let target = self.target; let out = match self.format { - DocumentationFormat::HTML => builder.doc_out(target), - DocumentationFormat::JSON => builder.json_doc_out(target), + DocumentationFormat::Html => builder.doc_out(target), + DocumentationFormat::Json => builder.json_doc_out(target), }; t!(fs::create_dir_all(&out)); - if self.format == DocumentationFormat::HTML { + if self.format == DocumentationFormat::Html { builder.ensure(SharedAssets { target: self.target }); } @@ -600,10 +600,10 @@ impl Step for Std { .into_string() .expect("non-utf8 paths are unsupported"); let mut extra_args = match self.format { - DocumentationFormat::HTML => { + DocumentationFormat::Html => { vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page] } - DocumentationFormat::JSON => vec!["--output-format", "json"], + DocumentationFormat::Json => vec!["--output-format", "json"], }; if !builder.config.docs_minification { @@ -613,7 +613,7 @@ impl Step for Std { doc_std(builder, self.format, stage, target, &out, &extra_args, &self.crates); // Don't open if the format is json - if let DocumentationFormat::JSON = self.format { + if let DocumentationFormat::Json = self.format { return; } @@ -646,15 +646,15 @@ const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "tes #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum DocumentationFormat { - HTML, - JSON, + Html, + Json, } impl DocumentationFormat { fn as_str(&self) -> &str { match self { - DocumentationFormat::HTML => "HTML", - DocumentationFormat::JSON => "JSON", + DocumentationFormat::Html => "HTML", + DocumentationFormat::Json => "JSON", } } } @@ -678,7 +678,7 @@ fn doc_std( let compiler = builder.compiler(stage, builder.config.build); - let target_doc_dir_name = if format == DocumentationFormat::JSON { "json-doc" } else { "doc" }; + let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" }; let target_dir = builder.stage_out(compiler, Mode::Std).join(target.triple).join(target_doc_dir_name); @@ -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/format.rs b/src/bootstrap/src/core/build_steps/format.rs index fc9f9789bd683..9fc65a0a73a10 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -225,12 +225,12 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { Some(first) => { let find_shortcut_candidates = |p: &PathBuf| { let mut candidates = Vec::new(); - for candidate in WalkBuilder::new(src.clone()).max_depth(Some(3)).build() { - if let Ok(entry) = candidate { - if let Some(dir_name) = p.file_name() { - if entry.path().is_dir() && entry.file_name() == dir_name { - candidates.push(entry.into_path()); - } + for entry in + WalkBuilder::new(src.clone()).max_depth(Some(3)).build().map_while(Result::ok) + { + if let Some(dir_name) = p.file_name() { + if entry.path().is_dir() && entry.file_name() == dir_name { + candidates.push(entry.into_path()); } } } diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 41920fabaa974..6a75f35c93a6e 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -20,8 +20,8 @@ const SHELL: &str = "bash"; #[cfg(not(target_os = "illumos"))] const SHELL: &str = "sh"; -// We have to run a few shell scripts, which choke quite a bit on both `\` -// characters and on `C:\` paths, so normalize both of them away. +/// We have to run a few shell scripts, which choke quite a bit on both `\` +/// characters and on `C:\` paths, so normalize both of them away. fn sanitize_sh(path: &Path) -> String { let path = path.to_str().unwrap().replace('\\', "/"); return change_drive(unc_to_lfs(&path)).unwrap_or(path); @@ -130,7 +130,7 @@ fn prepare_dir(destdir_env: &Option, mut path: PathBuf) -> String { // https://www.gnu.org/prep/standards/html_node/DESTDIR.html if let Some(destdir) = destdir_env { let without_destdir = path.clone(); - path = destdir.clone(); + path.clone_from(destdir); // Custom .join() which ignores disk roots. for part in without_destdir.components() { if let Component::Normal(s) = part { @@ -300,6 +300,15 @@ install!((self, builder, _config), ); } }; + LlvmBitcodeLinker, alias = "llvm-bitcode-linker", Self::should_build(_config), only_hosts: true, { + if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { compiler: self.compiler, target: self.target }) { + install_sh(builder, "llvm-bitcode-linker", self.compiler.stage, Some(self.target), &tarball); + } else { + builder.info( + &format!("skipping llvm-bitcode-linker stage{} ({})", self.compiler.stage, self.target), + ); + } + }; ); #[derive(Debug, Clone, Hash, PartialEq, Eq)] diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 701bd585eee76..d4473e240399f 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -42,14 +42,28 @@ pub struct Meta { root: String, } -// Linker flags to pass to LLVM's CMake invocation. +pub enum LlvmBuildStatus { + AlreadyBuilt(LlvmResult), + ShouldBuild(Meta), +} + +impl LlvmBuildStatus { + pub fn should_build(&self) -> bool { + match self { + LlvmBuildStatus::AlreadyBuilt(_) => false, + LlvmBuildStatus::ShouldBuild(_) => true, + } + } +} + +/// Linker flags to pass to LLVM's CMake invocation. #[derive(Debug, Clone, Default)] struct LdFlags { - // CMAKE_EXE_LINKER_FLAGS + /// CMAKE_EXE_LINKER_FLAGS exe: OsString, - // CMAKE_SHARED_LINKER_FLAGS + /// CMAKE_SHARED_LINKER_FLAGS shared: OsString, - // CMAKE_MODULE_LINKER_FLAGS + /// CMAKE_MODULE_LINKER_FLAGS module: OsString, } @@ -72,10 +86,10 @@ impl LdFlags { /// /// This will return the llvm-config if it can get it (but it will not build it /// if not). -pub fn prebuilt_llvm_config( - builder: &Builder<'_>, - target: TargetSelection, -) -> Result { +pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> LlvmBuildStatus { + // If we have llvm submodule initialized already, sync it. + builder.update_existing_submodule(&Path::new("src").join("llvm-project")); + builder.config.maybe_download_ci_llvm(); // If we're using a custom LLVM bail out here, but we can only use a @@ -90,10 +104,13 @@ pub fn prebuilt_llvm_config( llvm_cmake_dir.push("lib"); llvm_cmake_dir.push("cmake"); llvm_cmake_dir.push("llvm"); - return Ok(LlvmResult { llvm_config, llvm_cmake_dir }); + return LlvmBuildStatus::AlreadyBuilt(LlvmResult { llvm_config, llvm_cmake_dir }); } } + // Initialize the llvm submodule if not initialized already. + builder.update_submodule(&Path::new("src").join("llvm-project")); + let root = "src/llvm-project/llvm"; let out_dir = builder.llvm_out(target); @@ -125,10 +142,10 @@ pub fn prebuilt_llvm_config( stamp.path.display() )); } - return Ok(res); + return LlvmBuildStatus::AlreadyBuilt(res); } - Err(Meta { stamp, res, out_dir, root: root.into() }) + LlvmBuildStatus::ShouldBuild(Meta { stamp, res, out_dir, root: root.into() }) } /// This retrieves the LLVM sha we *want* to use, according to git history. @@ -274,12 +291,12 @@ impl Step for Llvm { target.to_string() }; + // If LLVM has already been built or been downloaded through download-ci-llvm, we avoid building it again. let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) { - Ok(p) => return p, - Err(m) => m, + LlvmBuildStatus::AlreadyBuilt(p) => return p, + LlvmBuildStatus::ShouldBuild(m) => m, }; - builder.update_submodule(&Path::new("src").join("llvm-project")); if builder.llvm_link_shared() && target.is_windows() { panic!("shared linking to LLVM is not currently supported on {}", target.triple); } @@ -364,10 +381,11 @@ impl Step for Llvm { cfg.define("LLVM_ENABLE_ZLIB", "OFF"); } - // Are we compiling for iOS/tvOS/watchOS? + // Are we compiling for iOS/tvOS/watchOS/visionOS? if target.contains("apple-ios") || target.contains("apple-tvos") || target.contains("apple-watchos") + || target.contains("apple-visionos") { // These two defines prevent CMake from automatically trying to add a MacOSX sysroot, which leads to a compiler error. cfg.define("CMAKE_OSX_SYSROOT", "/"); @@ -564,11 +582,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = output(cmd.arg("--version")); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { - if major >= 16 { + if major >= 17 { return; } } - panic!("\n\nbad LLVM version: {version}, need >=16.0\n\n") + panic!("\n\nbad LLVM version: {version}, need >=17.0\n\n") } fn configure_cmake( diff --git a/src/bootstrap/src/core/build_steps/mod.rs b/src/bootstrap/src/core/build_steps/mod.rs index 50d83789be82a..381ee7ef53b4c 100644 --- a/src/bootstrap/src/core/build_steps/mod.rs +++ b/src/bootstrap/src/core/build_steps/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod check; pub(crate) mod clean; +pub(crate) mod clippy; pub(crate) mod compile; pub(crate) mod dist; pub(crate) mod doc; @@ -13,3 +14,4 @@ pub(crate) mod synthetic_targets; pub(crate) mod test; pub(crate) mod tool; pub(crate) mod toolstate; +pub(crate) mod vendor; diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 61ee2fc1f6f3d..354eb2b600381 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -1,3 +1,8 @@ +//! Build-and-run steps for in-repo tools +//! +//! A bit of a hodge-podge as e.g. if a tool's a test fixture it should be in `build_steps::test`. +//! If it can be reached from `./x.py run` it can go here. + use std::path::PathBuf; use std::process::Command; @@ -10,32 +15,6 @@ use crate::core::config::TargetSelection; use crate::utils::helpers::output; use crate::Mode; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ExpandYamlAnchors; - -impl Step for ExpandYamlAnchors { - type Output = (); - - /// Runs the `expand-yaml_anchors` tool. - /// - /// This tool in `src/tools` reads the CI configuration files written in YAML and expands the - /// anchors in them, since GitHub Actions doesn't support them. - fn run(self, builder: &Builder<'_>) { - builder.info("Expanding YAML anchors in the GitHub Actions configuration"); - builder.run_delaying_failure( - builder.tool_cmd(Tool::ExpandYamlAnchors).arg("generate").arg(&builder.src), - ); - } - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/expand-yaml-anchors") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(ExpandYamlAnchors); - } -} - #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct BuildManifest; @@ -121,8 +100,6 @@ impl Step for ReplaceVersionPlaceholder { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - stage: u32, - host: TargetSelection, target: TargetSelection, } @@ -135,29 +112,35 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); + run.builder.ensure(Miri { target: run.target }); } fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; + let host = builder.build.build; let target = self.target; - let compiler = builder.compiler(stage, host); - - let miri = - builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - let miri_sysroot = test::Miri::build_miri_sysroot(builder, compiler, &miri, target); + let stage = builder.top_stage; + if stage == 0 { + eprintln!("miri cannot be run at stage 0"); + std::process::exit(1); + } + + // This compiler runs on the host, we'll just use it for the target. + let target_compiler = builder.compiler(stage, host); + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage + // compilers, which isn't what we want. Rustdoc should be linked in the same way as the + // rustc compiler it's paired with, so it must be built with the previous stage compiler. + let host_compiler = builder.compiler(stage - 1, host); + + // Get a target sysroot for Miri. + let miri_sysroot = test::Miri::build_miri_sysroot(builder, target_compiler, target); // # Run miri. // Running it via `cargo run` as that figures out the right dylib path. // add_rustc_lib_path does not add the path that contains librustc_driver-<...>.so. let mut miri = tool::prepare_tool_cargo( builder, - compiler, + host_compiler, Mode::ToolRustc, host, "run", diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 7bc68b5aec11f..df38d6166eb85 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -1,3 +1,10 @@ +//! First time setup of a dev environment +//! +//! These are build-and-run steps for `./x.py setup`, which allows quickly setting up the directory +//! for modifying, building, and running the compiler and library. Running arbitrary configuration +//! allows setting up things that cannot be simply captured inside the config.toml, in addition to +//! leading people away from manually editing most of the config.toml values. + use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::t; use crate::utils::change_tracker::CONFIG_CHANGE_HISTORY; @@ -25,6 +32,8 @@ pub enum Profile { None, } +static PROFILE_DIR: &str = "src/bootstrap/defaults"; + /// A list of historical hashes of `src/etc/rust_analyzer_settings.json`. /// New entries should be appended whenever this is updated so we can detect /// outdated vs. user-modified settings files. @@ -41,7 +50,7 @@ static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyze impl Profile { fn include_path(&self, src_path: &Path) -> PathBuf { - PathBuf::from(format!("{}/src/bootstrap/defaults/config.{}.toml", src_path.display(), self)) + PathBuf::from(format!("{}/{PROFILE_DIR}/config.{}.toml", src_path.display(), self)) } pub fn all() -> impl Iterator { @@ -93,10 +102,9 @@ impl FromStr for Profile { Ok(Profile::Tools) } "none" => Ok(Profile::None), - "llvm" | "codegen" => Err(format!( - "the \"llvm\" and \"codegen\" profiles have been removed,\ + "llvm" | "codegen" => Err("the \"llvm\" and \"codegen\" profiles have been removed,\ use \"compiler\" instead which has the same functionality" - )), + .to_string()), _ => Err(format!("unknown profile: '{s}'")), } } @@ -221,7 +229,7 @@ fn setup_config_toml(path: &PathBuf, profile: Profile, config: &Config) { let latest_change_id = CONFIG_CHANGE_HISTORY.last().unwrap().change_id; let settings = format!( - "# Includes one of the default files in src/bootstrap/defaults\n\ + "# Includes one of the default files in {PROFILE_DIR}\n\ profile = \"{profile}\"\n\ change-id = {latest_change_id}\n" ); @@ -474,10 +482,10 @@ impl Step for Hook { // install a git hook to automatically run tidy, if they want fn install_git_hook_maybe(config: &Config) -> io::Result<()> { - let git = t!(config.git().args(["rev-parse", "--git-common-dir"]).output().map(|output| { + let git = config.git().args(["rev-parse", "--git-common-dir"]).output().map(|output| { assert!(output.status.success(), "failed to run `git`"); PathBuf::from(t!(String::from_utf8(output.stdout)).trim()) - })); + })?; let hooks_dir = git.join("hooks"); let dst = hooks_dir.join("pre-push"); if dst.exists() { diff --git a/src/bootstrap/src/core/build_steps/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs index c057fa9a5667b..754d1e61da8c6 100644 --- a/src/bootstrap/src/core/build_steps/suggest.rs +++ b/src/bootstrap/src/core/build_steps/suggest.rs @@ -1,3 +1,5 @@ +//! Attempt to magically identify good tests to run + #![cfg_attr(feature = "build-metrics", allow(unused))] use clap::Parser; diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 690e20fb6b978..9a585a39e1eeb 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1,7 +1,7 @@ -//! Implementation of the test-related targets of the build system. +//! Build-and-run steps for `./x.py test` test fixtures //! -//! This file implements the various regression test suites that we execute on -//! our CI. +//! `./x.py test` (aka [`Kind::Test`]) is currently allowed to reach build steps in other modules. +//! However, this contains ~all test parts we expect people to be able to build and run locally. use std::env; use std::ffi::OsStr; @@ -493,8 +493,6 @@ impl Step for RustDemangler { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Miri { - stage: u32, - host: TargetSelection, target: TargetSelection, } @@ -503,41 +501,26 @@ impl Miri { pub fn build_miri_sysroot( builder: &Builder<'_>, compiler: Compiler, - miri: &Path, target: TargetSelection, ) -> String { let miri_sysroot = builder.out.join(compiler.host.triple).join("miri-sysroot"); - let mut cargo = tool::prepare_tool_cargo( + let mut cargo = builder::Cargo::new( builder, compiler, - Mode::ToolRustc, - compiler.host, - "run", - "src/tools/miri/cargo-miri", - SourceType::InTree, - &[], + Mode::Std, + SourceType::Submodule, + target, + "miri-setup", ); - cargo.add_rustc_lib_path(builder); - cargo.arg("--").arg("miri").arg("setup"); - cargo.arg("--target").arg(target.rustc_target_arg()); // Tell `cargo miri setup` where to find the sources. cargo.env("MIRI_LIB_SRC", builder.src.join("library")); - // Tell it where to find Miri. - cargo.env("MIRI", miri); // Tell it where to put the sysroot. cargo.env("MIRI_SYSROOT", &miri_sysroot); - // Debug things. - cargo.env("RUST_BACKTRACE", "1"); let mut cargo = Command::from(cargo); - let _guard = builder.msg( - Kind::Build, - compiler.stage + 1, - "miri sysroot", - compiler.host, - compiler.host, - ); + let _guard = + builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target); builder.run(&mut cargo); // # Determine where Miri put its sysroot. @@ -574,41 +557,51 @@ impl Step for Miri { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Miri { - stage: run.builder.top_stage, - host: run.build_triple(), - target: run.target, - }); + run.builder.ensure(Miri { target: run.target }); } /// Runs `cargo test` for miri. fn run(self, builder: &Builder<'_>) { - let stage = self.stage; - let host = self.host; + let host = builder.build.build; let target = self.target; - let compiler = builder.compiler(stage, host); - // We need the stdlib for the *next* stage, as it was built with this compiler that also built Miri. - // Except if we are at stage 2, the bootstrap loop is complete and we can stick with our current stage. - let compiler_std = builder.compiler(if stage < 2 { stage + 1 } else { stage }, host); - - let miri = - builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - let _cargo_miri = builder.ensure(tool::CargoMiri { - compiler, - target: self.host, + let stage = builder.top_stage; + if stage == 0 { + eprintln!("miri cannot be tested at stage 0"); + std::process::exit(1); + } + + // This compiler runs on the host, we'll just use it for the target. + let target_compiler = builder.compiler(stage, host); + // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise + // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage + // compilers, which isn't what we want. Rustdoc should be linked in the same way as the + // rustc compiler it's paired with, so it must be built with the previous stage compiler. + let host_compiler = builder.compiler(stage - 1, host); + + // Build our tools. + let miri = builder.ensure(tool::Miri { + compiler: host_compiler, + target: host, + extra_features: Vec::new(), + }); + // the ui tests also assume cargo-miri has been built + builder.ensure(tool::CargoMiri { + compiler: host_compiler, + target: host, extra_features: Vec::new(), }); - // The stdlib we need might be at a different stage. And just asking for the - // sysroot does not seem to populate it, so we do that first. - builder.ensure(compile::Std::new(compiler_std, host)); - let sysroot = builder.sysroot(compiler_std); - // We also need a Miri sysroot. - let miri_sysroot = Miri::build_miri_sysroot(builder, compiler, &miri, target); - - // # Run `cargo test`. + + // We also need sysroots, for Miri and for the host (the latter for build scripts). + // This is for the tests so everything is done with the target compiler. + let miri_sysroot = Miri::build_miri_sysroot(builder, target_compiler, target); + builder.ensure(compile::Std::new(target_compiler, host)); + let host_sysroot = builder.sysroot(target_compiler); + + // Run `cargo test`. + // This is with the Miri crate, so it uses the host compiler. let mut cargo = tool::prepare_tool_cargo( builder, - compiler, + host_compiler, Mode::ToolRustc, host, "test", @@ -616,26 +609,23 @@ impl Step for Miri { SourceType::InTree, &[], ); - let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "miri", host, target); cargo.add_rustc_lib_path(builder); + // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test + // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`. + let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host_compiler, host, builder); + // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); - cargo.env("MIRI_HOST_SYSROOT", &sysroot); + cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); cargo.env("MIRI", &miri); - if builder.config.locked_deps { - // enforce lockfiles - cargo.env("CARGO_EXTRA_FLAGS", "--locked"); - } // Set the target. cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); - // This can NOT be `run_cargo_test` since the Miri test runner - // does not understand the flags added by `add_flags_and_try_run_test`. - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); { + let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -650,48 +640,83 @@ impl Step for Miri { // Optimizations can change error locations and remove UB so don't run `fail` tests. cargo.args(["tests/pass", "tests/panic"]); - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", compiler, target, builder); { + let _guard = builder.msg_sysroot_tool( + Kind::Test, + stage, + "miri (mir-opt-level 4)", + host, + target, + ); let _time = helpers::timeit(builder); builder.run(&mut cargo); } } + } +} - // # Run `cargo miri test`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CargoMiri { + target: TargetSelection, +} + +impl Step for CargoMiri { + type Output = (); + const ONLY_HOSTS: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/miri/cargo-miri") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(CargoMiri { target: run.target }); + } + + /// Tests `cargo miri test`. + fn run(self, builder: &Builder<'_>) { + let host = builder.build.build; + let target = self.target; + let stage = builder.top_stage; + if stage == 0 { + eprintln!("cargo-miri cannot be tested at stage 0"); + std::process::exit(1); + } + + // This compiler runs on the host, we'll just use it for the target. + let compiler = builder.compiler(stage, host); + + // Run `cargo miri test`. // This is just a smoke test (Miri's own CI invokes this in a bunch of different ways and ensures // that we get the desired output), but that is sufficient to make sure that the libtest harness - // itself executes properly under Miri. + // itself executes properly under Miri, and that all the logic in `cargo-miri` does not explode. let mut cargo = tool::prepare_tool_cargo( builder, compiler, - Mode::ToolRustc, - host, - "run", - "src/tools/miri/cargo-miri", + Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test! + target, + "miri-test", + "src/tools/miri/test-cargo-miri", SourceType::Submodule, &[], ); - cargo.add_rustc_lib_path(builder); - cargo.arg("--").arg("miri").arg("test"); - if builder.config.locked_deps { - cargo.arg("--locked"); - } - cargo - .arg("--manifest-path") - .arg(builder.src.join("src/tools/miri/test-cargo-miri/Cargo.toml")); - cargo.arg("--target").arg(target.rustc_target_arg()); - cargo.arg("--tests"); // don't run doctests, they are too confused by the staging - cargo.arg("--").args(builder.config.test_args()); - // Tell `cargo miri` where to find things. - cargo.env("MIRI_SYSROOT", &miri_sysroot); - cargo.env("MIRI_HOST_SYSROOT", sysroot); - cargo.env("MIRI", &miri); - // Debug things. - cargo.env("RUST_BACKTRACE", "1"); + // We're not using `prepare_cargo_test` so we have to do this ourselves. + // (We're not using that as the test-cargo-miri crate is not known to bootstrap.) + match builder.doc_tests { + DocTests::Yes => {} + DocTests::No => { + cargo.args(["--lib", "--bins", "--examples", "--tests", "--benches"]); + } + DocTests::Only => { + cargo.arg("--doc"); + } + } + // Finally, pass test-args and run everything. + cargo.arg("--").args(builder.config.test_args()); let mut cargo = Command::from(cargo); { + let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target); let _time = helpers::timeit(builder); builder.run(&mut cargo); } @@ -898,7 +923,7 @@ impl Step for RustdocJSStd { builder.top_stage, self.target, builder, - DocumentationFormat::HTML, + DocumentationFormat::Html, )); let _guard = builder.msg( Kind::Test, @@ -1121,8 +1146,6 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to builder.info("tidy check"); builder.run_delaying_failure(&mut cmd); - builder.ensure(ExpandYamlAnchors); - builder.info("x.py completions check"); let [bash, zsh, fish, powershell] = ["x.py.sh", "x.py.zsh", "x.py.fish", "x.py.ps1"] .map(|filename| builder.src.join("src/etc/completions").join(filename)); @@ -1150,39 +1173,6 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ExpandYamlAnchors; - -impl Step for ExpandYamlAnchors { - type Output = (); - const ONLY_HOSTS: bool = true; - - /// Ensure the `generate-ci-config` tool was run locally. - /// - /// The tool in `src/tools` reads the CI definition in `src/ci/builders.yml` and generates the - /// appropriate configuration for all our CI providers. This step ensures the tool was called - /// by the user before committing CI changes. - fn run(self, builder: &Builder<'_>) { - // NOTE: `.github/` is not included in dist-src tarballs - if !builder.src.join(".github/workflows/ci.yml").exists() { - builder.info("Skipping YAML anchors check: GitHub Actions config not found"); - return; - } - builder.info("Ensuring the YAML anchors in the GitHub Actions config were expanded"); - builder.run_delaying_failure( - builder.tool_cmd(Tool::ExpandYamlAnchors).arg("check").arg(&builder.src), - ); - } - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/expand-yaml-anchors") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(ExpandYamlAnchors); - } -} - fn testdir(builder: &Builder<'_>, host: TargetSelection) -> PathBuf { builder.out.join(host.triple).join("test") } @@ -1289,9 +1279,9 @@ macro_rules! test_definitions { /// Adapted from [`test_definitions`]. macro_rules! coverage_test_alias { ($name:ident { - alias_and_mode: $alias_and_mode:expr, - default: $default:expr, - only_hosts: $only_hosts:expr $(,)? + alias_and_mode: $alias_and_mode:expr, // &'static str + default: $default:expr, // bool + only_hosts: $only_hosts:expr $(,)? // bool }) => { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name { @@ -1309,6 +1299,8 @@ macro_rules! coverage_test_alias { const ONLY_HOSTS: bool = $only_hosts; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // Register the mode name as a command-line alias. + // This allows `x test coverage-map` and `x test coverage-run`. run.alias($alias_and_mode) } @@ -1319,8 +1311,7 @@ macro_rules! coverage_test_alias { } fn run(self, builder: &Builder<'_>) { - Coverage { compiler: self.compiler, target: self.target } - .run_unified_suite(builder, Self::MODE) + Coverage::run_coverage_tests(builder, self.compiler, self.target, Self::MODE); } } }; @@ -1345,6 +1336,7 @@ impl Step for RunMakeSupport { run.builder.ensure(RunMakeSupport { compiler, target: run.build_triple() }); } + /// Builds run-make-support and returns the path to the resulting rlib. fn run(self, builder: &Builder<'_>) -> PathBuf { builder.ensure(compile::Std::new(self.compiler, self.target)); @@ -1363,17 +1355,65 @@ impl Step for RunMakeSupport { builder.run(&mut cargo); let lib_name = "librun_make_support.rlib"; - let lib = builder.tools_dir(self.compiler).join(&lib_name); + let lib = builder.tools_dir(self.compiler).join(lib_name); - let cargo_out = - builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(&lib_name); + let cargo_out = builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(lib_name); builder.copy_link(&cargo_out, &lib); lib } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CrateRunMakeSupport { + host: TargetSelection, +} + +impl Step for CrateRunMakeSupport { + type Output = (); + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/run-make-support") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(CrateRunMakeSupport { host: run.target }); + } + + /// Runs `cargo test` for run-make-support. + fn run(self, builder: &Builder<'_>) { + let host = self.host; + let compiler = builder.compiler(builder.top_stage, host); + + builder.ensure(compile::Std::new(compiler, host)); + let mut cargo = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolStd, + host, + "test", + "src/tools/run-make-support", + SourceType::InTree, + &[], + ); + cargo.allow_features("test"); + run_cargo_test( + cargo, + &[], + &[], + "run-make-support", + "run-make-support self test", + compiler, + host, + builder, + ); + } +} + default_test!(Ui { path: "tests/ui", mode: "ui", suite: "ui" }); +default_test!(Crashes { path: "tests/crashes", mode: "crashes", suite: "crashes" }); + default_test!(RunPassValgrind { path: "tests/run-pass-valgrind", mode: "run-pass-valgrind", @@ -1406,8 +1446,8 @@ host_test!(RustdocJson { path: "tests/rustdoc-json", mode: "rustdoc-json", suite host_test!(Pretty { path: "tests/pretty", mode: "pretty", suite: "pretty" }); -// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake` -// tests. +/// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake` +/// tests. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct RunMake { pub compiler: Compiler, @@ -1449,11 +1489,20 @@ host_test!(RunMakeFullDeps { default_test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly" }); -/// Custom test step that is responsible for running the coverage tests -/// in multiple different modes. +/// Coverage tests are a bit more complicated than other test suites, because +/// we want to run the same set of test files in multiple different modes, +/// in a way that's convenient and flexible when invoked manually. +/// +/// This combined step runs the specified tests (or all of `tests/coverage`) +/// in both "coverage-map" and "coverage-run" modes. /// -/// Each individual mode also has its own alias that will run the tests in -/// just that mode. +/// Used by: +/// - `x test coverage` +/// - `x test tests/coverage` +/// - `x test tests/coverage/trivial.rs` (etc) +/// +/// (Each individual mode also has its own step that will run the tests in +/// just that mode.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Coverage { pub compiler: Compiler, @@ -1464,24 +1513,41 @@ impl Coverage { const PATH: &'static str = "tests/coverage"; const SUITE: &'static str = "coverage"; - fn run_unified_suite(&self, builder: &Builder<'_>, mode: &'static str) { + /// Runs the coverage test suite (or a user-specified subset) in one mode. + /// + /// This same function is used by the multi-mode step ([`Coverage`]) and by + /// the single-mode steps ([`CoverageMap`] and [`CoverageRun`]), to help + /// ensure that they all behave consistently with each other, regardless of + /// how the coverage tests have been invoked. + fn run_coverage_tests( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, + mode: &'static str, + ) { + // Like many other test steps, we delegate to a `Compiletest` step to + // actually run the tests. (See `test_definitions!`.) builder.ensure(Compiletest { - compiler: self.compiler, - target: self.target, + compiler, + target, mode, suite: Self::SUITE, path: Self::PATH, compare_mode: None, - }) + }); } } impl Step for Coverage { type Output = (); + /// We rely on the individual CoverageMap/CoverageRun steps to run themselves. const DEFAULT: bool = false; + /// When manually invoked, try to run as much as possible. + /// Compiletest will automatically skip the "coverage-run" tests if necessary. const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // Take responsibility for command-line paths within `tests/coverage`. run.suite_path(Self::PATH) } @@ -1492,20 +1558,26 @@ impl Step for Coverage { } fn run(self, builder: &Builder<'_>) { - self.run_unified_suite(builder, CoverageMap::MODE); - self.run_unified_suite(builder, CoverageRun::MODE); + // Run the specified coverage tests (possibly all of them) in both modes. + Self::run_coverage_tests(builder, self.compiler, self.target, CoverageMap::MODE); + Self::run_coverage_tests(builder, self.compiler, self.target, CoverageRun::MODE); } } -// Aliases for running the coverage tests in only one mode. +// Runs `tests/coverage` in "coverage-map" mode only. +// Used by `x test` and `x test coverage-map`. coverage_test_alias!(CoverageMap { alias_and_mode: "coverage-map", default: true, only_hosts: false, }); +// Runs `tests/coverage` in "coverage-run" mode only. +// Used by `x test` and `x test coverage-run`. coverage_test_alias!(CoverageRun { alias_and_mode: "coverage-run", default: true, + // Compiletest knows how to automatically skip these tests when cross-compiling, + // but skipping the whole step here makes it clearer that they haven't run at all. only_hosts: true, }); @@ -1548,6 +1620,10 @@ impl Step for MirOpt { }) }; + run(self.target); + + // Run more targets with `--bless`. But we always run the host target first, since some + // tests use very specific `only` clauses that are not covered by the target set below. if builder.config.cmd.bless() { // All that we really need to do is cover all combinations of 32/64-bit and unwind/abort, // but while we're at it we might as well flex our cross-compilation support. This @@ -1566,8 +1642,6 @@ impl Step for MirOpt { }); run(panic_abort_target); } - } else { - run(self.target); } } } @@ -2421,6 +2495,7 @@ impl Step for CrateLibrustc { /// Given a `cargo test` subcommand, add the appropriate flags and run it. /// /// Returns whether the test succeeded. +#[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this. fn run_cargo_test<'a>( cargo: impl Into, libtest_args: &[&str], @@ -2507,9 +2582,14 @@ fn prepare_cargo_test( // // Note that to run the compiler we need to run with the *host* libraries, // but our wrapper scripts arrange for that to be the case anyway. - let mut dylib_path = dylib_path(); - dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target))); - cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); + // + // We skip everything on Miri as then this overwrites the libdir set up + // by `Cargo::new` and that actually makes things go wrong. + if builder.kind != Kind::Miri { + let mut dylib_path = dylib_path(); + dylib_path.insert(0, PathBuf::from(&*builder.sysroot_libdir(compiler, target))); + cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); + } if builder.remote_tested(target) { cargo.env( @@ -2565,9 +2645,9 @@ impl Step for Crate { let target = self.target; let mode = self.mode; + // Prepare sysroot // See [field@compile::Std::force_recompile]. - builder.ensure(compile::Std::force_recompile(compiler, target)); - builder.ensure(RemoteCopyLibs { compiler, target }); + builder.ensure(compile::Std::force_recompile(compiler, compiler.host)); // If we're not doing a full bootstrap but we're testing a stage2 // version of libstd, then what we're actually testing is the libstd @@ -2575,31 +2655,80 @@ impl Step for Crate { // we're working with automatically. let compiler = builder.compiler_for(compiler.stage, compiler.host, target); - let mut cargo = builder::Cargo::new( - builder, - compiler, - mode, - SourceType::InTree, - target, - builder.kind.as_str(), - ); + let mut cargo = if builder.kind == Kind::Miri { + if builder.top_stage == 0 { + eprintln!("ERROR: `x.py miri` requires stage 1 or higher"); + std::process::exit(1); + } + + // Build `cargo miri test` command + // (Implicitly prepares target sysroot) + let mut cargo = builder::Cargo::new( + builder, + compiler, + mode, + SourceType::InTree, + target, + "miri-test", + ); + // This hack helps bootstrap run standard library tests in Miri. The issue is as + // follows: when running `cargo miri test` on libcore, cargo builds a local copy of core + // and makes it a dependency of the integration test crate. This copy duplicates all the + // lang items, so the build fails. (Regular testing avoids this because the sysroot is a + // literal copy of what `cargo build` produces, but since Miri builds its own sysroot + // this does not work for us.) So we need to make it so that the locally built libcore + // contains all the items from `core`, but does not re-define them -- we want to replace + // the entire crate but a re-export of the sysroot crate. We do this by swapping out the + // source file: if `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a + // `lib.rs` file, and a `lib.miri.rs` file exists in the same folder, we build that + // instead. But crucially we only do that for the library, not the test builds. + cargo.env("MIRI_REPLACE_LIBRS_IF_NOT_TEST", "1"); + cargo + } else { + // Also prepare a sysroot for the target. + if builder.config.build != target { + builder.ensure(compile::Std::force_recompile(compiler, target)); + builder.ensure(RemoteCopyLibs { compiler, target }); + } + + // Build `cargo test` command + builder::Cargo::new( + builder, + compiler, + mode, + SourceType::InTree, + target, + builder.kind.as_str(), + ) + }; match mode { Mode::Std => { - compile::std_cargo(builder, target, compiler.stage, &mut cargo); - // `std_cargo` actually does the wrong thing: it passes `--sysroot build/host/stage2`, - // but we want to use the force-recompile std we just built in `build/host/stage2-test-sysroot`. - // Override it. - if builder.download_rustc() && compiler.stage > 0 { - let sysroot = builder - .out - .join(compiler.host.triple) - .join(format!("stage{}-test-sysroot", compiler.stage)); - cargo.env("RUSTC_SYSROOT", sysroot); + if builder.kind == Kind::Miri { + // We can't use `std_cargo` as that uses `optimized-compiler-builtins` which + // needs host tools for the given target. This is similar to what `compile::Std` + // does when `is_for_mir_opt_tests` is true. There's probably a chance for + // de-duplication here... `std_cargo` should support a mode that avoids needing + // host tools. + cargo + .arg("--manifest-path") + .arg(builder.src.join("library/sysroot/Cargo.toml")); + } else { + compile::std_cargo(builder, target, compiler.stage, &mut cargo); + // `std_cargo` actually does the wrong thing: it passes `--sysroot build/host/stage2`, + // but we want to use the force-recompile std we just built in `build/host/stage2-test-sysroot`. + // Override it. + if builder.download_rustc() && compiler.stage > 0 { + let sysroot = builder + .out + .join(compiler.host.triple) + .join(format!("stage{}-test-sysroot", compiler.stage)); + cargo.env("RUSTC_SYSROOT", sysroot); + } } } Mode::Rustc => { - compile::rustc_cargo(builder, &mut cargo, target, compiler.stage); + compile::rustc_cargo(builder, &mut cargo, target, &compiler); } _ => panic!("can only test libraries"), }; @@ -3186,6 +3315,11 @@ impl Step for CodegenCranelift { return; } + if builder.download_rustc() { + builder.info("CI rustc uses the default codegen backend. skipping"); + return; + } + if !target_supports_cranelift_backend(run.target) { builder.info("target not supported by rustc_codegen_cranelift. skipping"); return; @@ -3307,6 +3441,11 @@ impl Step for CodegenGCC { return; } + if builder.download_rustc() { + builder.info("CI rustc uses the default codegen backend. skipping"); + return; + } + let triple = run.target.triple; let target_supported = if triple.contains("linux") { triple.contains("x86_64") } else { false }; diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 3c20011210373..2e2c5e9e6f8b2 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -36,8 +36,9 @@ struct ToolBuild { impl Builder<'_> { #[track_caller] - fn msg_tool( + pub(crate) fn msg_tool( &self, + kind: Kind, mode: Mode, tool: &str, build_stage: u32, @@ -47,7 +48,7 @@ impl Builder<'_> { match mode { // depends on compiler stage, different to host compiler Mode::ToolRustc => self.msg_sysroot_tool( - Kind::Build, + kind, build_stage, format_args!("tool {tool}"), *host, @@ -100,6 +101,7 @@ impl Step for ToolBuild { cargo.allow_features(self.allow_features); } let _guard = builder.msg_tool( + Kind::Build, self.mode, self.tool, self.compiler.stage, @@ -133,6 +135,7 @@ impl Step for ToolBuild { } } +#[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this. pub fn prepare_tool_cargo( builder: &Builder<'_>, compiler: Compiler, @@ -299,7 +302,6 @@ bootstrap_tool!( RemoteTestClient, "src/tools/remote-test-client", "remote-test-client"; RustInstaller, "src/tools/rust-installer", "rust-installer"; RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes"; - ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors"; LintDocs, "src/tools/lint-docs", "lint-docs"; JsonDocCk, "src/tools/jsondocck", "jsondocck"; JsonDocLint, "src/tools/jsondoclint", "jsondoclint"; @@ -469,7 +471,7 @@ impl Step for Rustdoc { features.push("jemalloc".to_string()); } - let mut cargo = prepare_tool_cargo( + let cargo = prepare_tool_cargo( builder, build_compiler, Mode::ToolRustc, @@ -480,11 +482,8 @@ impl Step for Rustdoc { features.as_slice(), ); - if builder.config.rustc_parallel { - cargo.rustflag("--cfg=parallel_compiler"); - } - let _guard = builder.msg_tool( + Kind::Build, Mode::ToolRustc, "rustdoc", build_compiler.stage, @@ -546,7 +545,7 @@ impl Step for Cargo { } fn run(self, builder: &Builder<'_>) -> PathBuf { - let cargo_bin_path = builder.ensure(ToolBuild { + builder.ensure(ToolBuild { compiler: self.compiler, target: self.target, tool: "cargo", @@ -555,8 +554,7 @@ impl Step for Cargo { source_type: SourceType::Submodule, extra_features: Vec::new(), allow_features: "", - }); - cargo_bin_path + }) } } @@ -574,7 +572,7 @@ impl Step for LldWrapper { } fn run(self, builder: &Builder<'_>) -> PathBuf { - let src_exe = builder.ensure(ToolBuild { + builder.ensure(ToolBuild { compiler: self.compiler, target: self.target, tool: "lld-wrapper", @@ -583,9 +581,7 @@ impl Step for LldWrapper { source_type: SourceType::InTree, extra_features: Vec::new(), allow_features: "", - }); - - src_exe + }) } } @@ -692,6 +688,77 @@ impl Step for RustAnalyzerProcMacroSrv { } } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct LlvmBitcodeLinker { + pub compiler: Compiler, + pub target: TargetSelection, + pub extra_features: Vec, +} + +impl Step for LlvmBitcodeLinker { + type Output = PathBuf; + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let builder = run.builder; + run.path("src/tools/llvm-bitcode-linker").default_condition( + builder.config.extended + && builder + .config + .tools + .as_ref() + .map_or(builder.build.unstable_features(), |tools| { + tools.iter().any(|tool| tool == "llvm-bitcode-linker") + }), + ) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(LlvmBitcodeLinker { + compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.build), + extra_features: Vec::new(), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> PathBuf { + let bin_name = "llvm-bitcode-linker"; + + builder.ensure(compile::Std::new(self.compiler, self.compiler.host)); + builder.ensure(compile::Rustc::new(self.compiler, self.target)); + + let cargo = prepare_tool_cargo( + builder, + self.compiler, + Mode::ToolRustc, + self.target, + "build", + "src/tools/llvm-bitcode-linker", + SourceType::InTree, + &self.extra_features, + ); + + builder.run(&mut cargo.into()); + + let tool_out = builder + .cargo_out(self.compiler, Mode::ToolRustc, self.target) + .join(exe(bin_name, self.compiler.host)); + + if self.compiler.stage > 0 { + let bindir_self_contained = builder + .sysroot(self.compiler) + .join(format!("lib/rustlib/{}/bin/self-contained", self.target.triple)); + t!(fs::create_dir_all(&bindir_self_contained)); + let bin_destination = bindir_self_contained.join(exe(bin_name, self.compiler.host)); + builder.copy_link(&tool_out, &bin_destination); + bin_destination + } else { + tool_out + } + } +} + macro_rules! tool_extended { (($sel:ident, $builder:ident), $($name:ident, @@ -795,7 +862,6 @@ tool_extended!((self, builder), Rls, "src/tools/rls", "rls", stable=true, tool_std=true; RustDemangler, "src/tools/rust-demangler", "rust-demangler", stable=false, tool_std=true; Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"]; - LlvmBitcodeLinker, "src/tools/llvm-bitcode-linker", "llvm-bitcode-linker", stable=false, add_bins_to_sysroot = ["llvm-bitcode-linker"]; ); impl<'a> Builder<'a> { diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs index deb782cad0ce4..ca3756df4d779 100644 --- a/src/bootstrap/src/core/build_steps/toolstate.rs +++ b/src/bootstrap/src/core/build_steps/toolstate.rs @@ -1,3 +1,9 @@ +//! [Toolstate] checks to keep tools building +//! +//! Reachable via `./x.py test` but mostly relevant for CI, since it isn't run locally by default. +//! +//! [Toolstate]: https://forge.rust-lang.org/infra/toolstate.html + use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::utils::helpers::t; use serde_derive::{Deserialize, Serialize}; @@ -245,8 +251,12 @@ impl Builder<'_> { // Ensure the parent directory always exists t!(std::fs::create_dir_all(parent)); } - let mut file = - t!(fs::OpenOptions::new().create(true).write(true).read(true).open(path)); + let mut file = t!(fs::OpenOptions::new() + .create(true) + .truncate(false) + .write(true) + .read(true) + .open(path)); serde_json::from_reader(&mut file).unwrap_or_default() } else { @@ -278,8 +288,12 @@ impl Builder<'_> { // Ensure the parent directory always exists t!(std::fs::create_dir_all(parent)); } - let mut file = - t!(fs::OpenOptions::new().create(true).read(true).write(true).open(path)); + let mut file = t!(fs::OpenOptions::new() + .create(true) + .truncate(false) + .read(true) + .write(true) + .open(path)); let mut current_toolstates: HashMap, ToolState> = serde_json::from_reader(&mut file).unwrap_or_default(); diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs new file mode 100644 index 0000000000000..68f1b1bef3f39 --- /dev/null +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -0,0 +1,61 @@ +use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; +use std::path::PathBuf; +use std::process::Command; + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub(crate) struct Vendor { + sync_args: Vec, + versioned_dirs: bool, + root_dir: PathBuf, +} + +impl Step for Vendor { + type Output = (); + const DEFAULT: bool = true; + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("placeholder").default_condition(true) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(Vendor { + sync_args: run.builder.config.cmd.vendor_sync_args(), + versioned_dirs: run.builder.config.cmd.vendor_versioned_dirs(), + root_dir: run.builder.src.clone(), + }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let mut cmd = Command::new(&builder.initial_cargo); + cmd.arg("vendor"); + + if self.versioned_dirs { + cmd.arg("--versioned-dirs"); + } + + // Sync these paths by default. + for p in [ + "src/tools/cargo/Cargo.toml", + "src/tools/rust-analyzer/Cargo.toml", + "compiler/rustc_codegen_cranelift/Cargo.toml", + "compiler/rustc_codegen_gcc/Cargo.toml", + "src/bootstrap/Cargo.toml", + ] { + cmd.arg("--sync").arg(builder.src.join(p)); + } + + // Also sync explicitly requested paths. + for sync_arg in self.sync_args { + cmd.arg("--sync").arg(sync_arg); + } + + // Will read the libstd Cargo.toml + // which uses the unstable `public-dependency` feature. + cmd.env("RUSTC_BOOTSTRAP", "1"); + + cmd.current_dir(self.root_dir); + + builder.run(&mut cmd); + } +} diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 7f93fdc72ef08..0ad6c959309a2 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -13,9 +13,10 @@ use std::process::Command; use std::sync::OnceLock; use std::time::{Duration, Instant}; -use crate::core::build_steps::llvm; use crate::core::build_steps::tool::{self, SourceType}; -use crate::core::build_steps::{check, clean, compile, dist, doc, install, run, setup, test}; +use crate::core::build_steps::{ + check, clean, clippy, compile, dist, doc, install, llvm, run, setup, test, vendor, +}; use crate::core::config::flags::{Color, Subcommand}; use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection}; use crate::prepare_behaviour_dump_dir; @@ -34,13 +35,34 @@ use once_cell::sync::Lazy; #[cfg(test)] mod tests; +/// Builds and performs different [`Self::kind`]s of stuff and actions, taking +/// into account build configuration from e.g. config.toml. pub struct Builder<'a> { + /// Build configuration from e.g. config.toml. pub build: &'a Build, + + /// The stage to use. Either implicitly determined based on subcommand, or + /// explicitly specified with `--stage N`. Normally this is the stage we + /// use, but sometimes we want to run steps with a lower stage than this. pub top_stage: u32, + + /// What to build or what action to perform. pub kind: Kind, + + /// A cache of outputs of [`Step`]s so we can avoid running steps we already + /// ran. cache: Cache, + + /// A stack of [`Step`]s to run before we can run this builder. The output + /// of steps is cached in [`Self::cache`]. stack: RefCell>>, + + /// The total amount of time we spent running [`Step`]s in [`Self::stack`]. time_spent_on_dependencies: Cell, + + /// The paths passed on the command line. Used by steps to figure out what + /// to do. For example: with `./x check foo bar` we get `paths=["foo", + /// "bar"]`. pub paths: Vec, } @@ -132,8 +154,7 @@ impl RunConfig<'_> { Alias::Compiler => self.builder.in_tree_crates("rustc-main", Some(self.target)), }; - let crate_names = crates.into_iter().map(|krate| krate.name.to_string()).collect(); - crate_names + crates.into_iter().map(|krate| krate.name.to_string()).collect() } } @@ -323,16 +344,13 @@ const PATH_REMAP: &[(&str, &[&str])] = &[ fn remap_paths(paths: &mut Vec<&Path>) { let mut remove = vec![]; let mut add = vec![]; - for (i, path) in paths - .iter() - .enumerate() - .filter_map(|(i, path)| if let Some(s) = path.to_str() { Some((i, s)) } else { None }) + for (i, path) in paths.iter().enumerate().filter_map(|(i, path)| path.to_str().map(|s| (i, s))) { for &(search, replace) in PATH_REMAP { // Remove leading and trailing slashes so `tests/` and `tests` are equivalent if path.trim_matches(std::path::is_separator) == search { remove.push(i); - add.extend(replace.into_iter().map(Path::new)); + add.extend(replace.iter().map(Path::new)); break; } } @@ -554,29 +572,7 @@ impl<'a> ShouldRun<'a> { /// /// [`path`]: ShouldRun::path pub fn paths(mut self, paths: &[&str]) -> Self { - static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); - - let init_submodules_paths = |src: &PathBuf| { - let file = File::open(src.join(".gitmodules")).unwrap(); - - let mut submodules_paths = vec![]; - for line in BufReader::new(file).lines() { - if let Ok(line) = line { - let line = line.trim(); - - if line.starts_with("path") { - let actual_path = - line.split(' ').last().expect("Couldn't get value of path"); - submodules_paths.push(actual_path.to_owned()); - } - } - } - - submodules_paths - }; - - let submodules_paths = - SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.builder.src)); + let submodules_paths = self.builder.get_all_submodules(); self.paths.insert(PathSet::Set( paths @@ -653,6 +649,7 @@ pub enum Kind { Format, #[value(alias = "t")] Test, + Miri, Bench, #[value(alias = "d")] Doc, @@ -663,30 +660,10 @@ pub enum Kind { Run, Setup, Suggest, + Vendor, } impl Kind { - pub fn parse(string: &str) -> Option { - // these strings, including the one-letter aliases, must match the x.py help text - Some(match string { - "build" | "b" => Kind::Build, - "check" | "c" => Kind::Check, - "clippy" => Kind::Clippy, - "fix" => Kind::Fix, - "fmt" => Kind::Format, - "test" | "t" => Kind::Test, - "bench" => Kind::Bench, - "doc" | "d" => Kind::Doc, - "clean" => Kind::Clean, - "dist" => Kind::Dist, - "install" => Kind::Install, - "run" | "r" => Kind::Run, - "setup" => Kind::Setup, - "suggest" => Kind::Suggest, - _ => return None, - }) - } - pub fn as_str(&self) -> &'static str { match self { Kind::Build => "build", @@ -695,6 +672,7 @@ impl Kind { Kind::Fix => "fix", Kind::Format => "fmt", Kind::Test => "test", + Kind::Miri => "miri", Kind::Bench => "bench", Kind::Doc => "doc", Kind::Clean => "clean", @@ -703,6 +681,7 @@ impl Kind { Kind::Run => "run", Kind::Setup => "setup", Kind::Suggest => "suggest", + Kind::Vendor => "vendor", } } @@ -713,6 +692,7 @@ impl Kind { Kind::Doc => "Documenting", Kind::Run => "Running", Kind::Suggest => "Suggesting", + Kind::Clippy => "Linting", _ => { let title_letter = self.as_str()[0..1].to_ascii_uppercase(); return format!("{title_letter}{}ing", &self.as_str()[1..]); @@ -767,7 +747,35 @@ impl<'a> Builder<'a> { tool::CoverageDump, tool::LlvmBitcodeLinker ), - Kind::Check | Kind::Clippy | Kind::Fix => describe!( + Kind::Clippy => describe!( + clippy::Std, + clippy::Rustc, + clippy::Bootstrap, + clippy::BuildHelper, + clippy::BuildManifest, + clippy::CargoMiri, + clippy::Clippy, + clippy::CollectLicenseMetadata, + clippy::Compiletest, + clippy::CoverageDump, + clippy::Jsondocck, + clippy::Jsondoclint, + clippy::LintDocs, + clippy::LlvmBitcodeLinker, + clippy::Miri, + clippy::MiroptTestTools, + clippy::OptDist, + clippy::RemoteTestClient, + clippy::RemoteTestServer, + clippy::Rls, + clippy::RustAnalyzer, + clippy::RustDemangler, + clippy::Rustdoc, + clippy::Rustfmt, + clippy::RustInstaller, + clippy::Tidy, + ), + Kind::Check | Kind::Fix => describe!( check::Std, check::Rustc, check::Rustdoc, @@ -783,9 +791,9 @@ impl<'a> Builder<'a> { ), Kind::Test => describe!( crate::core::build_steps::toolstate::ToolStateCheck, - test::ExpandYamlAnchors, test::Tidy, test::Ui, + test::Crashes, test::RunPassValgrind, test::Coverage, test::CoverageMap, @@ -828,9 +836,11 @@ impl<'a> Builder<'a> { test::EditionGuide, test::Rustfmt, test::Miri, + test::CargoMiri, test::Clippy, test::RustDemangler, test::CompiletestTest, + test::CrateRunMakeSupport, test::RustdocJSStd, test::RustdocJSNotStd, test::RustdocGUI, @@ -844,6 +854,7 @@ impl<'a> Builder<'a> { // Run run-make last, since these won't pass without make on Windows test::RunMake, ), + Kind::Miri => describe!(test::Crate), Kind::Bench => describe!(test::Crate, test::CrateLibrustc), Kind::Doc => describe!( doc::UnstableBook, @@ -891,6 +902,7 @@ impl<'a> Builder<'a> { dist::Clippy, dist::Miri, dist::LlvmTools, + dist::LlvmBitcodeLinker, dist::RustDev, dist::Bootstrap, dist::Extended, @@ -920,7 +932,6 @@ impl<'a> Builder<'a> { install::Src, ), Kind::Run => describe!( - run::ExpandYamlAnchors, run::BuildManifest, run::BumpStage0, run::ReplaceVersionPlaceholder, @@ -932,6 +943,7 @@ impl<'a> Builder<'a> { ), Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode), Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), + Kind::Vendor => describe!(vendor::Vendor), // special-cased in Build::build() Kind::Format | Kind::Suggest => vec![], } @@ -992,6 +1004,7 @@ impl<'a> Builder<'a> { Subcommand::Fix => (Kind::Fix, &paths[..]), Subcommand::Doc { .. } => (Kind::Doc, &paths[..]), Subcommand::Test { .. } => (Kind::Test, &paths[..]), + Subcommand::Miri { .. } => (Kind::Miri, &paths[..]), Subcommand::Bench { .. } => (Kind::Bench, &paths[..]), Subcommand::Dist => (Kind::Dist, &paths[..]), Subcommand::Install => (Kind::Install, &paths[..]), @@ -1003,6 +1016,7 @@ impl<'a> Builder<'a> { Kind::Setup, path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)), ), + Subcommand::Vendor { .. } => (Kind::Vendor, &paths[..]), }; Self::new_internal(build, kind, paths.to_owned()) @@ -1031,10 +1045,10 @@ impl<'a> Builder<'a> { StepDescription::run(v, self, paths); } - /// Obtain a compiler at a given stage and for a given host. Explicitly does - /// not take `Compiler` since all `Compiler` instances are meant to be - /// obtained through this function, since it ensures that they are valid - /// (i.e., built and assembled). + /// Obtain a compiler at a given stage and for a given host (i.e., this is the target that the + /// compiler will run on, *not* the target it will build code for). Explicitly does not take + /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, + /// since it ensures that they are valid (i.e., built and assembled). pub fn compiler(&self, stage: u32, host: TargetSelection) -> Compiler { self.ensure(compile::Assemble { target_compiler: Compiler { stage, host } }) } @@ -1216,20 +1230,11 @@ impl<'a> Builder<'a> { } pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> Command { - let initial_sysroot_bin = self.initial_rustc.parent().unwrap(); - // Set PATH to include the sysroot bin dir so clippy can find cargo. - // FIXME: once rust-clippy#11944 lands on beta, set `CARGO` directly instead. - let path = t!(env::join_paths( - // The sysroot comes first in PATH to avoid using rustup's cargo. - std::iter::once(PathBuf::from(initial_sysroot_bin)) - .chain(env::split_paths(&t!(env::var("PATH")))) - )); - if run_compiler.stage == 0 { // `ensure(Clippy { stage: 0 })` *builds* clippy with stage0, it doesn't use the beta clippy. let cargo_clippy = self.build.config.download_clippy(); let mut cmd = Command::new(cargo_clippy); - cmd.env("PATH", &path); + cmd.env("CARGO", &self.initial_cargo); return cmd; } @@ -1249,7 +1254,38 @@ impl<'a> Builder<'a> { let mut cmd = Command::new(cargo_clippy); cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - cmd.env("PATH", path); + cmd.env("CARGO", &self.initial_cargo); + cmd + } + + pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> Command { + assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0"); + let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build); + + // Prepare the tools + let miri = self.ensure(tool::Miri { + compiler: build_compiler, + target: self.build.build, + extra_features: Vec::new(), + }); + let cargo_miri = self.ensure(tool::CargoMiri { + compiler: build_compiler, + target: self.build.build, + extra_features: Vec::new(), + }); + // Invoke cargo-miri, make sure it can find miri and cargo. + let mut cmd = Command::new(cargo_miri); + cmd.env("MIRI", &miri); + cmd.env("CARGO", &self.initial_cargo); + // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`, + // so they match the Miri we just built. However this means they are actually living one + // stage up, i.e. we are running `stage0-tools-bin/miri` with the libraries in `stage1/lib`. + // This is an unfortunate off-by-1 caused (possibly) by the fact that Miri doesn't have an + // "assemble" step like rustc does that would cross the stage boundary. We can't use + // `add_rustc_lib_path` as that's a NOP on Windows but we do need these libraries added to + // the PATH due to the stage mismatch. + // Also see https://github.com/rust-lang/rust/pull/123192#issuecomment-2028901503. + add_dylib_path(self.rustc_lib_paths(run_compiler), &mut cmd); cmd } @@ -1294,20 +1330,30 @@ impl<'a> Builder<'a> { compiler: Compiler, mode: Mode, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Command { - let mut cargo = if cmd == "clippy" { - self.cargo_clippy_cmd(compiler) + let mut cargo; + if cmd == "clippy" { + cargo = self.cargo_clippy_cmd(compiler); + cargo.arg(cmd); + } else if let Some(subcmd) = cmd.strip_prefix("miri") { + // Command must be "miri-X". + let subcmd = subcmd + .strip_prefix('-') + .unwrap_or_else(|| panic!("expected `miri-$subcommand`, but got {}", cmd)); + cargo = self.cargo_miri_cmd(compiler); + cargo.arg("miri").arg(subcmd); } else { - Command::new(&self.initial_cargo) - }; + cargo = Command::new(&self.initial_cargo); + cargo.arg(cmd); + } // Run cargo from the source root so it can find .cargo/config. // This matters when using vendoring and the working directory is outside the repository. cargo.current_dir(&self.src); let out_dir = self.stage_out(compiler, mode); - cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd); + cargo.env("CARGO_TARGET_DIR", &out_dir); // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` // from out of tree it shouldn't matter, since x.py is only used for @@ -1337,7 +1383,8 @@ impl<'a> Builder<'a> { if self.config.rust_optimize.is_release() { // FIXME: cargo bench/install do not accept `--release` - if cmd != "bench" && cmd != "install" { + // and miri doesn't want it + if cmd != "bench" && cmd != "install" && !cmd.starts_with("miri-") { cargo.arg("--release"); } } @@ -1353,14 +1400,15 @@ impl<'a> Builder<'a> { /// Cargo. This cargo will be configured to use `compiler` as the actual /// rustc compiler, its output will be scoped by `mode`'s output directory, /// it will pass the `--target` flag for the specified `target`, and will be - /// executing the Cargo command `cmd`. + /// executing the Cargo command `cmd`. `cmd` can be `miri-cmd` for commands + /// to be run with Miri. fn cargo( &self, compiler: Compiler, mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { let mut cargo = self.bare_cargo(compiler, mode, target, cmd); let out_dir = self.stage_out(compiler, mode); @@ -1411,7 +1459,7 @@ impl<'a> Builder<'a> { // rustc_llvm. But if LLVM is stale, that'll be a tiny amount // of work comparatively, and we'd likely need to rebuild it anyway, // so that's okay. - if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target).is_err() { + if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target).should_build() { cargo.env("RUST_CHECK", "1"); } } @@ -1513,7 +1561,7 @@ impl<'a> Builder<'a> { // `rustflags` without `cargo` making it required. rustflags.arg("-Zunstable-options"); for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { - if *restricted_mode == None || *restricted_mode == Some(mode) { + if restricted_mode.is_none() || *restricted_mode == Some(mode) { rustflags.arg(&check_cfg_arg(name, *values)); } } @@ -1671,7 +1719,8 @@ impl<'a> Builder<'a> { .env("RUSTDOC", self.bootstrap_out.join("rustdoc")) .env( "RUSTDOC_REAL", - if cmd == "doc" || cmd == "rustdoc" || (cmd == "test" && want_rustdoc) { + // Make sure to handle both `test` and `miri-test` commands. + if cmd == "doc" || cmd == "rustdoc" || (cmd.ends_with("test") && want_rustdoc) { self.rustdoc(compiler) } else { PathBuf::from("/path/to/nowhere/rustdoc/not/required") @@ -1694,6 +1743,15 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_WRAPPER_REAL", existing_wrapper); } + // If this is for `miri-test`, prepare the sysroots. + if cmd == "miri-test" { + self.ensure(compile::Std::new(compiler, compiler.host)); + let host_sysroot = self.sysroot(compiler); + let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target); + cargo.env("MIRI_SYSROOT", &miri_sysroot); + cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); + } + cargo.env(profile_var("STRIP"), self.config.rust_strip.to_string()); if let Some(stack_protector) = &self.config.rust_stack_protector { @@ -2065,15 +2123,27 @@ impl<'a> Builder<'a> { // break when incremental compilation is enabled. So this overrides the "no inlining // during incremental builds" heuristic for the standard library. rustflags.arg("-Zinline-mir"); + + // Similarly, we need to keep debug info for functions inlined into other std functions, + // even if we're not going to output debuginfo for the crate we're currently building, + // so that it'll be available when downstream consumers of std try to use it. + rustflags.arg("-Zinline-mir-preserve-debug"); } - // set rustc args passed from command line - let rustc_args = - self.config.cmd.rustc_args().iter().map(|s| s.to_string()).collect::>(); - if !rustc_args.is_empty() { - cargo.env("RUSTFLAGS", &rustc_args.join(" ")); + if self.config.rustc_parallel + && matches!(mode, Mode::ToolRustc | Mode::Rustc | Mode::Codegen) + { + // keep in sync with `bootstrap/lib.rs:Build::rustc_features` + // `cfg` option for rustc, `features` option for cargo, for conditional compilation + rustflags.arg("--cfg=parallel_compiler"); + rustdocflags.arg("--cfg=parallel_compiler"); } + // Pass the value of `--rustc-args` from test command. If it's not a test command, this won't set anything. + self.config.cmd.rustc_args().iter().for_each(|v| { + rustflags.arg(v); + }); + Cargo { command: cargo, compiler, @@ -2151,6 +2221,33 @@ impl<'a> Builder<'a> { out } + /// Return paths of all submodules managed by git. + /// If the current checkout is not managed by git, returns an empty slice. + pub fn get_all_submodules(&self) -> &[String] { + if !self.rust_info().is_managed_git_subrepository() { + return &[]; + } + + static SUBMODULES_PATHS: OnceLock> = OnceLock::new(); + + let init_submodules_paths = |src: &PathBuf| { + let file = File::open(src.join(".gitmodules")).unwrap(); + + let mut submodules_paths = vec![]; + for line in BufReader::new(file).lines().map_while(Result::ok) { + let line = line.trim(); + if line.starts_with("path") { + let actual_path = line.split(' ').last().expect("Couldn't get value of path"); + submodules_paths.push(actual_path.to_owned()); + } + } + + submodules_paths + }; + + SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src)) + } + /// Ensure that a given step is built *only if it's supposed to be built by default*, returning /// its output. This will cache the step, so it's safe (and good!) to call this as often as /// needed to ensure that all dependencies are build. @@ -2300,7 +2397,7 @@ impl Cargo { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd); cargo.configure_linker(builder); @@ -2314,7 +2411,7 @@ impl Cargo { mode: Mode, source_type: SourceType, target: TargetSelection, - cmd: &str, + cmd: &str, // FIXME make this properly typed ) -> Cargo { builder.cargo(compiler, mode, source_type, target, cmd) } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 7739303aca105..caec46366dde0 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::core::build_steps::doc::DocumentationFormat; -use crate::core::config::{Config, DryRun, TargetSelection}; +use crate::core::config::Config; use std::thread; fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { @@ -75,7 +75,7 @@ macro_rules! doc_std { $stage, TargetSelection::from_user(stringify!($target)), &builder, - DocumentationFormat::HTML, + DocumentationFormat::Html, ) }}; } @@ -599,7 +599,6 @@ mod dist { pass: None, run: None, only_modified: false, - skip: vec![], extra_checks: None, }; @@ -664,7 +663,6 @@ mod dist { no_fail_fast: false, doc: true, no_doc: false, - skip: vec![], bless: false, force_rerun: false, compare_mode: None, diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 3e1bc9a9acdd9..ed45bc30362a5 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -55,6 +55,7 @@ pub enum DryRun { pub enum DebuginfoLevel { #[default] None, + LineDirectivesOnly, LineTablesOnly, Limited, Full, @@ -70,16 +71,22 @@ impl<'de> Deserialize<'de> for DebuginfoLevel { use serde::de::Error; Ok(match Deserialize::deserialize(deserializer)? { - StringOrInt::String("none") | StringOrInt::Int(0) => DebuginfoLevel::None, - StringOrInt::String("line-tables-only") => DebuginfoLevel::LineTablesOnly, - StringOrInt::String("limited") | StringOrInt::Int(1) => DebuginfoLevel::Limited, - StringOrInt::String("full") | StringOrInt::Int(2) => DebuginfoLevel::Full, + StringOrInt::String(s) if s == "none" => DebuginfoLevel::None, + StringOrInt::Int(0) => DebuginfoLevel::None, + StringOrInt::String(s) if s == "line-directives-only" => { + DebuginfoLevel::LineDirectivesOnly + } + StringOrInt::String(s) if s == "line-tables-only" => DebuginfoLevel::LineTablesOnly, + StringOrInt::String(s) if s == "limited" => DebuginfoLevel::Limited, + StringOrInt::Int(1) => DebuginfoLevel::Limited, + StringOrInt::String(s) if s == "full" => DebuginfoLevel::Full, + StringOrInt::Int(2) => DebuginfoLevel::Full, StringOrInt::Int(n) => { let other = serde::de::Unexpected::Signed(n); return Err(D::Error::invalid_value(other, &"expected 0, 1, or 2")); } StringOrInt::String(s) => { - let other = serde::de::Unexpected::Str(s); + let other = serde::de::Unexpected::Str(&s); return Err(D::Error::invalid_value( other, &"expected none, line-tables-only, limited, or full", @@ -95,6 +102,7 @@ impl Display for DebuginfoLevel { use DebuginfoLevel::*; f.write_str(match self { None => "0", + LineDirectivesOnly => "line-directives-only", LineTablesOnly => "line-tables-only", Limited => "1", Full => "2", @@ -145,7 +153,6 @@ impl LldMode { /// `config.example.toml`. #[derive(Default, Clone)] pub struct Config { - pub changelog_seen: Option, // FIXME: Deprecated field. Remove it at 2024. pub change_id: Option, pub bypass_bootstrap_lock: bool, pub ccache: Option, @@ -338,6 +345,8 @@ pub struct Config { #[cfg(test)] pub initial_rustfmt: RefCell, + /// The paths to work with. For example: with `./x check foo bar` we get + /// `paths=["foo", "bar"]`. pub paths: Vec, } @@ -605,8 +614,8 @@ impl Target { #[derive(Deserialize, Default)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub(crate) struct TomlConfig { - changelog_seen: Option, // FIXME: Deprecated field. Remove it at 2024. - change_id: Option, + #[serde(flatten)] + change_id: ChangeIdWrapper, build: Option, install: Option, llvm: Option, @@ -616,6 +625,16 @@ pub(crate) struct TomlConfig { profile: Option, } +/// Since we use `#[serde(deny_unknown_fields)]` on `TomlConfig`, we need a wrapper type +/// for the "change-id" field to parse it even if other fields are invalid. This ensures +/// that if deserialization fails due to other fields, we can still provide the changelogs +/// to allow developers to potentially find the reason for the failure in the logs.. +#[derive(Deserialize, Default)] +pub(crate) struct ChangeIdWrapper { + #[serde(alias = "change-id")] + pub(crate) inner: Option, +} + /// Describes how to handle conflicts in merging two [`TomlConfig`] #[derive(Copy, Clone, Debug)] enum ReplaceOpt { @@ -634,17 +653,7 @@ trait Merge { impl Merge for TomlConfig { fn merge( &mut self, - TomlConfig { - build, - install, - llvm, - rust, - dist, - target, - profile: _, - changelog_seen, - change_id, - }: Self, + TomlConfig { build, install, llvm, rust, dist, target, profile: _, change_id }: Self, replace: ReplaceOpt, ) { fn do_merge(x: &mut Option, y: Option, replace: ReplaceOpt) { @@ -656,8 +665,7 @@ impl Merge for TomlConfig { } } } - self.changelog_seen.merge(changelog_seen, replace); - self.change_id.merge(change_id, replace); + self.change_id.inner.merge(change_id.inner, replace); do_merge(&mut self.build, build, replace); do_merge(&mut self.install, install, replace); do_merge(&mut self.llvm, llvm, replace); @@ -1023,8 +1031,8 @@ impl RustOptimize { #[derive(Deserialize)] #[serde(untagged)] -enum StringOrInt<'a> { - String(&'a str), +enum StringOrInt { + String(String), Int(i64), } @@ -1210,6 +1218,17 @@ impl Config { toml::from_str(&contents) .and_then(|table: toml::Value| TomlConfig::deserialize(table)) .unwrap_or_else(|err| { + if let Ok(Some(changes)) = toml::from_str(&contents) + .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table)).map(|change_id| change_id.inner.map(crate::find_recent_config_change_ids)) + { + if !changes.is_empty() { + println!( + "WARNING: There have been changes to x.py since you last updated:\n{}", + crate::human_readable_changes(&changes) + ); + } + } + eprintln!("failed to parse TOML configuration '{}': {err}", file.display()); exit!(2); }) @@ -1375,8 +1394,7 @@ impl Config { } toml.merge(override_toml, ReplaceOpt::Override); - config.changelog_seen = toml.changelog_seen; - config.change_id = toml.change_id; + config.change_id = toml.change_id.inner; let Build { build, @@ -1780,17 +1798,17 @@ impl Config { if let Some(v) = link_shared { config.llvm_link_shared.set(Some(v)); } - config.llvm_targets = targets.clone(); - config.llvm_experimental_targets = experimental_targets.clone(); + config.llvm_targets.clone_from(&targets); + config.llvm_experimental_targets.clone_from(&experimental_targets); config.llvm_link_jobs = link_jobs; - config.llvm_version_suffix = version_suffix.clone(); - config.llvm_clang_cl = clang_cl.clone(); + config.llvm_version_suffix.clone_from(&version_suffix); + config.llvm_clang_cl.clone_from(&clang_cl); - config.llvm_cflags = cflags.clone(); - config.llvm_cxxflags = cxxflags.clone(); - config.llvm_ldflags = ldflags.clone(); + config.llvm_cflags.clone_from(&cflags); + config.llvm_cxxflags.clone_from(&cxxflags); + config.llvm_ldflags.clone_from(&ldflags); set(&mut config.llvm_use_libcxx, use_libcxx); - config.llvm_use_linker = use_linker.clone(); + config.llvm_use_linker.clone_from(&use_linker); config.llvm_allow_old_toolchain = allow_old_toolchain.unwrap_or(false); config.llvm_polly = polly.unwrap_or(false); config.llvm_clang = clang.unwrap_or(false); @@ -1997,7 +2015,7 @@ impl Config { Subcommand::Build { .. } => { flags.stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 }) } - Subcommand::Test { .. } => { + Subcommand::Test { .. } | Subcommand::Miri { .. } => { flags.stage.or(test_stage).unwrap_or(if download_rustc { 2 } else { 1 }) } Subcommand::Bench { .. } => flags.stage.or(bench_stage).unwrap_or(2), @@ -2011,7 +2029,8 @@ impl Config { | Subcommand::Run { .. } | Subcommand::Setup { .. } | Subcommand::Format { .. } - | Subcommand::Suggest { .. } => flags.stage.unwrap_or(0), + | Subcommand::Suggest { .. } + | Subcommand::Vendor { .. } => flags.stage.unwrap_or(0), }; // CI should always run stage 2 builds, unless it specifically states otherwise @@ -2019,6 +2038,7 @@ impl Config { if flags.stage.is_none() && crate::CiEnv::current() != crate::CiEnv::None { match config.cmd { Subcommand::Test { .. } + | Subcommand::Miri { .. } | Subcommand::Doc { .. } | Subcommand::Build { .. } | Subcommand::Bench { .. } @@ -2037,7 +2057,8 @@ impl Config { | Subcommand::Run { .. } | Subcommand::Setup { .. } | Subcommand::Format { .. } - | Subcommand::Suggest { .. } => {} + | Subcommand::Suggest { .. } + | Subcommand::Vendor { .. } => {} } } @@ -2074,7 +2095,9 @@ impl Config { pub(crate) fn test_args(&self) -> Vec<&str> { let mut test_args = match self.cmd { - Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => { + Subcommand::Test { ref test_args, .. } + | Subcommand::Bench { ref test_args, .. } + | Subcommand::Miri { ref test_args, .. } => { test_args.iter().flat_map(|s| s.split_whitespace()).collect() } _ => vec![], @@ -2455,13 +2478,19 @@ impl Config { llvm::is_ci_llvm_available(self, asserts) } }; + match download_ci_llvm { - None => self.channel == "dev" && if_unchanged(), - Some(StringOrBool::Bool(b)) => 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" => { - llvm::is_ci_llvm_available(self, asserts) + 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 } Some(StringOrBool::String(s)) if s == "if-unchanged" => if_unchanged(), Some(StringOrBool::String(other)) => { diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 7262b785ee00a..f4ed7e76fba11 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -41,72 +41,72 @@ pub struct Flags { #[command(subcommand)] pub cmd: Subcommand, - #[arg(global(true), short, long, action = clap::ArgAction::Count)] + #[arg(global = true, short, long, action = clap::ArgAction::Count)] /// use verbose output (-vv for very verbose) pub verbose: u8, // each extra -v after the first is passed to Cargo - #[arg(global(true), short, long)] + #[arg(global = true, short, long)] /// use incremental compilation pub incremental: bool, - #[arg(global(true), long, value_hint = clap::ValueHint::FilePath, value_name = "FILE")] + #[arg(global = true, long, value_hint = clap::ValueHint::FilePath, value_name = "FILE")] /// TOML configuration file for build pub config: Option, - #[arg(global(true), long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")] + #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")] /// Build directory, overrides `build.build-dir` in `config.toml` pub build_dir: Option, - #[arg(global(true), long, value_hint = clap::ValueHint::Other, value_name = "BUILD")] + #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "BUILD")] /// build target of the stage0 compiler pub build: Option, - #[arg(global(true), long, value_hint = clap::ValueHint::Other, value_name = "HOST", value_parser = target_selection_list)] + #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "HOST", value_parser = target_selection_list)] /// host targets to build pub host: Option, - #[arg(global(true), long, value_hint = clap::ValueHint::Other, value_name = "TARGET", value_parser = target_selection_list)] + #[arg(global = true, long, value_hint = clap::ValueHint::Other, value_name = "TARGET", value_parser = target_selection_list)] /// target targets to build pub target: Option, - #[arg(global(true), long, value_name = "PATH")] + #[arg(global = true, long, value_name = "PATH")] /// build paths to exclude pub exclude: Vec, // keeping for client backward compatibility - #[arg(global(true), long, value_name = "PATH")] + #[arg(global = true, long, value_name = "PATH")] /// build paths to skip pub skip: Vec, - #[arg(global(true), long)] + #[arg(global = true, long)] /// include default paths in addition to the provided ones pub include_default_paths: bool, - #[arg(global(true), value_hint = clap::ValueHint::Other, long)] + #[arg(global = true, value_hint = clap::ValueHint::Other, long)] pub rustc_error_format: Option, - #[arg(global(true), long, value_hint = clap::ValueHint::CommandString, value_name = "CMD")] + #[arg(global = true, long, value_hint = clap::ValueHint::CommandString, value_name = "CMD")] /// command to run on failure pub on_fail: Option, - #[arg(global(true), long)] + #[arg(global = true, long)] /// dry run; don't build anything pub dry_run: bool, /// Indicates whether to dump the work done from bootstrap shims - #[arg(global(true), long)] + #[arg(global = true, long)] pub dump_bootstrap_shims: bool, - #[arg(global(true), value_hint = clap::ValueHint::Other, long, value_name = "N")] + #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")] /// stage to build (indicates compiler to use/test, e.g., stage 0 uses the /// bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.) pub stage: Option, - #[arg(global(true), value_hint = clap::ValueHint::Other, long, value_name = "N")] + #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")] /// stage(s) to keep without recompiling /// (pass multiple times to keep e.g., both stages 0 and 1) pub keep_stage: Vec, - #[arg(global(true), value_hint = clap::ValueHint::Other, long, value_name = "N")] + #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "N")] /// stage(s) of the standard library to keep without recompiling /// (pass multiple times to keep e.g., both stages 0 and 1) pub keep_stage_std: Vec, - #[arg(global(true), long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")] + #[arg(global = true, long, value_hint = clap::ValueHint::DirPath, value_name = "DIR")] /// path to the root of the rust checkout pub src: Option, #[arg( - global(true), + global = true, short, long, value_hint = clap::ValueHint::Other, @@ -117,26 +117,26 @@ pub struct Flags { pub jobs: usize, // This overrides the deny-warnings configuration option, // which passes -Dwarnings to the compiler invocations. - #[arg(global(true), long)] + #[arg(global = true, long)] #[arg(value_enum, default_value_t=Warnings::Default, value_name = "deny|warn")] /// if value is deny, will deny warnings /// if value is warn, will emit warnings /// otherwise, use the default configured behaviour pub warnings: Warnings, - #[arg(global(true), value_hint = clap::ValueHint::Other, long, value_name = "FORMAT")] + #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "FORMAT")] /// rustc error format pub error_format: Option, - #[arg(global(true), long)] + #[arg(global = true, long)] /// use message-format=json pub json_output: bool, - #[arg(global(true), long, value_name = "STYLE")] + #[arg(global = true, long, value_name = "STYLE")] #[arg(value_enum, default_value_t = Color::Auto)] /// whether to use color in cargo and rustc output pub color: Color, - #[arg(global(true), long)] + #[arg(global = true, long)] /// Bootstrap uses this value to decide whether it should bypass locking the build process. /// This is rarely needed (e.g., compiling the std library for different targets in parallel). /// @@ -144,41 +144,41 @@ pub struct Flags { pub bypass_bootstrap_lock: bool, /// whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml - #[arg(global(true), long, value_name = "VALUE")] + #[arg(global = true, long, value_name = "VALUE")] pub llvm_skip_rebuild: Option, /// generate PGO profile with rustc build - #[arg(global(true), value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] + #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] pub rust_profile_generate: Option, /// use PGO profile for rustc build - #[arg(global(true), value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] + #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] pub rust_profile_use: Option, /// use PGO profile for LLVM build - #[arg(global(true), value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] + #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] pub llvm_profile_use: Option, // LLVM doesn't support a custom location for generating profile // information. // // llvm_out/build/profiles/ is the location this writes to. /// generate PGO profile with llvm built for rustc - #[arg(global(true), long)] + #[arg(global = true, long)] pub llvm_profile_generate: bool, /// Enable BOLT link flags - #[arg(global(true), long)] + #[arg(global = true, long)] pub enable_bolt_settings: bool, /// Skip stage0 compiler validation - #[arg(global(true), long)] + #[arg(global = true, long)] pub skip_stage0_validation: bool, /// Additional reproducible artifacts that should be added to the reproducible artifacts archive. - #[arg(global(true), long)] + #[arg(global = true, long)] pub reproducible_artifact: Vec, - #[arg(global(true))] + #[arg(global = true)] /// paths for the subcommand pub paths: Vec, /// override options in config.toml - #[arg(global(true), value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")] + #[arg(global = true, value_hint = clap::ValueHint::Other, long, value_name = "section.option=value")] pub set: Vec, /// arguments passed to subcommands - #[arg(global(true), last(true), value_name = "ARGS")] + #[arg(global = true, last(true), value_name = "ARGS")] pub free_args: Vec, } @@ -192,7 +192,7 @@ impl Flags { struct HelpVerboseOnly { #[arg(short, long)] help: bool, - #[arg(global(true), short, long, action = clap::ArgAction::Count)] + #[arg(global = true, short, long, action = clap::ArgAction::Count)] pub verbose: u8, #[arg(value_enum)] cmd: Kind, @@ -260,16 +260,16 @@ pub enum Subcommand { #[arg(long, requires = "fix")] allow_staged: bool, /// clippy lints to allow - #[arg(global(true), short = 'A', action = clap::ArgAction::Append, value_name = "LINT")] + #[arg(global = true, short = 'A', action = clap::ArgAction::Append, value_name = "LINT")] allow: Vec, /// clippy lints to deny - #[arg(global(true), short = 'D', action = clap::ArgAction::Append, value_name = "LINT")] + #[arg(global = true, short = 'D', action = clap::ArgAction::Append, value_name = "LINT")] deny: Vec, /// clippy lints to warn on - #[arg(global(true), short = 'W', action = clap::ArgAction::Append, value_name = "LINT")] + #[arg(global = true, short = 'W', action = clap::ArgAction::Append, value_name = "LINT")] warn: Vec, /// clippy lints to forbid - #[arg(global(true), short = 'F', action = clap::ArgAction::Append, value_name = "LINT")] + #[arg(global = true, short = 'F', action = clap::ArgAction::Append, value_name = "LINT")] forbid: Vec, }, /// Run cargo fix @@ -339,9 +339,6 @@ pub enum Subcommand { #[arg(long)] /// run all tests regardless of failure no_fail_fast: bool, - #[arg(long, value_name = "SUBSTRING")] - /// skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times - skip: Vec, #[arg(long, value_name = "ARGS", allow_hyphen_values(true))] /// extra arguments to be passed for the test tool being used /// (e.g. libtest, compiletest or rustdoc) @@ -382,6 +379,25 @@ pub enum Subcommand { /// `//rustfix_missing_coverage.txt` rustfix_coverage: bool, }, + /// Build and run some test suites *in Miri* + Miri { + #[arg(long)] + /// run all tests regardless of failure + no_fail_fast: bool, + #[arg(long, value_name = "ARGS", allow_hyphen_values(true))] + /// extra arguments to be passed for the test tool being used + /// (e.g. libtest, compiletest or rustdoc) + test_args: Vec, + /// extra options to pass the compiler when running tests + #[arg(long, value_name = "ARGS", allow_hyphen_values(true))] + rustc_args: Vec, + #[arg(long)] + /// do not run doc tests + no_doc: bool, + #[arg(long)] + /// only run doc tests + doc: bool, + }, /// Build and run some benchmarks Bench { #[arg(long, allow_hyphen_values(true))] @@ -404,7 +420,7 @@ pub enum Subcommand { Arguments: This subcommand accepts a number of paths to tools to build and run. For example: - ./x.py run src/tools/expand-yaml-anchors + ./x.py run src/tools/bump-stage0 At least a tool needs to be called.")] /// Run tools contained in this repository Run { @@ -440,6 +456,15 @@ Arguments: #[arg(long)] run: bool, }, + /// Vendor dependencies + Vendor { + /// Additional `Cargo.toml` to sync and vendor + #[arg(long)] + sync: Vec, + /// Always include version in subdir name + #[arg(long)] + versioned_dirs: bool, + }, } impl Subcommand { @@ -453,18 +478,20 @@ impl Subcommand { Subcommand::Fix { .. } => Kind::Fix, Subcommand::Format { .. } => Kind::Format, Subcommand::Test { .. } => Kind::Test, + Subcommand::Miri { .. } => Kind::Miri, Subcommand::Clean { .. } => Kind::Clean, Subcommand::Dist { .. } => Kind::Dist, Subcommand::Install { .. } => Kind::Install, Subcommand::Run { .. } => Kind::Run, Subcommand::Setup { .. } => Kind::Setup, Subcommand::Suggest { .. } => Kind::Suggest, + Subcommand::Vendor { .. } => Kind::Vendor, } } pub fn rustc_args(&self) -> Vec<&str> { match *self { - Subcommand::Test { ref rustc_args, .. } => { + Subcommand::Test { ref rustc_args, .. } | Subcommand::Miri { ref rustc_args, .. } => { rustc_args.iter().flat_map(|s| s.split_whitespace()).collect() } _ => vec![], @@ -473,14 +500,16 @@ impl Subcommand { pub fn fail_fast(&self) -> bool { match *self { - Subcommand::Test { no_fail_fast, .. } => !no_fail_fast, + Subcommand::Test { no_fail_fast, .. } | Subcommand::Miri { no_fail_fast, .. } => { + !no_fail_fast + } _ => false, } } pub fn doc_tests(&self) -> DocTests { match *self { - Subcommand::Test { doc, no_doc, .. } => { + Subcommand::Test { doc, no_doc, .. } | Subcommand::Miri { no_doc, doc, .. } => { if doc { DocTests::Only } else if no_doc { @@ -562,6 +591,20 @@ impl Subcommand { _ => false, } } + + pub fn vendor_versioned_dirs(&self) -> bool { + match *self { + Subcommand::Vendor { versioned_dirs, .. } => versioned_dirs, + _ => false, + } + } + + pub fn vendor_sync_args(&self) -> Vec { + match self { + Subcommand::Vendor { sync, .. } => sync.clone(), + _ => vec![], + } + } } /// Returns the shell completion for a given shell, if the result differs from the current diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 99412848abbb7..23556e8bc5d38 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -1,4 +1,5 @@ -pub(crate) mod config; +#[allow(clippy::module_inception)] +mod config; pub(crate) mod flags; #[cfg(test)] mod tests; diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 6ac573c68df18..59e16b6542728 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -1,4 +1,5 @@ -use super::{flags::Flags, Config}; +use super::{flags::Flags, ChangeIdWrapper, Config}; +use crate::core::build_steps::clippy::get_clippy_rules_in_order; use crate::core::config::{LldMode, TomlConfig}; use clap::CommandFactory; @@ -11,12 +12,13 @@ use std::{ }; fn parse(config: &str) -> Config { - let config = format!("{config} \r\n build.rustc = \"/does-not-exists\" "); Config::parse_inner( &[ - "check".to_owned(), - "--config=/does/not/exist".to_owned(), - "--skip-stage0-validation".to_owned(), + "check".to_string(), + "--set=build.rustc=/does/not/exist".to_string(), + "--set=build.cargo=/does/not/exist".to_string(), + "--config=/does/not/exist".to_string(), + "--skip-stage0-validation".to_string(), ], |&_| toml::from_str(&config).unwrap(), ) @@ -169,7 +171,10 @@ fn override_toml_duplicate() { Config::parse_inner( &[ "check".to_owned(), + "--set=build.rustc=/does/not/exist".to_string(), + "--set=build.cargo=/does/not/exist".to_string(), "--config=/does/not/exist".to_owned(), + "--skip-stage0-validation".to_owned(), "--set=change-id=1".to_owned(), "--set=change-id=2".to_owned(), ], @@ -192,7 +197,15 @@ fn profile_user_dist() { .and_then(|table: toml::Value| TomlConfig::deserialize(table)) .unwrap() } - Config::parse_inner(&["check".to_owned()], get_toml); + Config::parse_inner( + &[ + "check".to_owned(), + "--set=build.rustc=/does/not/exist".to_string(), + "--set=build.cargo=/does/not/exist".to_string(), + "--skip-stage0-validation".to_string(), + ], + get_toml, + ); } #[test] @@ -237,3 +250,51 @@ fn rust_lld() { assert!(matches!(parse("rust.use-lld = true").lld_mode, LldMode::External)); assert!(matches!(parse("rust.use-lld = false").lld_mode, LldMode::Unused)); } + +#[test] +#[should_panic] +fn parse_config_with_unknown_field() { + parse("unknown-key = 1"); +} + +#[test] +fn parse_change_id_with_unknown_field() { + let config = r#" + change-id = 3461 + unknown-key = 1 + "#; + + let change_id_wrapper: ChangeIdWrapper = toml::from_str(config).unwrap(); + assert_eq!(change_id_wrapper.inner, Some(3461)); +} + +#[test] +fn order_of_clippy_rules() { + let args = vec![ + "clippy".to_string(), + "--fix".to_string(), + "--allow-dirty".to_string(), + "--allow-staged".to_string(), + "-Aclippy:all".to_string(), + "-Wclippy::style".to_string(), + "-Aclippy::foo1".to_string(), + "-Aclippy::foo2".to_string(), + ]; + let config = Config::parse(&args); + + let actual = match &config.cmd { + crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => { + get_clippy_rules_in_order(&args, &allow, &deny, &warn, &forbid) + } + _ => panic!("invalid subcommand"), + }; + + let expected = vec![ + "-Aclippy:all".to_string(), + "-Wclippy::style".to_string(), + "-Aclippy::foo1".to_string(), + "-Aclippy::foo2".to_string(), + ]; + + assert_eq!(expected, actual); +} diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 251138388cafd..75e0f646da699 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -1,6 +1,6 @@ use std::{ env, - ffi::{OsStr, OsString}, + ffi::OsString, fs::{self, File}, io::{BufRead, BufReader, BufWriter, ErrorKind, Write}, path::{Path, PathBuf}, @@ -183,7 +183,7 @@ impl Config { entries }; patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]); - if !fname.extension().map_or(false, |ext| ext == "so") { + if !path_is_dylib(fname) { // Finally, set the correct .interp for binaries let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); // FIXME: can we support utf8 here? `args` doesn't accept Vec, only OsString ... @@ -440,7 +440,7 @@ impl Config { let lib_dir = bin_root.join("lib"); for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { let lib = t!(lib); - if lib.path().extension() == Some(OsStr::new("so")) { + if path_is_dylib(&lib.path()) { self.fix_bin_or_dylib(&lib.path()); } } @@ -545,7 +545,7 @@ impl Config { let lib_dir = bin_root.join("lib"); for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { let lib = t!(lib); - if lib.path().extension() == Some(OsStr::new("so")) { + if path_is_dylib(&lib.path()) { self.fix_bin_or_dylib(&lib.path()); } } @@ -697,7 +697,7 @@ download-rustc = false let llvm_lib = llvm_root.join("lib"); for entry in t!(fs::read_dir(llvm_lib)) { let lib = t!(entry).path(); - if lib.extension().map_or(false, |ext| ext == "so") { + if path_is_dylib(&lib) { self.fix_bin_or_dylib(&lib); } } @@ -743,3 +743,8 @@ download-rustc = false self.unpack(&tarball, &llvm_root, "rust-dev"); } } + +fn path_is_dylib(path: &Path) -> bool { + // The .so is not necessarily the extension, it might be libLLVM.so.18.1 + path.to_str().map_or(false, |path| path.contains(".so")) +} diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index e03b1e179084e..6ccf29fb6cbef 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -14,6 +14,7 @@ use std::ffi::{OsStr, OsString}; use std::fs; use std::path::PathBuf; use std::process::Command; +use walkdir::WalkDir; use crate::builder::Kind; use crate::core::config::Target; @@ -177,6 +178,34 @@ than building it. continue; } + // Check if there exists a built-in target in the list of supported targets. + let mut has_target = false; + let target_str = target.to_string(); + + let supported_target_list = + output(Command::new(&build.config.initial_rustc).args(["--print", "target-list"])); + + has_target |= supported_target_list.contains(&target_str); + + // If not, check for a valid file location that may have been specified + // by the user for the custom target. + if let Some(custom_target_path) = env::var_os("RUST_TARGET_PATH") { + let mut target_os_str = OsString::from(&target_str); + target_os_str.push(".json"); + // Recursively traverse through nested directories. + let walker = WalkDir::new(custom_target_path).into_iter(); + for entry in walker.filter_map(|e| e.ok()) { + has_target |= entry.file_name() == target_os_str; + } + } + + if !has_target && !["A", "B", "C"].contains(&target_str.as_str()) { + panic!( + "No such target exists in the target list, + specify a correct location of the JSON specification file for custom targets!" + ); + } + if !build.config.dry_run() { cmd_finder.must_have(build.cc(*target)); if let Some(ar) = build.ar(*target) { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index f0fe994d155d0..c599709a32279 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -50,7 +50,9 @@ mod utils; pub use core::builder::PathSet; pub use core::config::flags::Subcommand; pub use core::config::Config; -pub use utils::change_tracker::{find_recent_config_change_ids, CONFIG_CHANGE_HISTORY}; +pub use utils::change_tracker::{ + find_recent_config_change_ids, human_readable_changes, CONFIG_CHANGE_HISTORY, +}; const LLVM_TOOLS: &[&str] = &[ "llvm-cov", // used to generate coverage report @@ -74,6 +76,7 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; /// Extra --check-cfg to add when building /// (Mode restriction, config name, config values (if any)) +#[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (None, "bootstrap", None), (Some(Mode::Rustc), "parallel_compiler", None), @@ -90,8 +93,9 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (Some(Mode::Std), "backtrace_in_libstd", None), /* Extra values not defined in the built-in targets yet, but used in std */ (Some(Mode::Std), "target_env", Some(&["libnx", "p2"])), - // (Some(Mode::Std), "target_os", Some(&[])), + (Some(Mode::Std), "target_os", Some(&["visionos"])), (Some(Mode::Std), "target_arch", Some(&["arm64ec", "spirv", "nvptx", "xtensa"])), + (Some(Mode::ToolStd), "target_os", Some(&["visionos"])), /* Extra names used by dependencies */ // FIXME: Used by serde_json, but we should not be triggering on external dependencies. (Some(Mode::Rustc), "no_btreemap_remove_entry", None), @@ -248,6 +252,8 @@ pub enum Mode { /// directory. This is for miscellaneous sets of tools that are built /// using the bootstrap stage0 compiler in its entirety (target libraries /// and all). Typically these tools compile with stable Rust. + /// + /// Only works for stage 0. ToolBootstrap, /// Build a tool which uses the locally built std, placing output in the @@ -370,11 +376,16 @@ impl Build { .expect("failed to read src/version"); let version = version.trim(); - let bootstrap_out = std::env::current_exe() + let mut bootstrap_out = std::env::current_exe() .expect("could not determine path to running process") .parent() .unwrap() .to_path_buf(); + // Since bootstrap is hardlink to deps/bootstrap-*, Solaris can sometimes give + // path with deps/ which is bad and needs to be avoided. + if bootstrap_out.ends_with("deps") { + bootstrap_out.pop(); + } if !bootstrap_out.join(exe("rustc", config.build)).exists() && !cfg!(test) { // this restriction can be lifted whenever https://github.com/rust-lang/rfcs/pull/3028 is implemented panic!( @@ -625,6 +636,18 @@ impl Build { } } + /// Updates the given submodule only if it's initialized already; nothing happens otherwise. + pub fn update_existing_submodule(&self, submodule: &Path) { + // Avoid running git when there isn't a git checkout. + if !self.config.submodules(self.rust_info()) { + return; + } + + if GitInfo::new(false, submodule).is_managed_git_subrepository() { + self.update_submodule(submodule); + } + } + /// Executes the entire build, as configured by the flags and configuration. pub fn build(&mut self) { unsafe { @@ -1007,15 +1030,23 @@ impl Build { let result = if !output.status.success() { if print_error { println!( - "\n\ncommand did not execute successfully: {:?}\n\ - expected success, got: {}\n\n\ - stdout ----\n{}\n\ - stderr ----\n{}\n\n", - command.command, + "\n\nCommand did not execute successfully.\ + \nExpected success, got: {}", output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) ); + + if !self.is_verbose() { + println!("Add `-v` to see more details.\n"); + } + + self.verbose(|| { + println!( + "\nSTDOUT ----\n{}\n\ + STDERR ----\n{}\n", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + }); } Err(()) } else { @@ -1065,6 +1096,16 @@ impl Build { } } + #[must_use = "Groups should not be dropped until the Step finishes running"] + #[track_caller] + fn msg_clippy( + &self, + what: impl Display, + target: impl Into>, + ) -> Option { + self.msg(Kind::Clippy, self.config.stage, what, self.config.build, target) + } + #[must_use = "Groups should not be dropped until the Step finishes running"] #[track_caller] fn msg_check( @@ -1338,9 +1379,24 @@ impl Build { self.musl_root(target).map(|root| root.join("lib")) } - /// Returns the sysroot for the wasi target, if defined - fn wasi_root(&self, target: TargetSelection) -> Option<&Path> { - self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p) + /// Returns the `lib` directory for the WASI target specified, if + /// configured. + /// + /// This first consults `wasi-root` as configured in per-target + /// configuration, and failing that it assumes that `$WASI_SDK_PATH` is + /// set in the environment, and failing that `None` is returned. + fn wasi_libdir(&self, target: TargetSelection) -> Option { + let configured = + self.config.target_config.get(&target).and_then(|t| t.wasi_root.as_ref()).map(|p| &**p); + if let Some(path) = configured { + return Some(path.join("lib").join(target.to_string())); + } + let mut env_root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?); + env_root.push("share"); + env_root.push("wasi-sysroot"); + env_root.push("lib"); + env_root.push(target.to_string()); + Some(env_root) } /// Returns `true` if this is a no-std `target`, if defined @@ -1387,6 +1443,13 @@ impl Build { if let Some(path) = finder.maybe_have("wasmtime") { if let Ok(mut path) = path.into_os_string().into_string() { path.push_str(" run -C cache=n --dir ."); + // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is + // required for libtest to work on beta/stable channels. + // + // NB: with Wasmtime 20 this can change to `-S inherit-env` to + // inherit the entire environment rather than just this single + // environment variable. + path.push_str(" --env RUSTC_BOOTSTRAP"); return Some(path); } } @@ -1568,10 +1631,7 @@ impl Build { /// Returns `true` if unstable features should be enabled for the compiler /// we're building. fn unstable_features(&self) -> bool { - match &self.config.channel[..] { - "stable" | "beta" => false, - "nightly" | _ => true, - } + !matches!(&self.config.channel[..], "stable" | "beta") } /// Returns a Vec of all the dependencies of the given root crate, @@ -1891,15 +1951,6 @@ impl Compiler { pub fn is_snapshot(&self, build: &Build) -> bool { self.stage == 0 && self.host == build.build } - - /// Returns if this compiler should be treated as a final stage one in the - /// current build session. - /// This takes into account whether we're performing a full bootstrap or - /// not; don't directly compare the stage with `2`! - pub fn is_final_stage(&self, build: &Build) -> bool { - let final_stage = if build.config.full_bootstrap { 2 } else { 1 }; - self.stage >= final_stage - } } fn envify(s: &str) -> String { diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 3ba4e0cb686e6..540b8671333a9 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -41,13 +41,11 @@ fn cc2ar(cc: &Path, target: TargetSelection) -> Option { Some(PathBuf::from(ar)) } else if target.is_msvc() { None - } else if target.contains("musl") { - Some(PathBuf::from("ar")) - } else if target.contains("openbsd") { + } else if target.contains("musl") || target.contains("openbsd") { Some(PathBuf::from("ar")) } else if target.contains("vxworks") { Some(PathBuf::from("wr-ar")) - } else if target.contains("android") { + } else if target.contains("android") || target.contains("-wasi") { Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar"))) } else { let parent = cc.parent().unwrap(); @@ -223,6 +221,16 @@ fn default_compiler( } } + t if t.contains("-wasi") => { + let root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?); + let compiler = match compiler { + Language::C => format!("{t}-clang"), + Language::CPlusPlus => format!("{t}-clang++"), + }; + let compiler = root.join("bin").join(compiler); + Some(compiler) + } + _ => None, } } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 14c1dc0730692..db3df598a0c63 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -60,6 +60,20 @@ pub fn find_recent_config_change_ids(current_id: usize) -> Vec { .collect() } +pub fn human_readable_changes(changes: &[ChangeInfo]) -> String { + let mut message = String::new(); + + for change in changes { + message.push_str(&format!(" [{}] {}\n", change.severity, change.summary)); + message.push_str(&format!( + " - PR Link https://github.com/rust-lang/rust/pull/{}\n", + change.change_id + )); + } + + message +} + /// Keeps track of major changes made to the bootstrap configuration. /// /// If you make any major changes (such as adding new values or changing default values), @@ -156,4 +170,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "`rust.split-debuginfo` has been moved to `target..split-debuginfo` and its default value is determined for each target individually.", }, + ChangeInfo { + change_id: 123711, + severity: ChangeSeverity::Warning, + summary: "The deprecated field `changelog-seen` has been removed. Using that field in `config.toml` from now on will result in breakage.", + }, ]; diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index a40ee18900182..928c9aa4dff44 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -549,7 +549,12 @@ pub fn hex_encode(input: T) -> String where T: AsRef<[u8]>, { - input.as_ref().iter().map(|x| format!("{:02x}", x)).collect() + use std::fmt::Write; + + input.as_ref().iter().fold(String::with_capacity(input.as_ref().len() * 2), |mut acc, &byte| { + write!(&mut acc, "{:02x}", byte).expect("Failed to write byte to the hex String."); + acc + }) } /// Create a `--check-cfg` argument invocation for a given name diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index c5c718a892f71..d7d20e57bd131 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -11,37 +11,35 @@ pub unsafe fn setup(build: &mut crate::Build) { } } +/// Job management on Windows for bootstrapping +/// +/// Most of the time when you're running a build system (e.g., make) you expect +/// Ctrl-C or abnormal termination to actually terminate the entire tree of +/// process in play, not just the one at the top. This currently works "by +/// default" on Unix platforms because Ctrl-C actually sends a signal to the +/// *process group* rather than the parent process, so everything will get torn +/// down. On Windows, however, this does not happen and Ctrl-C just kills the +/// parent process. +/// +/// To achieve the same semantics on Windows we use Job Objects to ensure that +/// all processes die at the same time. Job objects have a mode of operation +/// where when all handles to the object are closed it causes all child +/// processes associated with the object to be terminated immediately. +/// Conveniently whenever a process in the job object spawns a new process the +/// child will be associated with the job object as well. This means if we add +/// ourselves to the job object we create then everything will get torn down! +/// +/// Unfortunately most of the time the build system is actually called from a +/// python wrapper (which manages things like building the build system) so this +/// all doesn't quite cut it so far. To go the last mile we duplicate the job +/// object handle into our parent process (a python process probably) and then +/// close our own handle. This means that the only handle to the job object +/// resides in the parent python process, so when python dies the whole build +/// system dies (as one would probably expect!). +/// +/// Note that this is a Windows specific module as none of this logic is required on Unix. #[cfg(windows)] mod for_windows { - //! Job management on Windows for bootstrapping - //! - //! Most of the time when you're running a build system (e.g., make) you expect - //! Ctrl-C or abnormal termination to actually terminate the entire tree of - //! process in play, not just the one at the top. This currently works "by - //! default" on Unix platforms because Ctrl-C actually sends a signal to the - //! *process group* rather than the parent process, so everything will get torn - //! down. On Windows, however, this does not happen and Ctrl-C just kills the - //! parent process. - //! - //! To achieve the same semantics on Windows we use Job Objects to ensure that - //! all processes die at the same time. Job objects have a mode of operation - //! where when all handles to the object are closed it causes all child - //! processes associated with the object to be terminated immediately. - //! Conveniently whenever a process in the job object spawns a new process the - //! child will be associated with the job object as well. This means if we add - //! ourselves to the job object we create then everything will get torn down! - //! - //! Unfortunately most of the time the build system is actually called from a - //! python wrapper (which manages things like building the build system) so this - //! all doesn't quite cut it so far. To go the last mile we duplicate the job - //! object handle into our parent process (a python process probably) and then - //! close our own handle. This means that the only handle to the job object - //! resides in the parent python process, so when python dies the whole build - //! system dies (as one would probably expect!). - //! - //! Note that this module has a #[cfg(windows)] above it as none of this logic - //! is required on Unix. - use crate::Build; use std::env; use std::ffi::c_void; diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 70f25b2cc87c1..462b76d03bd25 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -231,14 +231,16 @@ impl<'a> Renderer<'a> { print!("\ntest result: "); self.builder.colored_stdout(|stdout| outcome.write_long(stdout)).unwrap(); println!( - ". {} passed; {} failed; {} ignored; {} measured; {} filtered out; \ - finished in {:.2?}\n", + ". {} passed; {} failed; {} ignored; {} measured; {} filtered out{time}\n", suite.passed, suite.failed, suite.ignored, suite.measured, suite.filtered_out, - Duration::from_secs_f64(suite.exec_time) + time = match suite.exec_time { + Some(t) => format!("; finished in {:.2?}", Duration::from_secs_f64(t)), + None => String::new(), + } ); } @@ -374,7 +376,9 @@ struct SuiteOutcome { ignored: usize, measured: usize, filtered_out: usize, - exec_time: f64, + /// The time it took to execute this test suite, or `None` if time measurement was not possible + /// (e.g. due to running on wasm). + exec_time: Option, } #[derive(serde_derive::Deserialize)] diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 4f99079a57f36..2a950e669b923 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -1,3 +1,10 @@ +//! Facilitates the management and generation of tarballs. +//! +//! Tarballs efficiently hold Rust compiler build artifacts and +//! capture a snapshot of each boostrap stage. +//! In uplifting, a tarball from Stage N captures essential components +//! to assemble Stage N + 1 compiler. + use std::{ path::{Path, PathBuf}, process::Command, @@ -11,22 +18,23 @@ use crate::utils::helpers::t; #[derive(Copy, Clone)] pub(crate) enum OverlayKind { Rust, - LLVM, + Llvm, Cargo, Clippy, Miri, Rustfmt, RustDemangler, - RLS, + Rls, RustAnalyzer, RustcCodegenCranelift, + LlvmBitcodeLinker, } impl OverlayKind { fn legal_and_readme(&self) -> &[&str] { match self { OverlayKind::Rust => &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"], - OverlayKind::LLVM => { + OverlayKind::Llvm => { &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"] } OverlayKind::Cargo => &[ @@ -53,7 +61,7 @@ impl OverlayKind { OverlayKind::RustDemangler => { &["src/tools/rust-demangler/README.md", "LICENSE-APACHE", "LICENSE-MIT"] } - OverlayKind::RLS => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"], + OverlayKind::Rls => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"], OverlayKind::RustAnalyzer => &[ "src/tools/rust-analyzer/README.md", "src/tools/rust-analyzer/LICENSE-APACHE", @@ -64,13 +72,19 @@ impl OverlayKind { "compiler/rustc_codegen_cranelift/LICENSE-APACHE", "compiler/rustc_codegen_cranelift/LICENSE-MIT", ], + OverlayKind::LlvmBitcodeLinker => &[ + "COPYRIGHT", + "LICENSE-APACHE", + "LICENSE-MIT", + "src/tools/llvm-bitcode-linker/README.md", + ], } } fn version(&self, builder: &Builder<'_>) -> String { match self { OverlayKind::Rust => builder.rust_version(), - OverlayKind::LLVM => builder.rust_version(), + OverlayKind::Llvm => builder.rust_version(), OverlayKind::RustDemangler => builder.release_num("rust-demangler"), OverlayKind::Cargo => { builder.cargo_info.version(builder, &builder.release_num("cargo")) @@ -82,11 +96,12 @@ impl OverlayKind { OverlayKind::Rustfmt => { builder.rustfmt_info.version(builder, &builder.release_num("rustfmt")) } - OverlayKind::RLS => builder.release(&builder.release_num("rls")), + OverlayKind::Rls => builder.release(&builder.release_num("rls")), OverlayKind::RustAnalyzer => builder .rust_analyzer_info .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")), OverlayKind::RustcCodegenCranelift => builder.rust_version(), + OverlayKind::LlvmBitcodeLinker => builder.rust_version(), } } } @@ -342,7 +357,7 @@ impl<'a> Tarball<'a> { &self.builder.config.dist_compression_profile }; - cmd.args(&["--compression-profile", compression_profile]); + cmd.args(["--compression-profile", compression_profile]); self.builder.run(&mut cmd); // Ensure there are no symbolic links in the tarball. In particular, diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 2e64568371306..9af368ef44504 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -14,7 +14,7 @@ for example: ./src/ci/docker/run.sh x86_64-gnu ``` -Images will output artifacts in an `obj` dir at the root of a repository. Note +Images will output artifacts in an `obj/$image_name` dir at the root of a repository. Note that the script will overwrite the contents of this directory. To match conditions in rusts CI, also set the environment variable `DEPLOY=1`, e.g.: @@ -22,12 +22,9 @@ To match conditions in rusts CI, also set the environment variable `DEPLOY=1`, e DEPLOY=1 ./src/ci/docker/run.sh x86_64-gnu ``` -**NOTE**: Re-using the same `obj` dir with different docker images with -the same target triple (e.g. `dist-x86_64-linux` and `dist-various-1`) -may result in strange linker errors, due shared library versions differing between platforms. - -If you encounter any issues when using multiple Docker images, try deleting your `obj` directory -before running your command. +**NOTE**: In CI, the script outputs the artifacts to the `obj` directory, +while locally, to the `obj/$image_name` directory. This is primarily to prevent +strange linker errors when using multiple Docker images. ## Filesystem layout diff --git a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile index 85b0f3b10818e..d5027589e0bd6 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile @@ -26,4 +26,6 @@ ENV RUST_CONFIGURE_ARGS \ --enable-sanitizers \ --enable-profiler \ --enable-compiler-docs -ENV SCRIPT python3 ../x.py --stage 2 test +# FIXME: Skipping cargo panic_abort_doc_tests due to https://github.com/rust-lang/rust/issues/123733 +ENV SCRIPT python3 ../x.py --stage 2 test && \ + python3 ../x.py --stage 2 test src/tools/cargo --test-args \"--skip panic_abort_doc_tests\" 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 09fbbac466c72..00552db4b0144 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -55,6 +55,9 @@ RUN ./install-riscv64-none-elf.sh COPY host-x86_64/dist-various-1/install-riscv32-none-elf.sh /build RUN ./install-riscv32-none-elf.sh +COPY host-x86_64/dist-various-1/install-llvm-mingw.sh /build +RUN ./install-llvm-mingw.sh + # Suppress some warnings in the openwrt toolchains we downloaded ENV STAGING_DIR=/tmp @@ -99,6 +102,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 @@ -110,6 +114,9 @@ ENV TARGETS=$TARGETS,armv7r-none-eabi ENV TARGETS=$TARGETS,armv7r-none-eabihf ENV TARGETS=$TARGETS,thumbv7neon-unknown-linux-gnueabihf ENV TARGETS=$TARGETS,armv7a-none-eabi +ENV TARGETS=$TARGETS,aarch64-pc-windows-gnullvm +ENV TARGETS=$TARGETS,i686-pc-windows-gnullvm +ENV TARGETS=$TARGETS,x86_64-pc-windows-gnullvm ENV CFLAGS_armv5te_unknown_linux_musleabi="-march=armv5te -marm -mfloat-abi=soft" \ CFLAGS_arm_unknown_linux_musleabi="-march=armv6 -marm" \ @@ -130,6 +137,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 \ @@ -139,7 +148,10 @@ ENV CFLAGS_armv5te_unknown_linux_musleabi="-march=armv5te -marm -mfloat-abi=soft CC_riscv64imac_unknown_none_elf=riscv64-unknown-elf-gcc \ CFLAGS_riscv64imac_unknown_none_elf=-march=rv64imac -mabi=lp64 \ CC_riscv64gc_unknown_none_elf=riscv64-unknown-elf-gcc \ - CFLAGS_riscv64gc_unknown_none_elf=-march=rv64gc -mabi=lp64 + CFLAGS_riscv64gc_unknown_none_elf=-march=rv64gc -mabi=lp64 \ + CC_aarch64_pc_windows_gnullvm=aarch64-w64-mingw32-clang \ + CC_i686_pc_windows_gnullvm=i686-w64-mingw32-clang \ + CC_x86_64_pc_windows_gnullvm=x86_64-w64-mingw32-clang ENV RUST_CONFIGURE_ARGS \ --musl-root-armv5te=/musl-armv5te \ diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh b/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh new file mode 100755 index 0000000000000..95471895fe771 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -ex + +release_date=20240404 +archive=llvm-mingw-${release_date}-ucrt-ubuntu-20.04-x86_64.tar.xz +curl -L https://github.com/mstorsjo/llvm-mingw/releases/download/${release_date}/${archive} | \ +tar --extract --lzma --strip 1 --directory /usr/local diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 9b15bb3530b61..bb6254942cbab 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -85,11 +85,9 @@ RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc sun COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -COPY host-x86_64/dist-various-2/build-wasi-toolchain.sh /tmp/ -RUN /tmp/build-wasi-toolchain.sh - -COPY host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh /tmp/ -RUN /tmp/build-wasi-threads-toolchain.sh +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-22/wasi-sdk-22.0-linux.tar.gz | \ + tar -xz +ENV WASI_SDK_PATH=/tmp/wasi-sdk-22.0 COPY scripts/freebsd-toolchain.sh /tmp/ RUN /tmp/freebsd-toolchain.sh i686 @@ -136,9 +134,6 @@ ENV TARGETS=$TARGETS,x86_64-unknown-uefi RUN ln -s /usr/include/x86_64-linux-gnu/asm /usr/local/include/asm ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-llvm-bitcode-linker --disable-docs \ - --set target.wasm32-wasi.wasi-root=/wasm32-wasip1 \ - --set target.wasm32-wasip1.wasi-root=/wasm32-wasip1 \ - --set target.wasm32-wasip1-threads.wasi-root=/wasm32-wasip1-threads \ --musl-root-armv7=/musl-armv7 ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh deleted file mode 100755 index 8f802eeaa8c0b..0000000000000 --- a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-threads-toolchain.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -set -ex - -# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz -curl https://ci-mirrors.rust-lang.org/rustc/2023-05-17-clang%2Bllvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz | \ - tar xJf - -bin="$PWD/clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04/bin" - -git clone https://github.com/WebAssembly/wasi-libc - -cd wasi-libc -git reset --hard ec4566beae84e54952637f0bf61bee4b4cacc087 -make -j$(nproc) \ - CC="$bin/clang" \ - NM="$bin/llvm-nm" \ - AR="$bin/llvm-ar" \ - THREAD_MODEL=posix \ - INSTALL_DIR=/wasm32-wasip1-threads \ - install - -cd .. -rm -rf wasi-libc -rm -rf clang+llvm* diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh deleted file mode 100755 index a2447494078d9..0000000000000 --- a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -set -ex - -# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz -curl https://ci-mirrors.rust-lang.org/rustc/2023-05-17-clang%2Bllvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04.tar.xz | \ - tar xJf - -bin="$PWD/clang+llvm-16.0.4-x86_64-linux-gnu-ubuntu-22.04/bin" - -git clone https://github.com/WebAssembly/wasi-libc - -cd wasi-libc -git reset --hard ec4566beae84e54952637f0bf61bee4b4cacc087 -make -j$(nproc) \ - CC="$bin/clang" \ - NM="$bin/llvm-nm" \ - AR="$bin/llvm-ar" \ - INSTALL_DIR=/wasm32-wasip1 \ - install - -cd .. -rm -rf wasi-libc -rm -rf clang+llvm* diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh index b22d60f2b1d10..5bc6f5cc2169b 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +GIT_REPO="https://github.com/rust-lang/gcc" + +# This commit hash needs to be updated to use a more recent gcc fork version. +GIT_COMMIT="78dc50f0e50e6cd1433149520bd512a4e0eaa1bc" + set -ex cd $1 @@ -7,13 +12,11 @@ cd $1 source shared.sh # Setting up folders for GCC -git clone https://github.com/antoyo/gcc gcc-src -cd gcc-src -# This commit hash needs to be updated to use a more recent gcc fork version. -git checkout 78dc50f0e50e6cd1433149520bd512a4e0eaa1bc +curl -L "$GIT_REPO/archive/$GIT_COMMIT.tar.gz" | + tar -xz --transform "s/gcc-$GIT_COMMIT/gcc-src/" -mkdir ../gcc-build ../gcc-install -cd ../gcc-build +mkdir gcc-build gcc-install +pushd gcc-build # Building GCC. hide_output \ @@ -28,6 +31,7 @@ hide_output \ hide_output make -j$(nproc) hide_output make install -rm -rf ../gcc-src +popd +rm -rf gcc-src gcc-build ln -s /scripts/gcc-install/lib/libgccjit.so /usr/lib/x86_64-linux-gnu/libgccjit.so ln -s /scripts/gcc-install/lib/libgccjit.so /usr/lib/x86_64-linux-gnu/libgccjit.so.0 diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index e5aa81d83d5a1..d0da7d3066013 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -21,10 +21,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* -RUN curl -sL https://nodejs.org/dist/v16.9.0/node-v16.9.0-linux-x64.tar.xz | tar -xJ -ENV PATH="/node-v16.9.0-linux-x64/bin:${PATH}" ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" +COPY scripts/nodejs.sh /scripts/ +RUN sh /scripts/nodejs.sh /node +ENV PATH="/node/bin:${PATH}" + # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. RUN npm install es-check@6.1.1 eslint@8.6.0 -g @@ -40,13 +42,13 @@ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 -ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ - # Check library crates on all tier 1 targets. - # We disable optimized compiler built-ins because that requires a C toolchain for the target. - # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs. - python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ +# Check library crates on all tier 1 targets. +# We disable optimized compiler built-ins because that requires a C toolchain for the target. +# We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs. +ENV SCRIPT python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ - python3 ../x.py clippy compiler -Aclippy::all -Dclippy::correctness && \ + python3 ../x.py clippy bootstrap -Dwarnings && \ + python3 ../x.py clippy compiler library -Aclippy::all -Dclippy::correctness && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py test --stage 0 core alloc std test proc_macro && \ @@ -60,7 +62,7 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ /scripts/validate-error-codes.sh && \ reuse --include-submodules lint && \ # Runs checks to ensure that there are no ES5 issues in our JS code. - es-check es6 ../src/librustdoc/html/static/js/*.js && \ + es-check es8 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 944d9aed3190b..f874b2ed475fd 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -28,8 +28,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ qemu-system-x86 \ && rm -rf /var/lib/apt/lists/* -RUN curl -sL https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-x64.tar.xz | \ - tar -xJ +COPY scripts/nodejs.sh /scripts/ +RUN sh /scripts/nodejs.sh /node +ENV PATH="/node/bin:${PATH}" WORKDIR /build/ COPY scripts/musl-toolchain.sh /build/ @@ -39,14 +40,13 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY host-x86_64/dist-various-2/build-wasi-toolchain.sh /tmp/ -RUN /tmp/build-wasi-toolchain.sh +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-22/wasi-sdk-22.0-linux.tar.gz | \ + tar -xz +ENV WASI_SDK_PATH=/wasi-sdk-22.0 ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ - --set build.nodejs=/node-v18.12.0-linux-x64/bin/node \ - --set rust.lld \ - --set target.wasm32-wasip1.wasi-root=/wasm32-wasip1 + --set rust.lld # Some run-make tests have assertions about code size, and enabling debug # assertions in libstd causes the binary to be much bigger than it would @@ -55,9 +55,9 @@ ENV RUST_CONFIGURE_ARGS \ ENV NO_DEBUG_ASSERTIONS=1 ENV NO_OVERFLOW_CHECKS=1 -RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v18.0.2/wasmtime-v18.0.2-x86_64-linux.tar.xz | \ +RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v19.0.0/wasmtime-v19.0.0-x86_64-linux.tar.xz | \ tar -xJ -ENV PATH "$PATH:/wasmtime-v18.0.2-x86_64-linux" +ENV PATH "$PATH:/wasmtime-v19.0.0-x86_64-linux" ENV WASM_TARGETS=wasm32-wasip1 ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_TARGETS \ @@ -68,9 +68,6 @@ ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_T tests/codegen \ tests/assembly \ library/core -ENV CC_wasm32_wasip1=clang-11 \ - CFLAGS_wasm32_wasip1="--sysroot /wasm32-wasip1" \ - AR_wasm32_wasip1=llvm-ar-11 ENV NVPTX_TARGETS=nvptx64-nvidia-cuda ENV NVPTX_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $NVPTX_TARGETS \ diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock index a579ea965f26f..b0c17d9a296c8 100644 --- a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "r-efi" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e244f96e03a3067f9e521d3167bd42657594cb8588c8d3a2db01545dc1af2e0" +checksum = "c47196f636c4cc0634b73b0405323d177753c2e15e866952c64ea22902567a34" [[package]] name = "uefi_qemu_test" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile index e08c4e1e8b7de..a74db2250fc67 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile @@ -25,5 +25,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +# Miri is just too slow with full assertions +ENV NO_DEBUG_ASSERTIONS=1 +ENV NO_OVERFLOW_CHECKS=1 + ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu ENV RUST_CHECK_TARGET check-aux diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh index d6de992913bf5..9cc508fe92881 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh @@ -5,7 +5,7 @@ set -euf -o pipefail -INTEGRATION_SHA=56310bca298872ffb5ea02e665956d9b6dc41171 +INTEGRATION_SHA=1011e3298775ee7cdf6f6dc73e808d6a86e33bd6 PICK_REFS=() checkout=fuchsia diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile deleted file mode 100644 index 4fc2b2e507e0d..0000000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/Dockerfile +++ /dev/null @@ -1,67 +0,0 @@ -FROM ubuntu:23.04 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-16-tools \ - llvm-16-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - mingw-w64 \ - # libgccjit dependencies - flex \ - libmpfr-dev \ - libgmp-dev \ - libmpc3 \ - libmpc-dev \ - && rm -rf /var/lib/apt/lists/* - -# Note: libgccjit needs to match the default gcc version for the linker to find it. - -# Install powershell (universal package) so we can test x.ps1 on Linux -RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ - dpkg -i powershell.deb && \ - rm -f powershell.deb - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 - -# This is not the latest LLVM version, so some components required by tests may -# be missing. -ENV IS_NOT_LATEST_LLVM 1 - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-16 \ - --enable-llvm-link-shared \ - --set rust.thin-lto-import-instr-limit=10 - -COPY host-x86_64/x86_64-gnu-llvm-16/script.sh /tmp/ - -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ - -RUN /scripts/build-gccjit.sh /scripts - -ENV SCRIPT /tmp/script.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile index 7c2ecd198e234..538962802c96f 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile @@ -56,11 +56,10 @@ ENV RUST_CONFIGURE_ARGS \ --enable-llvm-link-shared \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/x86_64-gnu-llvm-16/script.sh /tmp/ - COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts +COPY scripts/x86_64-gnu-llvm.sh /tmp/script.sh ENV SCRIPT /tmp/script.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile index e8383500dfc94..3476b10a3addc 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -24,11 +24,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ xz-utils \ nodejs \ mingw-w64 \ - libgccjit-13-dev \ + # libgccjit dependencies + flex \ + libmpfr-dev \ + libgmp-dev \ + libmpc3 \ + libmpc-dev \ && rm -rf /var/lib/apt/lists/* -# Note: libgccjit needs to match the default gcc version for the linker to find it. - # Install powershell (universal package) so we can test x.ps1 on Linux # FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ @@ -50,6 +53,10 @@ ENV RUST_CONFIGURE_ARGS \ --enable-llvm-link-shared \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/x86_64-gnu-llvm-16/script.sh /tmp/ +COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ +COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ + +RUN /scripts/build-gccjit.sh /scripts +COPY scripts/x86_64-gnu-llvm.sh /tmp/script.sh ENV SCRIPT /tmp/script.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 6f72056989818..6a09ab3065ffa 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -72,22 +72,12 @@ ENV GCC_EXEC_PREFIX="/usr/lib/gcc/" COPY host-x86_64/x86_64-gnu-tools/checktools.sh /tmp/ -RUN curl -sL https://nodejs.org/dist/v14.20.0/node-v14.20.0-linux-x64.tar.xz | tar -xJ -ENV NODE_FOLDER=/node-v14.20.0-linux-x64/bin -ENV PATH="$NODE_FOLDER:${PATH}" +COPY scripts/nodejs.sh /scripts/ +RUN sh /scripts/nodejs.sh /node +ENV PATH="/node/bin:${PATH}" COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/ -# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries -# to create a new folder. For reference: -# https://github.com/puppeteer/puppeteer/issues/375 -# -# We also specify the version in case we need to update it to go around cache limitations. -# -# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case -# the local version of the package is different than the one used by the CI. -RUN npm install -g browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true - ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --save-toolstates=/tmp/toolstate/toolstates.json \ @@ -100,6 +90,14 @@ COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts +# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries +# to create a new folder. For reference: +# https://github.com/puppeteer/puppeteer/issues/375 +# +# We also specify the version in case we need to update it to go around cache limitations. +# +# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case +# the local version of the package is different than the one used by the CI. ENV SCRIPT /tmp/checktools.sh ../x.py && \ - NODE_PATH=`npm root -g` python3 ../x.py test tests/rustdoc-gui --stage 2 \ - --test-args "'--no-sandbox --jobs 1'" + npm install browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true && \ + python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--no-sandbox --jobs 1'" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index a2b63962ba11e..50c2e5e29f0b8 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.16.11 \ No newline at end of file +0.17.2 \ No newline at end of file diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index f5c426a717f78..53b4583166d38 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -2,6 +2,7 @@ # ignore-tidy-linelength set -eu +set -x # so one can see where we are in the script X_PY="$1" @@ -31,9 +32,9 @@ python3 "$X_PY" test --stage 2 src/tools/rustfmt # that bugs which only surface when the GC runs at a specific time are more likely to cause CI to fail. # This significantly increases the runtime of our test suite, or we'd do this in PR CI too. if [ -z "${PR_CI_JOB:-}" ]; then - MIRIFLAGS=-Zmiri-provenance-gc=1 python3 "$X_PY" test --stage 2 src/tools/miri + MIRIFLAGS=-Zmiri-provenance-gc=1 python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri else - python3 "$X_PY" test --stage 2 src/tools/miri + python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri fi # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. @@ -41,8 +42,8 @@ case $HOST_TARGET in x86_64-unknown-linux-gnu) # Only this branch runs in PR CI. # Fully test all main OSes, including a 32bit target. - python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-apple-darwin - python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-msvc + python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target x86_64-apple-darwin + python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target i686-pc-windows-msvc # Only run "pass" tests for the remaining targets, which is quite a bit faster. python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass python3 "$X_PY" test --stage 2 src/tools/miri --target i686-unknown-linux-gnu --test-args pass @@ -61,3 +62,8 @@ case $HOST_TARGET in exit 1 ;; esac +# Also smoke-test `x.py miri`. This doesn't run any actual tests (that would take too long), +# but it ensures that the crates build properly when tested with Miri. +python3 "$X_PY" miri --stage 2 library/core --test-args notest +python3 "$X_PY" miri --stage 2 library/alloc --test-args notest +python3 "$X_PY" miri --stage 2 library/std --test-args notest diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 740eb7504f87b..fcc507b572c27 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -33,7 +33,13 @@ ci_dir="`dirname $script_dir`" src_dir="`dirname $ci_dir`" root_dir="`dirname $src_dir`" -objdir=$root_dir/obj +source "$ci_dir/shared.sh" + +if isCI; then + objdir=$root_dir/obj +else + objdir=$root_dir/obj/$image +fi dist=$objdir/build/dist @@ -41,12 +47,10 @@ if [ -d "$root_dir/.git" ]; then IS_GIT_SOURCE=1 fi -source "$ci_dir/shared.sh" - CACHE_DOMAIN="${CACHE_DOMAIN:-ci-caches.rust-lang.org}" if [ -f "$docker_dir/$image/Dockerfile" ]; then - if [ "$CI" != "" ]; then + if isCI; then hash_key=/tmp/.docker-hash-key.txt rm -f "${hash_key}" echo $image >> $hash_key @@ -102,7 +106,7 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then CACHE_IMAGE_TAG=${REGISTRY}/${REGISTRY_USERNAME}/rust-ci-cache:${cksum} # On non-CI jobs, we don't do any caching. - if [[ "$CI" == "" ]]; + if ! isCI; then retry docker build --rm -t rust-ci -f "$dockerfile" "$context" # On PR CI jobs, we don't have permissions to write to the registry cache, @@ -289,7 +293,7 @@ else command=(/checkout/src/ci/run.sh) fi -if [ "$CI" != "" ]; then +if isCI; then # Get some needed information for $BASE_COMMIT # # This command gets the last merge commit which we'll use as base to list @@ -323,6 +327,7 @@ docker \ --env GITHUB_ACTIONS \ --env GITHUB_REF \ --env GITHUB_STEP_SUMMARY="/checkout/obj/${SUMMARY_FILE}" \ + --env RUST_BACKTRACE \ --env TOOLSTATE_REPO_ACCESS_TOKEN \ --env TOOLSTATE_REPO \ --env TOOLSTATE_PUBLISH \ @@ -338,7 +343,9 @@ docker \ rust-ci \ "${command[@]}" -cat $objdir/${SUMMARY_FILE} >> "${GITHUB_STEP_SUMMARY}" +if isCI; then + cat $objdir/${SUMMARY_FILE} >> "${GITHUB_STEP_SUMMARY}" +fi if [ -f /.dockerenv ]; then rm -rf $objdir diff --git a/src/ci/docker/scripts/build-fuchsia-toolchain.sh b/src/ci/docker/scripts/build-fuchsia-toolchain.sh index beea2f522fde9..7a0d4fcffc156 100755 --- a/src/ci/docker/scripts/build-fuchsia-toolchain.sh +++ b/src/ci/docker/scripts/build-fuchsia-toolchain.sh @@ -4,13 +4,13 @@ set -ex source shared.sh FUCHSIA_SDK_URL=https://chrome-infra-packages.appspot.com/dl/fuchsia/sdk/core/linux-amd64 -FUCHSIA_SDK_ID=MrhQwtmP8CpZre-i_PNOREcThbUcrX3bA-45d6WQr-cC -FUCHSIA_SDK_SHA256=32b850c2d98ff02a59adefa2fcf34e44471385b51cad7ddb03ee3977a590afe7 +FUCHSIA_SDK_ID=version:20.20240412.3.1 +FUCHSIA_SDK_SHA256=cc52f3497487dd813c89d9316e6967efcea89c7759edccf3e40fcf3662e53f19 FUCHSIA_SDK_USR_DIR=/usr/local/core-linux-amd64-fuchsia-sdk CLANG_DOWNLOAD_URL=\ https://chrome-infra-packages.appspot.com/dl/fuchsia/third_party/clang/linux-amd64 -CLANG_DOWNLOAD_ID=Tpc85d1ZwSlZ6UKl2d96GRUBGNA5JKholOKe24sRDr0C -CLANG_DOWNLOAD_SHA256=4e973ce5dd59c12959e942a5d9df7a19150118d03924a86894e29edb8b110ebd +CLANG_DOWNLOAD_ID=git_revision:c777c011a709dffd4fa5e79cad7947b7c3405d02 +CLANG_DOWNLOAD_SHA256=779167422ad73c292f049dcea5569f84577af9292189ed2749518b966a4d0844 install_clang() { mkdir -p clang_download diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py index 437b51641fc92..8ac00a8863f6f 100755 --- a/src/ci/docker/scripts/fuchsia-test-runner.py +++ b/src/ci/docker/scripts/fuchsia-test-runner.py @@ -280,7 +280,7 @@ def start(self): # Look up the product bundle transfer manifest. self.log_info("Looking up the product bundle transfer manifest...") product_name = "minimal." + self.triple_to_arch(self.target) - fuchsia_version = "14.20230811.2.1" + fuchsia_version = "20.20240412.3.1" # FIXME: We should be able to replace this with the machine parsable # `ffx --machine json product lookup ...` once F15 is released. diff --git a/src/ci/docker/scripts/nodejs.sh b/src/ci/docker/scripts/nodejs.sh new file mode 100644 index 0000000000000..ae28da0bdc7fd --- /dev/null +++ b/src/ci/docker/scripts/nodejs.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -ex + +NODEJS_VERSION=v20.12.2 +INSTALL_PATH=${1:-/node} + +url="https://nodejs.org/dist/${NODEJS_VERSION}/node-${NODEJS_VERSION}-linux-x64.tar.xz" +curl -sL "$url" | tar -xJ +mv node-${NODEJS_VERSION}-linux-x64 "${INSTALL_PATH}" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/script.sh b/src/ci/docker/scripts/x86_64-gnu-llvm.sh similarity index 100% rename from src/ci/docker/host-x86_64/x86_64-gnu-llvm-16/script.sh rename to src/ci/docker/scripts/x86_64-gnu-llvm.sh diff --git a/src/ci/github-actions/calculate-job-matrix.py b/src/ci/github-actions/calculate-job-matrix.py new file mode 100755 index 0000000000000..68565f489c939 --- /dev/null +++ b/src/ci/github-actions/calculate-job-matrix.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 + +""" +This script serves for generating a matrix of jobs that should +be executed on CI. + +It reads job definitions from `src/ci/github-actions/jobs.yml` +and filters them based on the event that happened on CI. +""" +import dataclasses +import enum +import json +import logging +import os +from pathlib import Path +from typing import List, Dict, Any, Optional + +import yaml + +CI_DIR = Path(__file__).absolute().parent.parent +JOBS_YAML_PATH = Path(__file__).absolute().parent / "jobs.yml" + +Job = Dict[str, Any] + + +def name_jobs(jobs: List[Dict], prefix: str) -> List[Job]: + """ + Add a `name` attribute to each job, based on its image and the given `prefix`. + """ + for job in jobs: + job["name"] = f"{prefix} - {job['image']}" + return jobs + + +def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: + """ + Prepends `environment` to the `env` attribute of each job. + The `env` of each job has higher precedence than `environment`. + """ + for job in jobs: + env = environment.copy() + env.update(job.get("env", {})) + job["env"] = env + return jobs + + +class WorkflowRunType(enum.Enum): + PR = enum.auto() + Try = enum.auto() + Auto = enum.auto() + + +@dataclasses.dataclass +class GitHubCtx: + event_name: str + ref: str + repository: str + + +def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: + if ctx.event_name == "pull_request": + return WorkflowRunType.PR + elif ctx.event_name == "push": + old_bors_try_build = ( + ctx.ref in ("refs/heads/try", "refs/heads/try-perf") and + ctx.repository == "rust-lang-ci/rust" + ) + new_bors_try_build = ( + ctx.ref == "refs/heads/automation/bors/try" and + ctx.repository == "rust-lang/rust" + ) + try_build = old_bors_try_build or new_bors_try_build + + if try_build: + return WorkflowRunType.Try + + if ctx.ref == "refs/heads/auto" and ctx.repository == "rust-lang-ci/rust": + return WorkflowRunType.Auto + + return None + + +def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[Job]: + if run_type == WorkflowRunType.PR: + return add_base_env(name_jobs(job_data["pr"], "PR"), job_data["envs"]["pr"]) + elif run_type == WorkflowRunType.Try: + return add_base_env(name_jobs(job_data["try"], "try"), job_data["envs"]["try"]) + elif run_type == WorkflowRunType.Auto: + return add_base_env(name_jobs(job_data["auto"], "auto"), job_data["envs"]["auto"]) + + return [] + + +def skip_jobs(jobs: List[Dict[str, Any]], channel: str) -> List[Job]: + """ + Skip CI jobs that are not supposed to be executed on the given `channel`. + """ + return [j for j in jobs if j.get("only_on_channel", channel) == channel] + + +def get_github_ctx() -> GitHubCtx: + return GitHubCtx( + event_name=os.environ["GITHUB_EVENT_NAME"], + ref=os.environ["GITHUB_REF"], + repository=os.environ["GITHUB_REPOSITORY"] + ) + + +def format_run_type(run_type: WorkflowRunType) -> str: + if run_type == WorkflowRunType.PR: + return "pr" + elif run_type == WorkflowRunType.Auto: + return "auto" + elif run_type == WorkflowRunType.Try: + return "try" + else: + raise AssertionError() + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + with open(JOBS_YAML_PATH) as f: + data = yaml.safe_load(f) + + github_ctx = get_github_ctx() + + run_type = find_run_type(github_ctx) + logging.info(f"Job type: {run_type}") + + with open(CI_DIR / "channel") as f: + channel = f.read().strip() + + jobs = [] + if run_type is not None: + jobs = calculate_jobs(run_type, data) + jobs = skip_jobs(jobs, channel) + run_type = format_run_type(run_type) + + logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}") + print(f"jobs={json.dumps(jobs)}") + print(f"run_type={run_type}") diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml deleted file mode 100644 index bc81b1e04a764..0000000000000 --- a/src/ci/github-actions/ci.yml +++ /dev/null @@ -1,811 +0,0 @@ -###################################################### -# WARNING! Action needed when changing this file # -###################################################### - -# Due to GitHub Actions limitations, we can't use YAML Anchors directly in the -# CI configuration stored on the repository. To work around that this file is -# expanded by a tool in the repository, and the expansion is committed as well. -# -# After you make any change to the file you'll need to run this command: -# -# ./x.py run src/tools/expand-yaml-anchors -# -# ...and commit the file it updated in addition to this one. If you forget this -# step CI will fail. - ---- -############################### -# YAML Anchors Definition # -############################### - -# This key contains most of the YAML anchors that will be used later in the -# document. YAML anchors allows us to greatly reduce duplication inside the CI -# configuration by reusing parts of the configuration. -# -# YAML anchors work by defining an anchor with `&anchor-name` and reusing its -# content in another place with `*anchor-name`. The special `<<` map key merges -# the content of the map with the content of the anchor (or list of anchors). -# -# The expand-yaml-anchors tool will automatically remove this block from the -# output YAML file. -x--expand-yaml-anchors--remove: - - &shared-ci-variables - CI_JOB_NAME: ${{ matrix.name }} - CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - # commit of PR sha or commit sha. `GITHUB_SHA` is not accurate for PRs. - HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} - DOCKER_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - &public-variables - SCCACHE_BUCKET: rust-lang-ci-sccache2 - TOOLSTATE_REPO: https://github.com/rust-lang-nursery/rust-toolstate - CACHE_DOMAIN: ci-caches.rust-lang.org - - - &prod-variables - SCCACHE_BUCKET: rust-lang-ci-sccache2 - DEPLOY_BUCKET: rust-lang-ci2 - TOOLSTATE_REPO: https://github.com/rust-lang-nursery/rust-toolstate - TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/rust-lang/rust/issues - TOOLSTATE_PUBLISH: 1 - # AWS_SECRET_ACCESS_KEYs are stored in GitHub's secrets storage, named - # AWS_SECRET_ACCESS_KEY_. Including the key id in the name allows to - # rotate them in a single branch while keeping the old key in another - # branch, which wouldn't be possible if the key was named with the kind - # (caches, artifacts...). - CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZI5DHEBFL - ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZN24CBO55 - AWS_REGION: us-west-1 - CACHE_DOMAIN: ci-caches.rust-lang.org - - - &dummy-variables - SCCACHE_BUCKET: rust-lang-gha-caches - DEPLOY_BUCKET: rust-lang-gha - TOOLSTATE_REPO: https://github.com/pietroalbini/rust-toolstate - TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/pietroalbini/rust-toolstate/issues - TOOLSTATE_PUBLISH: 1 - # AWS_SECRET_ACCESS_KEYs are stored in GitHub's secrets storage, named - # AWS_SECRET_ACCESS_KEY_. Including the key id in the name allows to - # rotate them in a single branch while keeping the old key in another - # branch, which wouldn't be possible if the key was named with the kind - # (caches, artifacts...). - CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZOMUQATD5 - ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZH5AYXDVF - AWS_REGION: us-west-1 - CACHE_DOMAIN: ci-caches-gha.rust-lang.org - - - &base-job - env: {} - - - &job-linux-4c - os: ubuntu-20.04-4core-16gb - <<: *base-job - - - &job-linux-8c - os: ubuntu-20.04-8core-32gb - <<: *base-job - - - &job-linux-16c - os: ubuntu-20.04-16core-64gb - <<: *base-job - - - &job-macos-xl - os: macos-13 # We use the standard runner for now - <<: *base-job - - - &job-macos-m1 - os: macos-14 - <<: *base-job - - - &job-windows-8c - os: windows-2019-8core-32gb - <<: *base-job - - - &job-windows-16c - os: windows-2019-16core-64gb - <<: *base-job - - - &job-aarch64-linux - os: [self-hosted, ARM64, linux] - - - &step - if: success() && !env.SKIP_JOB - - - &base-ci-job - defaults: - run: - shell: ${{ contains(matrix.os, 'windows') && 'msys2 {0}' || 'bash' }} - timeout-minutes: 600 - runs-on: "${{ matrix.os }}" - env: *shared-ci-variables - steps: - - if: contains(matrix.os, 'windows') - uses: msys2/setup-msys2@v2.22.0 - with: - # i686 jobs use mingw32. x86_64 and cross-compile jobs use mingw64. - msystem: ${{ contains(matrix.name, 'i686') && 'mingw32' || 'mingw64' }} - # don't try to download updates for already installed packages - update: false - # don't try to use the msys that comes built-in to the github runner, - # so we can control what is installed (i.e. not python) - release: true - # Inherit the full path from the Windows environment, with MSYS2's */bin/ - # dirs placed in front. This lets us run Windows-native Python etc. - path-type: inherit - install: > - make - dos2unix - diffutils - - - name: disable git crlf conversion - run: git config --global core.autocrlf false - - - name: checkout the source code - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - # Rust Log Analyzer can't currently detect the PR number of a GitHub - # Actions build on its own, so a hint in the log message is needed to - # point it in the right direction. - - name: configure the PR in which the error message will be posted - run: echo "[CI_PR_NUMBER=$num]" - env: - num: ${{ github.event.number }} - if: success() && !env.SKIP_JOB && github.event_name == 'pull_request' - - - name: add extra environment variables - run: src/ci/scripts/setup-environment.sh - env: - # Since it's not possible to merge `${{ matrix.env }}` with the other - # variables in `job..env`, the variables defined in the matrix - # are passed to the `setup-environment.sh` script encoded in JSON, - # which then uses log commands to actually set them. - EXTRA_VARIABLES: ${{ toJson(matrix.env) }} - <<: *step - - - name: decide whether to skip this job - run: src/ci/scripts/should-skip-this.sh - <<: *step - - - name: ensure the channel matches the target branch - run: src/ci/scripts/verify-channel.sh - <<: *step - - - name: collect CPU statistics - run: src/ci/scripts/collect-cpu-stats.sh - <<: *step - - - name: show the current environment - run: src/ci/scripts/dump-environment.sh - <<: *step - - - name: install awscli - run: src/ci/scripts/install-awscli.sh - <<: *step - - - name: install sccache - run: src/ci/scripts/install-sccache.sh - <<: *step - - - name: select Xcode - run: src/ci/scripts/select-xcode.sh - <<: *step - - - name: install clang - run: src/ci/scripts/install-clang.sh - <<: *step - - - name: install tidy - run: src/ci/scripts/install-tidy.sh - <<: *step - - - name: install WIX - run: src/ci/scripts/install-wix.sh - <<: *step - - - name: disable git crlf conversion - run: src/ci/scripts/disable-git-crlf-conversion.sh - <<: *step - - - name: checkout submodules - run: src/ci/scripts/checkout-submodules.sh - <<: *step - - - name: install MSYS2 - run: src/ci/scripts/install-msys2.sh - <<: *step - - - name: install MinGW - run: src/ci/scripts/install-mingw.sh - <<: *step - - - name: install ninja - run: src/ci/scripts/install-ninja.sh - <<: *step - - - name: enable ipv6 on Docker - run: src/ci/scripts/enable-docker-ipv6.sh - <<: *step - - # Disable automatic line ending conversion (again). On Windows, when we're - # installing dependencies, something switches the git configuration directory or - # re-enables autocrlf. We've not tracked down the exact cause -- and there may - # be multiple -- but this should ensure submodules are checked out with the - # appropriate line endings. - - name: disable git crlf conversion - run: src/ci/scripts/disable-git-crlf-conversion.sh - <<: *step - - - name: ensure line endings are correct - run: src/ci/scripts/verify-line-endings.sh - <<: *step - - - name: ensure backported commits are in upstream branches - run: src/ci/scripts/verify-backported-commits.sh - <<: *step - - - name: ensure the stable version number is correct - run: src/ci/scripts/verify-stable-version-number.sh - <<: *step - - - name: run the build - run: src/ci/scripts/run-build-from-ci.sh - env: - AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }} - TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} - <<: *step - - - name: create github artifacts - run: src/ci/scripts/create-doc-artifacts.sh - <<: *step - - - name: upload artifacts to github - uses: actions/upload-artifact@v3 - with: - # name is set in previous step - name: ${{ env.DOC_ARTIFACT_NAME }} - path: obj/artifacts/doc - if-no-files-found: ignore - retention-days: 5 - <<: *step - - - name: upload artifacts to S3 - run: src/ci/scripts/upload-artifacts.sh - env: - AWS_ACCESS_KEY_ID: ${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }} - # Adding a condition on DEPLOY=1 or DEPLOY_ALT=1 is not needed as all deploy - # builders *should* have the AWS credentials available. Still, explicitly - # adding the condition is helpful as this way CI will not silently skip - # deploying artifacts from a dist builder if the variables are misconfigured, - # erroring about invalid credentials instead. - if: success() && !env.SKIP_JOB && (github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1') - <<: *step - - # These snippets are used by the try-success, try-failure, auto-success and auto-failure jobs. - # Check out their documentation for more information on why they're needed. - - - &base-outcome-job - name: bors build finished - runs-on: ubuntu-latest - - - &base-success-job - steps: - - name: mark the job as a success - run: exit 0 - shell: bash - <<: *base-outcome-job - - - &base-failure-job - steps: - - name: mark the job as a failure - run: exit 1 - shell: bash - <<: *base-outcome-job - -########################### -# Builders definition # -########################### - -name: CI -on: - push: - branches: - - auto - - try - - try-perf - - automation/bors/try - - master - pull_request: - branches: - - "**" - -permissions: - contents: read - packages: write - -defaults: - run: - # On Linux, macOS, and Windows, use the system-provided bash as the default - # shell. (This should only make a difference on Windows, where the default - # shell is PowerShell.) - shell: bash - -concurrency: - # For a given workflow, if we push to the same branch, cancel all previous builds on that branch. - # We add an exception for try builds (try branch) and unrolled rollup builds (try-perf), which - # are all triggered on the same branch, but which should be able to run concurrently. - group: ${{ github.workflow }}-${{ ((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.sha) || github.ref }} - cancel-in-progress: true - -jobs: - pr: - <<: *base-ci-job - name: PR - ${{ matrix.name }} - env: - <<: [*shared-ci-variables, *public-variables] - PR_CI_JOB: 1 - if: github.event_name == 'pull_request' - continue-on-error: ${{ matrix.name == 'mingw-check-tidy' }} - strategy: - matrix: - include: - - name: mingw-check - <<: *job-linux-4c - - - name: mingw-check-tidy - <<: *job-linux-4c - - - name: x86_64-gnu-llvm-16 - env: - ENABLE_GCC_CODEGEN: "1" - <<: *job-linux-16c - - - name: x86_64-gnu-tools - <<: *job-linux-16c - - auto: - <<: *base-ci-job - name: auto - ${{ matrix.name }} - env: - <<: [*shared-ci-variables, *prod-variables] - if: github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust' - strategy: - matrix: - include: - ############################# - # Linux/Docker builders # - ############################# - - - name: aarch64-gnu - <<: *job-aarch64-linux - - - name: arm-android - <<: *job-linux-8c - - - name: armhf-gnu - <<: *job-linux-8c - - - name: dist-aarch64-linux - env: - CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-8c - - - name: dist-android - <<: *job-linux-8c - - - name: dist-arm-linux - <<: *job-linux-16c - - - name: dist-armhf-linux - <<: *job-linux-8c - - - name: dist-armv7-linux - <<: *job-linux-8c - - - name: dist-i586-gnu-i586-i686-musl - <<: *job-linux-8c - - - name: dist-i686-linux - <<: *job-linux-8c - - - name: dist-loongarch64-linux - <<: *job-linux-8c - - - name: dist-ohos - <<: *job-linux-8c - - - name: dist-powerpc-linux - <<: *job-linux-8c - - - name: dist-powerpc64-linux - <<: *job-linux-8c - - - name: dist-powerpc64le-linux - <<: *job-linux-8c - - - name: dist-riscv64-linux - <<: *job-linux-8c - - - name: dist-s390x-linux - <<: *job-linux-8c - - - name: dist-various-1 - <<: *job-linux-8c - - - name: dist-various-2 - <<: *job-linux-8c - - - name: dist-x86_64-freebsd - <<: *job-linux-8c - - - name: dist-x86_64-illumos - <<: *job-linux-8c - - - &dist-x86_64-linux - name: dist-x86_64-linux - env: - CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-16c - - - name: dist-x86_64-linux-alt - env: - IMAGE: dist-x86_64-linux - CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-16c - - - name: dist-x86_64-musl - env: - CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-8c - - - name: dist-x86_64-netbsd - <<: *job-linux-8c - - - name: i686-gnu - <<: *job-linux-8c - - - name: i686-gnu-nopt - <<: *job-linux-8c - - - name: mingw-check - <<: *job-linux-4c - - - name: test-various - <<: *job-linux-8c - - - name: x86_64-gnu - <<: *job-linux-4c - - # This job ensures commits landing on nightly still pass the full - # test suite on the stable channel. There are some UI tests that - # depend on the channel being built (for example if they include the - # channel name on the output), and this builder prevents landing - # changes that would result in broken builds after a promotion. - - name: x86_64-gnu-stable - env: - IMAGE: x86_64-gnu - RUST_CI_OVERRIDE_RELEASE_CHANNEL: stable - # Only run this job on the nightly channel. Running this on beta - # could cause failures when `dev: 1` in `stage0.txt`, and running - # this on stable is useless. - CI_ONLY_WHEN_CHANNEL: nightly - <<: *job-linux-4c - - - name: x86_64-gnu-aux - <<: *job-linux-4c - - - name: x86_64-gnu-integration - env: - # Only run this job on the nightly channel. Fuchsia requires - # nightly features to compile, and this job would fail if - # executed on beta and stable. - CI_ONLY_WHEN_CHANNEL: nightly - <<: *job-linux-8c - - - name: x86_64-gnu-debug - <<: *job-linux-8c - - - name: x86_64-gnu-distcheck - <<: *job-linux-8c - - - name: x86_64-gnu-llvm-18 - env: - RUST_BACKTRACE: 1 - <<: *job-linux-8c - - - name: x86_64-gnu-llvm-17 - env: - RUST_BACKTRACE: 1 - <<: *job-linux-8c - - - name: x86_64-gnu-llvm-16 - env: - RUST_BACKTRACE: 1 - <<: *job-linux-8c - - - name: x86_64-gnu-nopt - <<: *job-linux-4c - - - name: x86_64-gnu-tools - env: - DEPLOY_TOOLSTATES_JSON: toolstates-linux.json - <<: *job-linux-8c - - #################### - # macOS Builders # - #################### - - - name: dist-x86_64-apple - env: - SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin - RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1 - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - MACOSX_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_14.3.1.app - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 - DIST_REQUIRE_ALL_TOOLS: 1 - CODEGEN_BACKENDS: llvm,cranelift - <<: *job-macos-xl - - - name: dist-apple-various - env: - SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim - RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - MACOSX_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_14.3.1.app - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 - <<: *job-macos-xl - - - name: x86_64-apple-1 - env: &env-x86_64-apple-tests - SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps - RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - MACOSX_DEPLOYMENT_TARGET: 10.12 - MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_14.3.1.app - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 - <<: *job-macos-xl - - - name: x86_64-apple-2 - env: - SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc tests/run-make-fulldeps - <<: *env-x86_64-apple-tests - <<: *job-macos-xl - - # This target only needs to support 11.0 and up as nothing else supports the hardware - - name: dist-aarch64-apple - env: - SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin - RUST_CONFIGURE_ARGS: >- - --enable-full-tools - --enable-sanitizers - --enable-profiler - --set rust.jemalloc - --set llvm.ninja=false - --set rust.lto=thin - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_14.3.1.app - USE_XCODE_CLANG: 1 - MACOSX_DEPLOYMENT_TARGET: 11.0 - MACOSX_STD_DEPLOYMENT_TARGET: 11.0 - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 - DIST_REQUIRE_ALL_TOOLS: 1 - <<: *job-macos-m1 - - # This target only needs to support 11.0 and up as nothing else supports the hardware - - name: aarch64-apple - env: - SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin - RUST_CONFIGURE_ARGS: >- - --enable-sanitizers - --enable-profiler - --set rust.jemalloc - RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_14.3.1.app - USE_XCODE_CLANG: 1 - MACOSX_DEPLOYMENT_TARGET: 11.0 - MACOSX_STD_DEPLOYMENT_TARGET: 11.0 - NO_LLVM_ASSERTIONS: 1 - NO_DEBUG_ASSERTIONS: 1 - NO_OVERFLOW_CHECKS: 1 - <<: *job-macos-m1 - - ###################### - # Windows Builders # - ###################### - - - name: x86_64-msvc - env: - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler - SCRIPT: make ci-msvc - <<: *job-windows-8c - - - name: i686-msvc - env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc - SCRIPT: make ci-msvc - <<: *job-windows-8c - - - name: x86_64-msvc-ext - env: - SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo && src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows - HOST_TARGET: x86_64-pc-windows-msvc - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld --save-toolstates=/tmp/toolstate/toolstates.json - DEPLOY_TOOLSTATES_JSON: toolstates-windows.json - <<: *job-windows-8c - - # 32/64-bit MinGW builds. - # - # We are using MinGW with POSIX threads since LLVM requires - # C++'s std::thread which is disabled in libstdc++ with win32 threads. - # FIXME: Libc++ doesn't have this limitation so we can avoid - # winpthreads if we switch to it. - # - # Instead of relying on the MinGW version installed on CI we download - # and install one ourselves so we won't be surprised by changes to CI's - # build image. - # - # Finally, note that the downloads below are all in the `rust-lang-ci` S3 - # bucket, but they clearly didn't originate there! The downloads originally - # came from the mingw-w64 SourceForge download site. Unfortunately - # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. - - - name: i686-mingw - env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - SCRIPT: make ci-mingw - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). - NO_DOWNLOAD_CI_LLVM: 1 - CUSTOM_MINGW: 1 - <<: *job-windows-8c - - - name: x86_64-mingw - env: - SCRIPT: make ci-mingw - RUST_CONFIGURE_ARGS: >- - --build=x86_64-pc-windows-gnu - --enable-profiler - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). - NO_DOWNLOAD_CI_LLVM: 1 - CUSTOM_MINGW: 1 - <<: *job-windows-8c - - - name: dist-x86_64-msvc - env: - RUST_CONFIGURE_ARGS: >- - --build=x86_64-pc-windows-msvc - --host=x86_64-pc-windows-msvc - --target=x86_64-pc-windows-msvc - --enable-full-tools - --enable-profiler - --set rust.codegen-units=1 - SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths - DIST_REQUIRE_ALL_TOOLS: 1 - <<: *job-windows-8c - - - name: dist-i686-msvc - env: - RUST_CONFIGURE_ARGS: >- - --build=i686-pc-windows-msvc - --host=i686-pc-windows-msvc - --target=i686-pc-windows-msvc,i586-pc-windows-msvc - --enable-full-tools - --enable-profiler - SCRIPT: python x.py dist bootstrap --include-default-paths - DIST_REQUIRE_ALL_TOOLS: 1 - <<: *job-windows-8c - - - name: dist-aarch64-msvc - env: - RUST_CONFIGURE_ARGS: >- - --build=x86_64-pc-windows-msvc - --host=aarch64-pc-windows-msvc - --enable-full-tools - --enable-profiler - SCRIPT: python x.py dist bootstrap --include-default-paths - DIST_REQUIRE_ALL_TOOLS: 1 - <<: *job-windows-8c - - - name: dist-i686-mingw - env: - RUST_CONFIGURE_ARGS: >- - --build=i686-pc-windows-gnu - --enable-full-tools - --enable-profiler - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). - NO_DOWNLOAD_CI_LLVM: 1 - SCRIPT: python x.py dist bootstrap --include-default-paths - CUSTOM_MINGW: 1 - DIST_REQUIRE_ALL_TOOLS: 1 - <<: *job-windows-8c - - - name: dist-x86_64-mingw - env: - SCRIPT: python x.py dist bootstrap --include-default-paths - RUST_CONFIGURE_ARGS: >- - --build=x86_64-pc-windows-gnu - --enable-full-tools - --enable-profiler - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). - NO_DOWNLOAD_CI_LLVM: 1 - CUSTOM_MINGW: 1 - DIST_REQUIRE_ALL_TOOLS: 1 - <<: *job-windows-8c - - - name: dist-x86_64-msvc-alt - env: - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler - SCRIPT: python x.py dist bootstrap --include-default-paths - <<: *job-windows-8c - - try: - <<: *base-ci-job - name: try - ${{ matrix.name }} - env: - DIST_TRY_BUILD: 1 - <<: [*shared-ci-variables, *prod-variables] - if: github.event_name == 'push' && (((github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust') || ((github.ref == 'refs/heads/automation/bors/try') && github.repository == 'rust-lang/rust')) - strategy: - matrix: - include: - - &dist-x86_64-linux - name: dist-x86_64-linux - env: - CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-16c - - - master: - name: master - runs-on: ubuntu-latest - env: - <<: [*prod-variables] - if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'rust-lang-ci/rust' - steps: - - name: checkout the source code - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: publish toolstate - run: src/ci/publish_toolstate.sh - shell: bash - env: - TOOLSTATE_REPO_ACCESS_TOKEN: ${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }} - <<: *step - - # These jobs don't actually test anything, but they're used to tell bors the - # build completed, as there is no practical way to detect when a workflow is - # successful listening to webhooks only. - try-success: - needs: [try] - if: "success() && github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'" - <<: *base-success-job - try-failure: - needs: [try] - if: "!success() && github.event_name == 'push' && (github.ref == 'refs/heads/try' || github.ref == 'refs/heads/try-perf') && github.repository == 'rust-lang-ci/rust'" - <<: *base-failure-job - auto-success: - needs: [auto] - if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'" - <<: *base-success-job - auto-failure: - needs: [auto] - if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'" - <<: *base-failure-job diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml new file mode 100644 index 0000000000000..1445f0dc46d54 --- /dev/null +++ b/src/ci/github-actions/jobs.yml @@ -0,0 +1,459 @@ +# This file contains definitions of CI job parameters that are loaded +# dynamically in CI from ci.yml. +runners: + - &base-job + env: { } + + - &job-linux-4c + os: ubuntu-20.04-4core-16gb + <<: *base-job + + - &job-linux-8c + os: ubuntu-20.04-8core-32gb + <<: *base-job + + - &job-linux-16c + os: ubuntu-20.04-16core-64gb + <<: *base-job + + - &job-macos-xl + os: macos-13 # We use the standard runner for now + <<: *base-job + + - &job-macos-m1 + os: macos-14 + <<: *base-job + + - &job-windows-8c + os: windows-2019-8core-32gb + <<: *base-job + + - &job-windows-16c + os: windows-2019-16core-64gb + <<: *base-job + + - &job-aarch64-linux + os: [ self-hosted, ARM64, linux ] + +envs: + production: + &production + DEPLOY_BUCKET: rust-lang-ci2 + TOOLSTATE_ISSUES_API_URL: https://api.github.com/repos/rust-lang/rust/issues + TOOLSTATE_PUBLISH: 1 + # AWS_SECRET_ACCESS_KEYs are stored in GitHub's secrets storage, named + # AWS_SECRET_ACCESS_KEY_. Including the key id in the name allows to + # rotate them in a single branch while keeping the old key in another + # branch, which wouldn't be possible if the key was named with the kind + # (caches, artifacts...). + CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZI5DHEBFL + ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZN24CBO55 + AWS_REGION: us-west-1 + + try: + <<: *production + DIST_TRY_BUILD: 1 + + auto: + <<: *production + + pr: + PR_CI_JOB: 1 + +# Jobs that run on each push to a pull request (PR) +# These jobs automatically inherit envs.pr, to avoid repeating +# it in each job definition. +pr: + - image: mingw-check + <<: *job-linux-4c + - image: mingw-check-tidy + continue_on_error: true + <<: *job-linux-4c + - image: x86_64-gnu-llvm-17 + env: + ENABLE_GCC_CODEGEN: "1" + <<: *job-linux-16c + - image: x86_64-gnu-tools + <<: *job-linux-16c + +# Jobs that run when you perform a try build (@bors try) +# These jobs automatically inherit envs.try, to avoid repeating +# it in each job definition. +try: + - image: dist-x86_64-linux + env: + CODEGEN_BACKENDS: llvm,cranelift + <<: *job-linux-16c + +# Main CI jobs that have to be green to merge a commit into master +# These jobs automatically inherit envs.auto, to avoid repeating +# it in each job definition. +auto: + ############################# + # Linux/Docker builders # + ############################# + + - image: aarch64-gnu + <<: *job-aarch64-linux + + - image: arm-android + <<: *job-linux-8c + + - image: armhf-gnu + <<: *job-linux-8c + + - image: dist-aarch64-linux + env: + CODEGEN_BACKENDS: llvm,cranelift + <<: *job-linux-8c + + - image: dist-android + <<: *job-linux-8c + + - image: dist-arm-linux + <<: *job-linux-16c + + - image: dist-armhf-linux + <<: *job-linux-8c + + - image: dist-armv7-linux + <<: *job-linux-8c + + - image: dist-i586-gnu-i586-i686-musl + <<: *job-linux-8c + + - image: dist-i686-linux + <<: *job-linux-8c + + - image: dist-loongarch64-linux + <<: *job-linux-8c + + - image: dist-ohos + <<: *job-linux-8c + + - image: dist-powerpc-linux + <<: *job-linux-8c + + - image: dist-powerpc64-linux + <<: *job-linux-8c + + - image: dist-powerpc64le-linux + <<: *job-linux-8c + + - image: dist-riscv64-linux + <<: *job-linux-8c + + - image: dist-s390x-linux + <<: *job-linux-8c + + - image: dist-various-1 + <<: *job-linux-8c + + - image: dist-various-2 + <<: *job-linux-8c + + - image: dist-x86_64-freebsd + <<: *job-linux-8c + + - image: dist-x86_64-illumos + <<: *job-linux-8c + + - image: dist-x86_64-linux + env: + CODEGEN_BACKENDS: llvm,cranelift + <<: *job-linux-16c + + - image: dist-x86_64-linux-alt + env: + IMAGE: dist-x86_64-linux + CODEGEN_BACKENDS: llvm,cranelift + <<: *job-linux-16c + + - image: dist-x86_64-musl + env: + CODEGEN_BACKENDS: llvm,cranelift + <<: *job-linux-8c + + - image: dist-x86_64-netbsd + <<: *job-linux-8c + + - image: i686-gnu + <<: *job-linux-8c + + - image: i686-gnu-nopt + <<: *job-linux-8c + + - image: mingw-check + <<: *job-linux-4c + + - image: test-various + <<: *job-linux-8c + + - image: x86_64-gnu + <<: *job-linux-4c + + # This job ensures commits landing on nightly still pass the full + # test suite on the stable channel. There are some UI tests that + # depend on the channel being built (for example if they include the + # channel name on the output), and this builder prevents landing + # changes that would result in broken builds after a promotion. + - image: x86_64-gnu-stable + # Only run this job on the nightly channel. Running this on beta + # could cause failures when `dev: 1` in `stage0.txt`, and running + # this on stable is useless. + only_on_channel: nightly + env: + IMAGE: x86_64-gnu + RUST_CI_OVERRIDE_RELEASE_CHANNEL: stable + <<: *job-linux-4c + + - image: x86_64-gnu-aux + <<: *job-linux-4c + + - image: x86_64-gnu-integration + # Only run this job on the nightly channel. Fuchsia requires + # nightly features to compile, and this job would fail if + # executed on beta and stable. + only_on_channel: nightly + <<: *job-linux-8c + + - image: x86_64-gnu-debug + <<: *job-linux-8c + + - image: x86_64-gnu-distcheck + <<: *job-linux-8c + + - image: x86_64-gnu-llvm-18 + env: + RUST_BACKTRACE: 1 + <<: *job-linux-8c + + - image: x86_64-gnu-llvm-17 + env: + RUST_BACKTRACE: 1 + <<: *job-linux-8c + + - image: x86_64-gnu-nopt + <<: *job-linux-4c + + - image: x86_64-gnu-tools + env: + DEPLOY_TOOLSTATES_JSON: toolstates-linux.json + <<: *job-linux-8c + + #################### + # macOS Builders # + #################### + + - image: dist-x86_64-apple + env: + SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin + RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1 + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.12 + SELECT_XCODE: /Applications/Xcode_14.3.1.app + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + NO_OVERFLOW_CHECKS: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + CODEGEN_BACKENDS: llvm,cranelift + <<: *job-macos-xl + + - image: dist-apple-various + env: + SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim + RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.12 + SELECT_XCODE: /Applications/Xcode_14.3.1.app + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + NO_OVERFLOW_CHECKS: 1 + <<: *job-macos-xl + + - image: x86_64-apple-1 + env: &env-x86_64-apple-tests + SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc --skip tests/run-make-fulldeps + RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + MACOSX_DEPLOYMENT_TARGET: 10.12 + MACOSX_STD_DEPLOYMENT_TARGET: 10.12 + SELECT_XCODE: /Applications/Xcode_14.3.1.app + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + NO_OVERFLOW_CHECKS: 1 + <<: *job-macos-xl + + - image: x86_64-apple-2 + env: + SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc tests/run-make-fulldeps + <<: *env-x86_64-apple-tests + <<: *job-macos-xl + + # This target only needs to support 11.0 and up as nothing else supports the hardware + - image: dist-aarch64-apple + env: + SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin + RUST_CONFIGURE_ARGS: >- + --enable-full-tools + --enable-sanitizers + --enable-profiler + --set rust.jemalloc + --set llvm.ninja=false + --set rust.lto=thin + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + SELECT_XCODE: /Applications/Xcode_14.3.1.app + USE_XCODE_CLANG: 1 + MACOSX_DEPLOYMENT_TARGET: 11.0 + MACOSX_STD_DEPLOYMENT_TARGET: 11.0 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + NO_OVERFLOW_CHECKS: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-macos-m1 + + # This target only needs to support 11.0 and up as nothing else supports the hardware + - image: aarch64-apple + env: + SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin + RUST_CONFIGURE_ARGS: >- + --enable-sanitizers + --enable-profiler + --set rust.jemalloc + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + SELECT_XCODE: /Applications/Xcode_14.3.1.app + USE_XCODE_CLANG: 1 + MACOSX_DEPLOYMENT_TARGET: 11.0 + MACOSX_STD_DEPLOYMENT_TARGET: 11.0 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + NO_OVERFLOW_CHECKS: 1 + <<: *job-macos-m1 + + ###################### + # Windows Builders # + ###################### + + - image: x86_64-msvc + env: + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler + SCRIPT: make ci-msvc + <<: *job-windows-8c + + - image: i686-msvc + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc + SCRIPT: make ci-msvc + <<: *job-windows-8c + + - image: x86_64-msvc-ext + env: + SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo && src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows + HOST_TARGET: x86_64-pc-windows-msvc + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld --save-toolstates=/tmp/toolstate/toolstates.json + DEPLOY_TOOLSTATES_JSON: toolstates-windows.json + <<: *job-windows-8c + + # 32/64-bit MinGW builds. + # + # We are using MinGW with POSIX threads since LLVM requires + # C++'s std::thread which is disabled in libstdc++ with win32 threads. + # FIXME: Libc++ doesn't have this limitation so we can avoid + # winpthreads if we switch to it. + # + # Instead of relying on the MinGW version installed on CI we download + # and install one ourselves so we won't be surprised by changes to CI's + # build image. + # + # Finally, note that the downloads below are all in the `rust-lang-ci` S3 + # bucket, but they clearly didn't originate there! The downloads originally + # came from the mingw-w64 SourceForge download site. Unfortunately + # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. + + - image: i686-mingw + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu + SCRIPT: make ci-mingw + # We are intentionally allowing an old toolchain on this builder (and that's + # incompatible with LLVM downloads today). + NO_DOWNLOAD_CI_LLVM: 1 + CUSTOM_MINGW: 1 + <<: *job-windows-8c + + - image: x86_64-mingw + env: + SCRIPT: make ci-mingw + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu + # We are intentionally allowing an old toolchain on this builder (and that's + # incompatible with LLVM downloads today). + NO_DOWNLOAD_CI_LLVM: 1 + CUSTOM_MINGW: 1 + <<: *job-windows-8c + + - image: dist-x86_64-msvc + env: + RUST_CONFIGURE_ARGS: >- + --build=x86_64-pc-windows-msvc + --host=x86_64-pc-windows-msvc + --target=x86_64-pc-windows-msvc + --enable-full-tools + --enable-profiler + --set rust.codegen-units=1 + SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-8c + + - image: dist-i686-msvc + env: + RUST_CONFIGURE_ARGS: >- + --build=i686-pc-windows-msvc + --host=i686-pc-windows-msvc + --target=i686-pc-windows-msvc,i586-pc-windows-msvc + --enable-full-tools + --enable-profiler + SCRIPT: python x.py dist bootstrap --include-default-paths + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-8c + + - image: dist-aarch64-msvc + env: + RUST_CONFIGURE_ARGS: >- + --build=x86_64-pc-windows-msvc + --host=aarch64-pc-windows-msvc + --enable-full-tools + --enable-profiler + SCRIPT: python x.py dist bootstrap --include-default-paths + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-8c + + - image: dist-i686-mingw + env: + RUST_CONFIGURE_ARGS: >- + --build=i686-pc-windows-gnu + --enable-full-tools + # We are intentionally allowing an old toolchain on this builder (and that's + # incompatible with LLVM downloads today). + NO_DOWNLOAD_CI_LLVM: 1 + SCRIPT: python x.py dist bootstrap --include-default-paths + CUSTOM_MINGW: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-8c + + - image: dist-x86_64-mingw + env: + SCRIPT: python x.py dist bootstrap --include-default-paths + RUST_CONFIGURE_ARGS: >- + --build=x86_64-pc-windows-gnu + --enable-full-tools + # We are intentionally allowing an old toolchain on this builder (and that's + # incompatible with LLVM downloads today). + NO_DOWNLOAD_CI_LLVM: 1 + CUSTOM_MINGW: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + <<: *job-windows-8c + + - image: dist-x86_64-msvc-alt + env: + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler + SCRIPT: python x.py dist bootstrap --include-default-paths + <<: *job-windows-8c diff --git a/src/ci/scripts/install-msys2.sh b/src/ci/scripts/install-msys2.sh index e3f76744cbe64..2ae782356047b 100755 --- a/src/ci/scripts/install-msys2.sh +++ b/src/ci/scripts/install-msys2.sh @@ -28,16 +28,19 @@ if isWindows; then # Install pacboy for easily installing packages pacman -S --noconfirm pactoys - # Delete these pre-installed tools so we can't accidentally use them, because we are using the - # MSYS2 setup action versions instead. - # Delete pre-installed version of MSYS2 - echo "Cleaning up tools in PATH" - rm -r "/c/msys64/" - # Delete Strawberry Perl, which contains a version of mingw - rm -r "/c/Strawberry/" - # Delete these other copies of mingw, I don't even know where they come from. - rm -r "/c/mingw64/" - rm -r "/c/mingw32/" + # Remove these pre-installed tools so we can't accidentally use them, because we are using the + # MSYS2 setup action versions instead. Because `rm -r`-ing them is slow, we mv them off path + # instead. + # Remove pre-installed version of MSYS2 + echo "Cleaning up existing tools in PATH" + notpath="/c/NOT/ON/PATH/" + mkdir --parents "$notpath" + mv -t "$notpath" "/c/msys64/" + # Remove Strawberry Perl, which contains a version of mingw + mv -t "$notpath" "/c/Strawberry/" + # Remove these other copies of mingw, I don't even know where they come from. + mv -t "$notpath" "/c/mingw64/" + mv -t "$notpath" "/c/mingw32/" echo "Finished cleaning up tools in PATH" if isKnownToBeMingwBuild; then diff --git a/src/ci/scripts/install-ninja.sh b/src/ci/scripts/install-ninja.sh index 5145a03e353de..23cbc2eb6d107 100755 --- a/src/ci/scripts/install-ninja.sh +++ b/src/ci/scripts/install-ninja.sh @@ -8,7 +8,7 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" if isWindows; then mkdir ninja - curl -o ninja.zip "${MIRRORS_BASE}/2017-03-15-ninja-win.zip" + curl -o ninja.zip "${MIRRORS_BASE}/2024-03-28-v1.11.1-ninja-win.zip" 7z x -oninja ninja.zip rm ninja.zip ciCommandSetEnv "RUST_CONFIGURE_ARGS" "${RUST_CONFIGURE_ARGS} --enable-ninja" diff --git a/src/ci/scripts/should-skip-this.sh b/src/ci/scripts/should-skip-this.sh deleted file mode 100755 index 48127166ad080..0000000000000 --- a/src/ci/scripts/should-skip-this.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# Set the SKIP_JOB environment variable if this job is not supposed to run on the current builder. - -set -euo pipefail -IFS=$'\n\t' - -source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" - -if [[ -n "${CI_ONLY_WHEN_CHANNEL-}" ]]; then - if [[ "${CI_ONLY_WHEN_CHANNEL}" = "$(cat src/ci/channel)" ]]; then - echo "The channel is the expected one" - else - echo "Not executing this job as the channel is not the expected one" - ciCommandSetEnv SKIP_JOB 1 - exit 0 - fi -fi - - -echo "Executing the job since there is no skip rule preventing the execution" -exit 0 diff --git a/src/ci/scripts/upload-artifacts.sh b/src/ci/scripts/upload-artifacts.sh index 9755edb6dce58..c9c85ec20b45a 100755 --- a/src/ci/scripts/upload-artifacts.sh +++ b/src/ci/scripts/upload-artifacts.sh @@ -45,3 +45,17 @@ deploy_url="s3://${DEPLOY_BUCKET}/${deploy_dir}/$(ciCommit)" retry aws s3 cp --storage-class INTELLIGENT_TIERING \ --no-progress --recursive --acl public-read "${upload_dir}" "${deploy_url}" + +access_url="https://ci-artifacts.rust-lang.org/${deploy_dir}/$(ciCommit)" + +# Output URLs to the uploaded artifacts to GitHub summary (if it is available) +# to make them easily accessible. +if [ -n "${GITHUB_STEP_SUMMARY}" ] +then + echo "# CI artifacts" >> "${GITHUB_STEP_SUMMARY}" + + for filename in "${upload_dir}"/*.xz; do + filename=`basename "${filename}"` + echo "- [${filename}](${access_url}/${filename})" >> "${GITHUB_STEP_SUMMARY}" + done +fi diff --git a/src/doc/book b/src/doc/book index 19c40bfd2d576..d207d894cc5e1 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 19c40bfd2d57641d962f3119a1c343355f1b3c5e +Subproject commit d207d894cc5e1d496ab99beeacd1a420e5d4d238 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index e1eead1181a69..0c68e90acaae5 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit e1eead1181a691e56299294d5f1d62fe7a26d317 +Subproject commit 0c68e90acaae5a611f8f5098a3c2980de9845ab2 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 2e95fc2fd31d6..aa7d4b0b4653d 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 2e95fc2fd31d669947e993aa07ef10dc9828bee7 +Subproject commit aa7d4b0b4653ddb47cb1de2036d090ec2ba9dab1 diff --git a/src/doc/nomicon b/src/doc/nomicon index 6bc2415218d4d..0d5f88475fe28 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 6bc2415218d4dd0cb01433d8320f5ccf79c343a1 +Subproject commit 0d5f88475fe285affa6dbbc806e9e44d730797c0 diff --git a/src/doc/reference b/src/doc/reference index 5afb503a4c1ea..5854fcc286557 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 5afb503a4c1ea3c84370f8f4c08a1cddd1cdf6ad +Subproject commit 5854fcc286557ad3ab34d325073d11d8118096b6 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index e093099709456..60d34b5fd33db 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit e093099709456e6fd74fecd2505fdf49a2471c10 +Subproject commit 60d34b5fd33db1346f9aabfc0c9d0bda6c8e42be diff --git a/src/doc/rust.css b/src/doc/rust.css index bd2e0b9451870..9e51f03085fb8 100644 --- a/src/doc/rust.css +++ b/src/doc/rust.css @@ -136,6 +136,28 @@ h1 a:link, h1 a:visited, h2 a:link, h2 a:visited, h3 a:link, h3 a:visited, h4 a:link, h4 a:visited, h5 a:link, h5 a:visited {color: black;} +h1, h2, h3, h4, h5 { + /* This is needed to be able to position the doc-anchor. Ideally there + would be a "); } - - write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4)); } write!(w, ""); } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index f153a90832910..6d2bb31ee133e 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -1,3 +1,5 @@ +pub(crate) mod encode; + use std::collections::hash_map::Entry; use std::collections::{BTreeMap, VecDeque}; @@ -17,16 +19,53 @@ use crate::html::format::join_with_double_colon; use crate::html::markdown::short_markdown_summary; use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId}; +use encode::{bitmap_to_string, write_vlqhex_to_string}; + +/// The serialized search description sharded version +/// +/// The `index` is a JSON-encoded list of names and other information. +/// +/// The desc has newlined descriptions, split up by size into 128KiB shards. +/// For example, `(4, "foo\nbar\nbaz\nquux")`. +/// +/// There is no single, optimal size for these shards, because it depends on +/// configuration values that we can't predict or control, such as the version +/// of HTTP used (HTTP/1.1 would work better with larger files, while HTTP/2 +/// and 3 are more agnostic), transport compression (gzip, zstd, etc), whether +/// the search query is going to produce a large number of results or a small +/// number, the bandwidth delay product of the network... +/// +/// Gzipping some standard library descriptions to guess what transport +/// compression will do, the compressed file sizes can be as small as 4.9KiB +/// or as large as 18KiB (ignoring the final 1.9KiB shard of leftovers). +/// A "reasonable" range for files is for them to be bigger than 1KiB, +/// since that's about the amount of data that can be transferred in a +/// single TCP packet, and 64KiB, the maximum amount of data that +/// TCP can transfer in a single round trip without extensions. +/// +/// [1]: https://en.wikipedia.org/wiki/Maximum_transmission_unit#MTUs_for_common_media +/// [2]: https://en.wikipedia.org/wiki/Sliding_window_protocol#Basic_concept +/// [3]: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features +pub(crate) struct SerializedSearchIndex { + pub(crate) index: String, + pub(crate) desc: Vec<(usize, String)>, +} + +const DESC_INDEX_SHARD_LEN: usize = 128 * 1024; + /// Builds the search index from the collected metadata pub(crate) fn build_index<'tcx>( krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<'tcx>, -) -> String { +) -> SerializedSearchIndex { + // Maps from ID to position in the `crate_paths` array. let mut itemid_to_pathid = FxHashMap::default(); let mut primitives = FxHashMap::default(); let mut associated_types = FxHashMap::default(); - let mut crate_paths = vec![]; + + // item type, display path, re-exported internal path + let mut crate_paths: Vec<(ItemType, Vec, Option>)> = vec![]; // Attach all orphan items to the type's definition if the type // has since been learned. @@ -36,11 +75,13 @@ pub(crate) fn build_index<'tcx>( let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache)); cache.search_index.push(IndexItem { ty: item.type_(), + defid: item.item_id.as_def_id(), name: item.name.unwrap(), path: join_with_double_colon(&fqp[..fqp.len() - 1]), desc, parent: Some(parent), parent_idx: None, + exact_path: None, impl_id, search_type: get_function_type_for_search( item, @@ -65,8 +106,9 @@ pub(crate) fn build_index<'tcx>( // Sort search index items. This improves the compressibility of the search index. cache.search_index.sort_unstable_by(|k1, k2| { // `sort_unstable_by_key` produces lifetime errors - let k1 = (&k1.path, k1.name.as_str(), &k1.ty, &k1.parent); - let k2 = (&k2.path, k2.name.as_str(), &k2.ty, &k2.parent); + // HACK(rustdoc): should not be sorting `CrateNum` or `DefIndex`, this will soon go away, too + let k1 = (&k1.path, k1.name.as_str(), &k1.ty, k1.parent.map(|id| (id.index, id.krate))); + let k2 = (&k2.path, k2.name.as_str(), &k2.ty, k2.parent.map(|id| (id.index, id.krate))); Ord::cmp(&k1, &k2) }); @@ -89,9 +131,10 @@ pub(crate) fn build_index<'tcx>( map: &mut FxHashMap, itemid: F, lastpathid: &mut isize, - crate_paths: &mut Vec<(ItemType, Vec)>, + crate_paths: &mut Vec<(ItemType, Vec, Option>)>, item_type: ItemType, path: &[Symbol], + exact_path: Option<&[Symbol]>, ) -> RenderTypeId { match map.entry(itemid) { Entry::Occupied(entry) => RenderTypeId::Index(*entry.get()), @@ -99,7 +142,11 @@ pub(crate) fn build_index<'tcx>( let pathid = *lastpathid; entry.insert(pathid); *lastpathid += 1; - crate_paths.push((item_type, path.to_vec())); + crate_paths.push(( + item_type, + path.to_vec(), + exact_path.map(|path| path.to_vec()), + )); RenderTypeId::Index(pathid) } } @@ -112,14 +159,24 @@ pub(crate) fn build_index<'tcx>( primitives: &mut FxHashMap, associated_types: &mut FxHashMap, lastpathid: &mut isize, - crate_paths: &mut Vec<(ItemType, Vec)>, + crate_paths: &mut Vec<(ItemType, Vec, Option>)>, ) -> Option { - let Cache { ref paths, ref external_paths, .. } = *cache; + let Cache { ref paths, ref external_paths, ref exact_paths, .. } = *cache; match id { RenderTypeId::DefId(defid) => { if let Some(&(ref fqp, item_type)) = paths.get(&defid).or_else(|| external_paths.get(&defid)) { + let exact_fqp = exact_paths + .get(&defid) + .or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp)) + // Re-exports only count if the name is exactly the same. + // This is a size optimization, since it means we only need + // to store the name once (and the path is re-used for everything + // exported from this same module). It's also likely to Do + // What I Mean, since if a re-export changes the name, it might + // also be a change in semantic meaning. + .filter(|fqp| fqp.last() == fqp.last()); Some(insert_into_map( itemid_to_pathid, ItemId::DefId(defid), @@ -127,6 +184,7 @@ pub(crate) fn build_index<'tcx>( crate_paths, item_type, fqp, + exact_fqp.map(|x| &x[..]).filter(|exact_fqp| exact_fqp != fqp), )) } else { None @@ -141,6 +199,7 @@ pub(crate) fn build_index<'tcx>( crate_paths, ItemType::Primitive, &[sym], + None, )) } RenderTypeId::Index(_) => Some(id), @@ -151,6 +210,7 @@ pub(crate) fn build_index<'tcx>( crate_paths, ItemType::AssocType, &[sym], + None, )), } } @@ -162,7 +222,7 @@ pub(crate) fn build_index<'tcx>( primitives: &mut FxHashMap, associated_types: &mut FxHashMap, lastpathid: &mut isize, - crate_paths: &mut Vec<(ItemType, Vec)>, + crate_paths: &mut Vec<(ItemType, Vec, Option>)>, ) { if let Some(generics) = &mut ty.generics { for item in generics { @@ -259,7 +319,7 @@ pub(crate) fn build_index<'tcx>( } } - let Cache { ref paths, .. } = *cache; + let Cache { ref paths, ref exact_paths, ref external_paths, .. } = *cache; // Then, on parent modules let crate_items: Vec<&IndexItem> = search_index @@ -274,7 +334,13 @@ pub(crate) fn build_index<'tcx>( lastpathid += 1; if let Some(&(ref fqp, short)) = paths.get(&defid) { - crate_paths.push((short, fqp.clone())); + let exact_fqp = exact_paths + .get(&defid) + .or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp)) + .filter(|exact_fqp| { + exact_fqp.last() == Some(&item.name) && *exact_fqp != fqp + }); + crate_paths.push((short, fqp.clone(), exact_fqp.cloned())); Some(pathid) } else { None @@ -282,6 +348,42 @@ pub(crate) fn build_index<'tcx>( } }); + if let Some(defid) = item.defid + && item.parent_idx.is_none() + { + // If this is a re-export, retain the original path. + // Associated items don't use this. + // Their parent carries the exact fqp instead. + let exact_fqp = exact_paths + .get(&defid) + .or_else(|| external_paths.get(&defid).map(|&(ref fqp, _)| fqp)); + item.exact_path = exact_fqp.and_then(|fqp| { + // Re-exports only count if the name is exactly the same. + // This is a size optimization, since it means we only need + // to store the name once (and the path is re-used for everything + // exported from this same module). It's also likely to Do + // What I Mean, since if a re-export changes the name, it might + // also be a change in semantic meaning. + if fqp.last() != Some(&item.name) { + return None; + } + let path = + if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) { + // `#[macro_export]` always exports to the crate root. + tcx.crate_name(defid.krate).to_string() + } else { + if fqp.len() < 2 { + return None; + } + join_with_double_colon(&fqp[..fqp.len() - 1]) + }; + if path == item.path { + return None; + } + Some(path) + }); + } + // Omit the parent path if it is same to that of the prior item. if lastpath == &item.path { item.path.clear(); @@ -318,21 +420,26 @@ pub(crate) fn build_index<'tcx>( .collect::>(); struct CrateData<'a> { - doc: String, items: Vec<&'a IndexItem>, - paths: Vec<(ItemType, Vec)>, + paths: Vec<(ItemType, Vec, Option>)>, // The String is alias name and the vec is the list of the elements with this alias. // // To be noted: the `usize` elements are indexes to `items`. aliases: &'a BTreeMap>, // Used when a type has more than one impl with an associated item with the same name. associated_item_disambiguators: &'a Vec<(usize, String)>, + // A list of shard lengths encoded as vlqhex. See the comment in write_vlqhex_to_string + // for information on the format. + desc_index: String, + // A list of items with no description. This is eventually turned into a bitmap. + empty_desc: Vec, } struct Paths { ty: ItemType, name: Symbol, path: Option, + exact_path: Option, } impl Serialize for Paths { @@ -346,6 +453,10 @@ pub(crate) fn build_index<'tcx>( if let Some(ref path) = self.path { seq.serialize_element(path)?; } + if let Some(ref path) = self.exact_path { + assert!(self.path.is_some()); + seq.serialize_element(path)?; + } seq.end() } } @@ -368,14 +479,39 @@ pub(crate) fn build_index<'tcx>( mod_paths.insert(&item.path, index); } let mut paths = Vec::with_capacity(self.paths.len()); - for (ty, path) in &self.paths { + for (ty, path, exact) in &self.paths { if path.len() < 2 { - paths.push(Paths { ty: *ty, name: path[0], path: None }); + paths.push(Paths { ty: *ty, name: path[0], path: None, exact_path: None }); continue; } let full_path = join_with_double_colon(&path[..path.len() - 1]); + let full_exact_path = exact + .as_ref() + .filter(|exact| exact.last() == path.last() && exact.len() >= 2) + .map(|exact| join_with_double_colon(&exact[..exact.len() - 1])); + let exact_path = extra_paths.len() + self.items.len(); + let exact_path = full_exact_path.as_ref().map(|full_exact_path| match extra_paths + .entry(full_exact_path.clone()) + { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + if let Some(index) = mod_paths.get(&full_exact_path) { + return *index; + } + entry.insert(exact_path); + if !revert_extra_paths.contains_key(&exact_path) { + revert_extra_paths.insert(exact_path, full_exact_path.clone()); + } + exact_path + } + }); if let Some(index) = mod_paths.get(&full_path) { - paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*index) }); + paths.push(Paths { + ty: *ty, + name: *path.last().unwrap(), + path: Some(*index), + exact_path, + }); continue; } // It means it comes from an external crate so the item and its path will be @@ -383,32 +519,57 @@ pub(crate) fn build_index<'tcx>( // // `index` is put after the last `mod_paths` let index = extra_paths.len() + self.items.len(); - if !revert_extra_paths.contains_key(&index) { - revert_extra_paths.insert(index, full_path.clone()); - } - match extra_paths.entry(full_path) { + match extra_paths.entry(full_path.clone()) { Entry::Occupied(entry) => { paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*entry.get()), + exact_path, }); } Entry::Vacant(entry) => { entry.insert(index); + if !revert_extra_paths.contains_key(&index) { + revert_extra_paths.insert(index, full_path); + } paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(index), + exact_path, }); } } } + // Direct exports use adjacent arrays for the current crate's items, + // but re-exported exact paths don't. + let mut re_exports = Vec::new(); + for (item_index, item) in self.items.iter().enumerate() { + if let Some(exact_path) = item.exact_path.as_ref() { + if let Some(path_index) = mod_paths.get(&exact_path) { + re_exports.push((item_index, *path_index)); + } else { + let path_index = extra_paths.len() + self.items.len(); + let path_index = match extra_paths.entry(exact_path.clone()) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + entry.insert(path_index); + if !revert_extra_paths.contains_key(&path_index) { + revert_extra_paths.insert(path_index, exact_path.clone()); + } + path_index + } + }; + re_exports.push((item_index, path_index)); + } + } + } + let mut names = Vec::with_capacity(self.items.len()); let mut types = String::with_capacity(self.items.len()); let mut full_paths = Vec::with_capacity(self.items.len()); - let mut descriptions = Vec::with_capacity(self.items.len()); let mut parents = Vec::with_capacity(self.items.len()); let mut functions = String::with_capacity(self.items.len()); let mut deprecated = Vec::with_capacity(self.items.len()); @@ -431,7 +592,6 @@ pub(crate) fn build_index<'tcx>( parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0)); names.push(item.name.as_str()); - descriptions.push(&item.desc); if !item.path.is_empty() { full_paths.push((index, &item.path)); @@ -443,7 +603,8 @@ pub(crate) fn build_index<'tcx>( } if item.deprecation.is_some() { - deprecated.push(index); + // bitmasks always use 1-indexing for items, with 0 as the crate itself + deprecated.push(u32::try_from(index + 1).unwrap()); } } @@ -454,17 +615,17 @@ pub(crate) fn build_index<'tcx>( let has_aliases = !self.aliases.is_empty(); let mut crate_data = serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?; - crate_data.serialize_field("doc", &self.doc)?; crate_data.serialize_field("t", &types)?; crate_data.serialize_field("n", &names)?; - // Serialize as an array of item indices and full paths crate_data.serialize_field("q", &full_paths)?; - crate_data.serialize_field("d", &descriptions)?; crate_data.serialize_field("i", &parents)?; crate_data.serialize_field("f", &functions)?; - crate_data.serialize_field("c", &deprecated)?; + crate_data.serialize_field("D", &self.desc_index)?; crate_data.serialize_field("p", &paths)?; + crate_data.serialize_field("r", &re_exports)?; crate_data.serialize_field("b", &self.associated_item_disambiguators)?; + crate_data.serialize_field("c", &bitmap_to_string(&deprecated))?; + crate_data.serialize_field("e", &bitmap_to_string(&self.empty_desc))?; if has_aliases { crate_data.serialize_field("a", &self.aliases)?; } @@ -472,16 +633,58 @@ pub(crate) fn build_index<'tcx>( } } - // Collect the index into a string - format!( + let (empty_desc, desc) = { + let mut empty_desc = Vec::new(); + let mut result = Vec::new(); + let mut set = String::new(); + let mut len: usize = 0; + let mut item_index: u32 = 0; + for desc in std::iter::once(&crate_doc).chain(crate_items.iter().map(|item| &item.desc)) { + if desc == "" { + empty_desc.push(item_index); + item_index += 1; + continue; + } + if set.len() >= DESC_INDEX_SHARD_LEN { + result.push((len, std::mem::replace(&mut set, String::new()))); + len = 0; + } else if len != 0 { + set.push('\n'); + } + set.push_str(&desc); + len += 1; + item_index += 1; + } + result.push((len, std::mem::replace(&mut set, String::new()))); + (empty_desc, result) + }; + + let desc_index = { + let mut desc_index = String::with_capacity(desc.len() * 4); + for &(len, _) in desc.iter() { + write_vlqhex_to_string(len.try_into().unwrap(), &mut desc_index); + } + desc_index + }; + + assert_eq!( + crate_items.len() + 1, + desc.iter().map(|(len, _)| *len).sum::() + empty_desc.len() + ); + + // The index, which is actually used to search, is JSON + // It uses `JSON.parse(..)` to actually load, since JSON + // parses faster than the full JavaScript syntax. + let index = format!( r#"["{}",{}]"#, krate.name(tcx), serde_json::to_string(&CrateData { - doc: crate_doc, items: crate_items, paths: crate_paths, aliases: &aliases, associated_item_disambiguators: &associated_item_disambiguators, + desc_index, + empty_desc, }) .expect("failed serde conversion") // All these `replace` calls are because we have to go through JS string for JSON content. @@ -489,7 +692,8 @@ pub(crate) fn build_index<'tcx>( .replace('\'', r"\'") // We need to escape double quotes for the JSON. .replace("\\\"", "\\\\\"") - ) + ); + SerializedSearchIndex { index, desc } } pub(crate) fn get_function_type_for_search<'tcx>( @@ -586,7 +790,7 @@ fn get_index_type_id( } } // Not supported yet - clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, + clean::Type::Pat(..) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, } } diff --git a/src/librustdoc/html/render/search_index/encode.rs b/src/librustdoc/html/render/search_index/encode.rs new file mode 100644 index 0000000000000..8d715814faad7 --- /dev/null +++ b/src/librustdoc/html/render/search_index/encode.rs @@ -0,0 +1,242 @@ +use base64::prelude::*; + +pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) { + let (sign, magnitude): (bool, u32) = + if n >= 0 { (false, n.try_into().unwrap()) } else { (true, (-n).try_into().unwrap()) }; + // zig-zag encoding + let value: u32 = (magnitude << 1) | (if sign { 1 } else { 0 }); + // Self-terminating hex use capital letters for everything but the + // least significant digit, which is lowercase. For example, decimal 17 + // would be `` Aa `` if zig-zag encoding weren't used. + // + // Zig-zag encoding, however, stores the sign bit as the last bit. + // This means, in the last hexit, 1 is actually `c`, -1 is `b` + // (`a` is the imaginary -0), and, because all the bits are shifted + // by one, `` A` `` is actually 8 and `` Aa `` is -8. + // + // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html + // describes the encoding in more detail. + let mut shift: u32 = 28; + let mut mask: u32 = 0xF0_00_00_00; + // first skip leading zeroes + while shift < 32 { + let hexit = (value & mask) >> shift; + if hexit != 0 || shift == 0 { + break; + } + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } + // now write the rest + while shift < 32 { + let hexit = (value & mask) >> shift; + let hex = char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); + string.push(hex); + shift = shift.wrapping_sub(4); + mask = mask >> 4; + } +} + +// Used during bitmap encoding +enum Container { + /// number of ones, bits + Bits(Box<[u64; 1024]>), + /// list of entries + Array(Vec), + /// list of (start, len-1) + Run(Vec<(u16, u16)>), +} +impl Container { + fn popcount(&self) -> u32 { + match self { + Container::Bits(bits) => bits.iter().copied().map(|x| x.count_ones()).sum(), + Container::Array(array) => { + array.len().try_into().expect("array can't be bigger than 2**32") + } + Container::Run(runs) => { + runs.iter().copied().map(|(_, lenm1)| u32::from(lenm1) + 1).sum() + } + } + } + fn push(&mut self, value: u16) { + match self { + Container::Bits(bits) => bits[value as usize >> 6] |= 1 << (value & 0x3F), + Container::Array(array) => { + array.push(value); + if array.len() >= 4096 { + let array = std::mem::replace(array, Vec::new()); + *self = Container::Bits(Box::new([0; 1024])); + for value in array { + self.push(value); + } + } + } + Container::Run(runs) => { + if let Some(r) = runs.last_mut() + && r.0 + r.1 + 1 == value + { + r.1 += 1; + } else { + runs.push((value, 0)); + } + } + } + } + fn try_make_run(&mut self) -> bool { + match self { + Container::Bits(bits) => { + let mut r: u64 = 0; + for (i, chunk) in bits.iter().copied().enumerate() { + let next_chunk = + i.checked_add(1).and_then(|i| bits.get(i)).copied().unwrap_or(0); + r += !chunk & u64::from((chunk << 1).count_ones()); + r += !next_chunk & u64::from((chunk >> 63).count_ones()); + } + if (2 + 4 * r) >= 8192 { + return false; + } + let bits = std::mem::replace(bits, Box::new([0; 1024])); + *self = Container::Run(Vec::new()); + for (i, bits) in bits.iter().copied().enumerate() { + if bits == 0 { + continue; + } + for j in 0..64 { + let value = (u16::try_from(i).unwrap() << 6) | j; + if bits & (1 << j) != 0 { + self.push(value); + } + } + } + true + } + Container::Array(array) if array.len() <= 5 => false, + Container::Array(array) => { + let mut r = 0; + let mut prev = None; + for value in array.iter().copied() { + if value.checked_sub(1) != prev { + r += 1; + } + prev = Some(value); + } + if 2 + 4 * r >= 2 * array.len() + 2 { + return false; + } + let array = std::mem::replace(array, Vec::new()); + *self = Container::Run(Vec::new()); + for value in array { + self.push(value); + } + true + } + Container::Run(_) => true, + } + } +} + +// checked against roaring-rs in +// https://gitlab.com/notriddle/roaring-test +pub(crate) fn write_bitmap_to_bytes( + domain: &[u32], + mut out: impl std::io::Write, +) -> std::io::Result<()> { + // https://arxiv.org/pdf/1603.06549.pdf + let mut keys = Vec::::new(); + let mut containers = Vec::::new(); + let mut key: u16; + let mut domain_iter = domain.into_iter().copied().peekable(); + let mut has_run = false; + while let Some(entry) = domain_iter.next() { + key = (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); + let value: u16 = (entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit"); + let mut container = Container::Array(vec![value]); + while let Some(entry) = domain_iter.peek().copied() { + let entry_key: u16 = + (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); + if entry_key != key { + break; + } + domain_iter.next().expect("peeking just succeeded"); + container + .push((entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit")); + } + keys.push(key); + has_run = container.try_make_run() || has_run; + containers.push(container); + } + // https://github.com/RoaringBitmap/RoaringFormatSpec + const SERIAL_COOKIE_NO_RUNCONTAINER: u32 = 12346; + const SERIAL_COOKIE: u32 = 12347; + const NO_OFFSET_THRESHOLD: u32 = 4; + let size: u32 = containers.len().try_into().unwrap(); + let start_offset = if has_run { + out.write_all(&u32::to_le_bytes(SERIAL_COOKIE | ((size - 1) << 16)))?; + for set in containers.chunks(8) { + let mut b = 0; + for (i, container) in set.iter().enumerate() { + if matches!(container, &Container::Run(..)) { + b |= 1 << i; + } + } + out.write_all(&[b])?; + } + if size < NO_OFFSET_THRESHOLD { + 4 + 4 * size + ((size + 7) / 8) + } else { + 4 + 8 * size + ((size + 7) / 8) + } + } else { + out.write_all(&u32::to_le_bytes(SERIAL_COOKIE_NO_RUNCONTAINER))?; + out.write_all(&u32::to_le_bytes(containers.len().try_into().unwrap()))?; + 4 + 4 + 4 * size + 4 * size + }; + for (&key, container) in keys.iter().zip(&containers) { + // descriptive header + let key: u32 = key.into(); + let count: u32 = container.popcount() - 1; + out.write_all(&u32::to_le_bytes((count << 16) | key))?; + } + if !has_run || size >= NO_OFFSET_THRESHOLD { + // offset header + let mut starting_offset = start_offset; + for container in &containers { + out.write_all(&u32::to_le_bytes(starting_offset))?; + starting_offset += match container { + Container::Bits(_) => 8192u32, + Container::Array(array) => u32::try_from(array.len()).unwrap() * 2, + Container::Run(runs) => 2 + u32::try_from(runs.len()).unwrap() * 4, + }; + } + } + for container in &containers { + match container { + Container::Bits(bits) => { + for chunk in bits.iter() { + out.write_all(&u64::to_le_bytes(*chunk))?; + } + } + Container::Array(array) => { + for value in array.iter() { + out.write_all(&u16::to_le_bytes(*value))?; + } + } + Container::Run(runs) => { + out.write_all(&u16::to_le_bytes(runs.len().try_into().unwrap()))?; + for (start, lenm1) in runs.iter().copied() { + out.write_all(&u16::to_le_bytes(start))?; + out.write_all(&u16::to_le_bytes(lenm1))?; + } + } + } + } + Ok(()) +} + +pub(crate) fn bitmap_to_string(domain: &[u32]) -> String { + let mut buf = Vec::new(); + let mut strbuf = String::new(); + write_bitmap_to_bytes(&domain, &mut buf).unwrap(); + BASE64_STANDARD.encode_string(&buf, &mut strbuf); + strbuf +} diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs index 738ea0aee7e9d..a27e327f2353a 100644 --- a/src/librustdoc/html/render/type_layout.rs +++ b/src/librustdoc/html/render/type_layout.rs @@ -3,8 +3,7 @@ use askama::Template; use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; -use rustc_middle::ty::layout::LayoutError; -use rustc_middle::ty::Adt; +use rustc_middle::ty::{self, layout::LayoutError}; use rustc_span::symbol::Symbol; use rustc_target::abi::{Primitive, TagEncoding, Variants}; @@ -57,7 +56,7 @@ pub(crate) fn document_type_layout<'a, 'cx: 'a>( variants .iter_enumerated() .map(|(variant_idx, variant_layout)| { - let Adt(adt, _) = type_layout.ty.kind() else { + let ty::Adt(adt, _) = type_layout.ty.kind() else { span_bug!(tcx.def_span(ty_def_id), "not an adt") }; let name = adt.variant(variant_idx).name; diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fbd45b2b48ef9..c806bf1cc66f3 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -24,6 +24,7 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::formats::Impl; use crate::html::format::Buffer; +use crate::html::render::search_index::SerializedSearchIndex; use crate::html::render::{AssocItemLink, ImplRenderingParameters}; use crate::html::{layout, static_files}; use crate::visit::DocVisitor; @@ -46,7 +47,7 @@ use crate::{try_err, try_none}; pub(super) fn write_shared( cx: &mut Context<'_>, krate: &Crate, - search_index: String, + search_index: SerializedSearchIndex, options: &RenderOptions, ) -> Result<(), Error> { // Write out the shared files. Note that these are shared among all rustdoc @@ -312,7 +313,7 @@ pub(super) fn write_shared( let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, krate.name(cx.tcx()).as_str()), &dst); - all_indexes.push(search_index); + all_indexes.push(search_index.index); krates.push(krate.name(cx.tcx()).to_string()); krates.sort(); @@ -335,6 +336,32 @@ else if (window.initSearch) window.initSearch(searchIndex); Ok(v.into_bytes()) })?; + let search_desc_dir = cx.dst.join(format!("search.desc/{krate}", krate = krate.name(cx.tcx()))); + if Path::new(&search_desc_dir).exists() { + try_err!(std::fs::remove_dir_all(&search_desc_dir), &search_desc_dir); + } + try_err!(std::fs::create_dir_all(&search_desc_dir), &search_desc_dir); + let kratename = krate.name(cx.tcx()).to_string(); + for (i, (_, data)) in search_index.desc.into_iter().enumerate() { + let output_filename = static_files::suffix_path( + &format!("{kratename}-desc-{i}-.js"), + &cx.shared.resource_suffix, + ); + let path = search_desc_dir.join(output_filename); + try_err!( + std::fs::write( + &path, + &format!( + r##"searchState.loadedDescShard({kratename}, {i}, {data})"##, + kratename = serde_json::to_string(&kratename).unwrap(), + data = serde_json::to_string(&data).unwrap(), + ) + .into_bytes() + ), + &path + ); + } + write_invocation_specific("crates.js", &|| { let krates = krates.iter().map(|k| format!("\"{k}\"")).join(","); Ok(format!("window.ALL_CRATES = [{krates}];").into_bytes()) diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index 1a34530c2d16e..a1e9cc6dfa142 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 2015, + "ecmaVersion": 8, "sourceType": "module" }, "rules": { diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index f425f3ec95c31..ccb97d7df4c3b 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -34,7 +34,7 @@ nav.sub { in rustdoc.css */ /* Begin theme: light */ -:root { +:root, :root:not([data-theme]) { --main-background-color: white; --main-color: black; --settings-input-color: #2196f3; @@ -140,7 +140,7 @@ nav.sub { @media (prefers-color-scheme: dark) { /* Begin theme: dark */ - :root { + :root, :root:not([data-theme]) { --main-background-color: #353535; --main-color: #ddd; --settings-input-color: #2196f3; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 9993dfb1d8c20..80a4ea0424a26 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1133,6 +1133,7 @@ so that we can apply CSS-filters to change the arrow color in themes */ .setting-check input:checked { background-color: var(--settings-input-color); border-width: 1px; + /* cross-mark image in the settings checkboxes */ content: url('data:image/svg+xml,\ \ '); @@ -1603,7 +1604,31 @@ a.tooltip:hover::after { border-color: var(--settings-button-border-focus); } +#settings-menu > a { + line-height: 0; + font-size: 0; +} +#settings-menu > a:before { + /* Wheel */ + content: url('data:image/svg+xml,\ + '); + width: 22px; + height: 22px; +} + #sidebar-button > a:before { + /* sidebar resizer image */ content: url('data:image/svg+xml,\ \ @@ -1617,18 +1642,37 @@ a.tooltip:hover::after { color: var(--copy-path-button-color); background: var(--main-background-color); height: 34px; + width: 33px; margin-left: 10px; padding: 0; padding-left: 2px; border: 0; - width: 33px; + font-size: 0; } -#copy-path > img { +#copy-path::before { filter: var(--copy-path-img-filter); -} -#copy-path:hover > img { + /* clipboard */ + content: url('data:image/svg+xml,\ +\ +\ +'); + width: 19px; + height: 18px; +} +#copy-path:hover::before { filter: var(--copy-path-img-hover-filter); } +#copy-path.clicked::before { + /* Checkmark */ + content: url('data:image/svg+xml,\ + \ + '); +} @keyframes rotating { from { @@ -1818,6 +1862,7 @@ However, it's not needed with smaller screen width because the doc/code block is /* sidebar button opens modal use hamburger button */ .src #sidebar-button > a:before, .sidebar-menu-toggle:before { + /* hamburger button image */ content: url('data:image/svg+xml,\ '); @@ -1831,6 +1876,7 @@ However, it's not needed with smaller screen width because the doc/code block is /* src sidebar button opens a folder view */ .src #sidebar-button > a:before { + /* folder image */ content: url('data:image/svg+xml,\ \ @@ -2315,8 +2361,14 @@ in src-script.js and main.js tooling to ensure different themes all define all the variables. Do not alter their formatting. */ +/* +About `:root:not([data-theme])`: if for any reason the JS is enabled but cannot be loaded, +`noscript` won't be enabled and the doc will have no color applied. To do around this, we +add a selector check that if `data-theme` is not defined, then we apply the light theme +by default. +*/ /* Begin theme: light */ -:root[data-theme="light"] { +:root[data-theme="light"], :root:not([data-theme]) { --main-background-color: white; --main-color: black; --settings-input-color: #2196f3; diff --git a/src/librustdoc/html/static/images/clipboard.svg b/src/librustdoc/html/static/images/clipboard.svg deleted file mode 100644 index 8adbd99630488..0000000000000 --- a/src/librustdoc/html/static/images/clipboard.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/librustdoc/html/static/images/favicon-16x16.png b/src/librustdoc/html/static/images/favicon-16x16.png deleted file mode 100644 index ea4b45cae1618..0000000000000 Binary files a/src/librustdoc/html/static/images/favicon-16x16.png and /dev/null differ diff --git a/src/librustdoc/html/static/images/wheel.svg b/src/librustdoc/html/static/images/wheel.svg deleted file mode 100644 index 83c07f63d10ab..0000000000000 --- a/src/librustdoc/html/static/images/wheel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index d24148b9556b9..8cebce7ae8610 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -239,20 +239,27 @@ let FunctionType; * `doc` contains the description of the crate. * * `p` is a list of path/type pairs. It is used for parents and function parameters. + * The first item is the type, the second is the name, the third is the visible path (if any) and + * the fourth is the canonical path used for deduplication (if any). + * + * `r` is the canonical path used for deduplication of re-exported items. + * It is not used for associated items like methods (that's the fourth element + * of `p`) but is used for modules items like free functions. * * `c` is an array of item indices that are deprecated. * @typedef {{ * doc: string, * a: Object, * n: Array, - * t: String, + * t: string, * d: Array, - * q: Array<[Number, string]>, - * i: Array, + * q: Array<[number, string]>, + * i: Array, * f: string, - * p: Array, - * b: Array<[Number, String]>, - * c: Array + * p: Array<[number, string] | [number, string, number] | [number, string, number, number]>, + * b: Array<[number, String]>, + * c: Array, + * r: Array<[number, number]>, * }} */ let RawSearchIndexCrate; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index b9a769a7c6da4..64c356607788c 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -329,6 +329,30 @@ function preLoadCss(cssUrl) { search.innerHTML = "

" + searchState.loadingText + "

"; searchState.showResults(search); }, + descShards: new Map(), + loadDesc: async function({descShard, descIndex}) { + if (descShard.promise === null) { + descShard.promise = new Promise((resolve, reject) => { + // The `resolve` callback is stored in the `descShard` + // object, which is itself stored in `this.descShards` map. + // It is called in `loadedDescShard` by the + // search.desc script. + descShard.resolve = resolve; + const ds = descShard; + const fname = `${ds.crate}-desc-${ds.shard}-`; + const url = resourcePath( + `search.desc/${descShard.crate}/${fname}`, + ".js", + ); + loadScript(url, reject); + }); + } + const list = await descShard.promise; + return list[descIndex]; + }, + loadedDescShard: function(crate, shard, data) { + this.descShards.get(crate)[shard].resolve(data.split("\n")); + }, }; const toggleAllDocsId = "toggle-all-docs"; @@ -381,7 +405,7 @@ function preLoadCss(cssUrl) { window.location.replace("#" + item.id); }, 0); } - } + }, ); } } @@ -438,6 +462,7 @@ function preLoadCss(cssUrl) { case "s": case "S": + case "/": ev.preventDefault(); searchState.focus(); break; @@ -585,7 +610,7 @@ function preLoadCss(cssUrl) { const script = document .querySelector("script[data-ignore-extern-crates]"); const ignoreExternCrates = new Set( - (script ? script.getAttribute("data-ignore-extern-crates") : "").split(",") + (script ? script.getAttribute("data-ignore-extern-crates") : "").split(","), ); for (const lib of libs) { if (lib === window.currentCrate || ignoreExternCrates.has(lib)) { @@ -1098,7 +1123,7 @@ function preLoadCss(cssUrl) { } else { wrapper.style.setProperty( "--popover-arrow-offset", - (wrapperPos.right - pos.right + 4) + "px" + (wrapperPos.right - pos.right + 4) + "px", ); } wrapper.style.visibility = ""; @@ -1310,7 +1335,7 @@ function preLoadCss(cssUrl) { const shortcuts = [ ["?", "Show this help dialog"], - ["S", "Focus the search field"], + ["S / /", "Focus the search field"], ["↑", "Move up in search results"], ["↓", "Move down in search results"], ["← / →", "Switch result tab (when results focused)"], @@ -1680,7 +1705,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", - desiredSidebarSize + "px" + desiredSidebarSize + "px", ); }, 100); } @@ -1773,31 +1798,15 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm document.execCommand("copy"); document.body.removeChild(el); - // There is always one children, but multiple childNodes. - but.children[0].style.display = "none"; - - let tmp; - if (but.childNodes.length < 2) { - tmp = document.createTextNode("✓"); - but.appendChild(tmp); - } else { - onEachLazy(but.childNodes, e => { - if (e.nodeType === Node.TEXT_NODE) { - tmp = e; - return true; - } - }); - tmp.textContent = "✓"; - } + but.classList.add("clicked"); if (reset_button_timeout !== null) { window.clearTimeout(reset_button_timeout); } function reset_button() { - tmp.textContent = ""; reset_button_timeout = null; - but.children[0].style.display = ""; + but.classList.remove("clicked"); } reset_button_timeout = window.setTimeout(reset_button, 1000); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 875ebe2fc90d4..41a9897de6dfb 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -79,6 +79,7 @@ const longItemTypes = [ // used for special search precedence const TY_GENERIC = itemTypes.indexOf("generic"); +const TY_IMPORT = itemTypes.indexOf("import"); const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; // Hard limit on how deep to recurse into generics when doing type-driven search. @@ -206,14 +207,14 @@ const editDistanceState = { // insertion this.current[j - 1] + 1, // substitution - this.prev[j - 1] + substitutionCost + this.prev[j - 1] + substitutionCost, ); if ((i > 1) && (j > 1) && (a[aIdx] === b[bIdx - 1]) && (a[aIdx - 1] === b[bIdx])) { // transposition this.current[j] = Math.min( this.current[j], - this.prevPrev[j - 2] + 1 + this.prevPrev[j - 2] + 1, ); } } @@ -242,6 +243,14 @@ function initSearch(rawSearchIndex) { * @type {Array} */ let searchIndex; + /** + * @type {Map} + */ + let searchIndexDeprecated; + /** + * @type {Map} + */ + let searchIndexEmptyDesc; /** * @type {Uint32Array} */ @@ -426,7 +435,7 @@ function initSearch(rawSearchIndex) { return c === "," || c === "="; } -/** + /** * Returns `true` if the given `c` character is a path separator. For example * `:` in `a::b` or a whitespace in `a b`. * @@ -856,8 +865,8 @@ function initSearch(rawSearchIndex) { parserState, parserState.userQuery.slice(start, end), generics, - isInGenerics - ) + isInGenerics, + ), ); } } @@ -1295,7 +1304,7 @@ function initSearch(rawSearchIndex) { * * @return {ResultsTable} */ - function execQuery(parsedQuery, filterCrates, currentCrate) { + async function execQuery(parsedQuery, filterCrates, currentCrate) { const results_others = new Map(), results_in_args = new Map(), results_returned = new Map(); @@ -1316,14 +1325,23 @@ function initSearch(rawSearchIndex) { obj.dist = result.dist; const res = buildHrefAndPath(obj); obj.displayPath = pathSplitter(res[0]); - obj.fullPath = obj.displayPath + obj.name; - // To be sure than it some items aren't considered as duplicate. - obj.fullPath += "|" + obj.ty; + // To be sure than it some items aren't considered as duplicate. + obj.fullPath = res[2] + "|" + obj.ty; if (duplicates.has(obj.fullPath)) { continue; } + + // Exports are specifically not shown if the items they point at + // are already in the results. + if (obj.ty === TY_IMPORT && duplicates.has(res[2])) { + continue; + } + if (duplicates.has(res[2] + "|" + TY_IMPORT)) { + continue; + } duplicates.add(obj.fullPath); + duplicates.add(res[2]); obj.href = res[1]; out.push(obj); @@ -1342,9 +1360,9 @@ function initSearch(rawSearchIndex) { * @param {Results} results * @param {boolean} isType * @param {string} preferredCrate - * @returns {[ResultObject]} + * @returns {Promise<[ResultObject]>} */ - function sortResults(results, isType, preferredCrate) { + async function sortResults(results, isType, preferredCrate) { const userQuery = parsedQuery.userQuery; const result_list = []; for (const result of results.values()) { @@ -1394,8 +1412,8 @@ function initSearch(rawSearchIndex) { } // sort deprecated items later - a = aaa.item.deprecated; - b = bbb.item.deprecated; + a = searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex); + b = searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex); if (a !== b) { return a - b; } @@ -1422,8 +1440,8 @@ function initSearch(rawSearchIndex) { } // sort by description (no description goes later) - a = (aaa.item.desc === ""); - b = (bbb.item.desc === ""); + a = searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex); + b = searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex); if (a !== b) { return a - b; } @@ -1477,7 +1495,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensIn, solutionCb, - unboxingDepth + unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -1524,7 +1542,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, )) { continue; } @@ -1541,7 +1559,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensScratch, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, )) { return true; } @@ -1551,7 +1569,7 @@ function initSearch(rawSearchIndex) { whereClause, mgens ? new Map(mgens) : null, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, )) { return true; } @@ -1625,7 +1643,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensScratch, - unboxingDepth + unboxingDepth, ); if (!solution) { return false; @@ -1638,7 +1656,7 @@ function initSearch(rawSearchIndex) { whereClause, simplifiedMgens, solutionCb, - unboxingDepth + unboxingDepth, ); if (passesUnification) { return true; @@ -1646,7 +1664,7 @@ function initSearch(rawSearchIndex) { } return false; }, - unboxingDepth + unboxingDepth, ); if (passesUnification) { return true; @@ -1663,7 +1681,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, )) { continue; } @@ -1689,7 +1707,7 @@ function initSearch(rawSearchIndex) { whereClause, mgensScratch, solutionCb, - unboxingDepth + 1 + unboxingDepth + 1, ); if (passesUnification) { return true; @@ -1820,7 +1838,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensIn, - unboxingDepth + unboxingDepth, ) { if (fnType.bindings.size < queryElem.bindings.size) { return false; @@ -1849,7 +1867,7 @@ function initSearch(rawSearchIndex) { // possible solutions return false; }, - unboxingDepth + unboxingDepth, ); return newSolutions; }); @@ -1887,7 +1905,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + unboxingDepth, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -1914,7 +1932,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgensTmp, - unboxingDepth + unboxingDepth, ); } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { const simplifiedGenerics = [ @@ -1926,7 +1944,7 @@ function initSearch(rawSearchIndex) { queryElem, whereClause, mgens, - unboxingDepth + unboxingDepth, ); } return false; @@ -1975,7 +1993,7 @@ function initSearch(rawSearchIndex) { elem, whereClause, mgens, - unboxingDepth + 1 + unboxingDepth + 1, ); } if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && @@ -1989,7 +2007,7 @@ function initSearch(rawSearchIndex) { elem, whereClause, mgens, - unboxingDepth + unboxingDepth, ); } } @@ -2007,7 +2025,7 @@ function initSearch(rawSearchIndex) { return 0; } const maxPathEditDistance = Math.floor( - contains.reduce((acc, next) => acc + next.length, 0) / 3 + contains.reduce((acc, next) => acc + next.length, 0) / 3, ); let ret_dist = maxPathEditDistance + 1; const path = ty.path.split("::"); @@ -2066,12 +2084,14 @@ function initSearch(rawSearchIndex) { crate: item.crate, name: item.name, path: item.path, - desc: item.desc, + descShard: item.descShard, + descIndex: item.descIndex, + exactPath: item.exactPath, ty: item.ty, parent: item.parent, type: item.type, is_alias: true, - deprecated: item.deprecated, + bitIndex: item.bitIndex, implDisambiguator: item.implDisambiguator, }; } @@ -2192,7 +2212,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxEditDistance + maxEditDistance, ) { if (!row || (filterCrates !== null && row.crate !== filterCrates)) { return; @@ -2204,7 +2224,7 @@ function initSearch(rawSearchIndex) { // atoms in the function not present in the query const tfpDist = compareTypeFingerprints( fullId, - parsedQuery.typeFingerprint + parsedQuery.typeFingerprint, ); if (tfpDist !== null) { const in_args = row.type && row.type.inputs @@ -2276,7 +2296,7 @@ function initSearch(rawSearchIndex) { const tfpDist = compareTypeFingerprints( row.id, - parsedQuery.typeFingerprint + parsedQuery.typeFingerprint, ); if (tfpDist === null) { return; @@ -2298,10 +2318,10 @@ function initSearch(rawSearchIndex) { row.type.where_clause, mgens, null, - 0 // unboxing depth + 0, // unboxing depth ); }, - 0 // unboxing depth + 0, // unboxing depth )) { return; } @@ -2419,7 +2439,7 @@ function initSearch(rawSearchIndex) { } return [typeNameIdMap.get(name).id, constraints]; - }) + }), ); } @@ -2446,7 +2466,7 @@ function initSearch(rawSearchIndex) { results_others, results_in_args, results_returned, - maxEditDistance + maxEditDistance, ); } } @@ -2477,12 +2497,27 @@ function initSearch(rawSearchIndex) { innerRunQuery(); } - const ret = createQueryResults( + const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ sortResults(results_in_args, true, currentCrate), sortResults(results_returned, true, currentCrate), sortResults(results_others, false, currentCrate), + ]); + const ret = createQueryResults( + sorted_in_args, + sorted_returned, + sorted_others, parsedQuery); handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate); + await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { + const descs = await Promise.all(list.map(result => { + return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? + "" : + searchState.loadDesc(result); + })); + for (const [i, result] of list.entries()) { + result.desc = descs[i]; + } + })); if (parsedQuery.error !== null && ret.others.length !== 0) { // It means some doc aliases were found so let's "remove" the error! ret.query.error = null; @@ -2515,6 +2550,7 @@ function initSearch(rawSearchIndex) { const type = itemTypes[item.ty]; const name = item.name; let path = item.path; + let exactPath = item.exactPath; if (type === "mod") { displayPath = path + "::"; @@ -2536,6 +2572,7 @@ function initSearch(rawSearchIndex) { const parentType = itemTypes[myparent.ty]; let pageType = parentType; let pageName = myparent.name; + exactPath = `${myparent.exactPath}::${myparent.name}`; if (parentType === "primitive") { displayPath = myparent.name + "::"; @@ -2564,7 +2601,7 @@ function initSearch(rawSearchIndex) { href = ROOT_PATH + item.path.replace(/::/g, "/") + "/" + type + "." + name + ".html"; } - return [displayPath, href]; + return [displayPath, href, `${exactPath}::${name}`]; } function pathSplitter(path) { @@ -2581,14 +2618,14 @@ function initSearch(rawSearchIndex) { * @param {ParsedQuery} query * @param {boolean} display - True if this is the active tab */ - function addTab(array, query, display) { + async function addTab(array, query, display) { const extraClass = display ? " active" : ""; const output = document.createElement("div"); if (array.length > 0) { output.className = "search-results " + extraClass; - array.forEach(item => { + for (const item of array) { const name = item.name; const type = itemTypes[item.ty]; const longType = longItemTypes[item.ty]; @@ -2624,7 +2661,7 @@ ${item.displayPath}${name}\ link.appendChild(description); output.appendChild(link); - }); + } } else if (query.error === null) { output.className = "search-failed" + extraClass; output.innerHTML = "No results :(
" + @@ -2666,7 +2703,7 @@ ${item.displayPath}${name}\ * @param {boolean} go_to_first * @param {string} filterCrates */ - function showResults(results, go_to_first, filterCrates) { + async function showResults(results, go_to_first, filterCrates) { const search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true") @@ -2699,9 +2736,11 @@ ${item.displayPath}${name}\ currentResults = results.query.userQuery; - const ret_others = addTab(results.others, results.query, true); - const ret_in_args = addTab(results.in_args, results.query, false); - const ret_returned = addTab(results.returned, results.query, false); + const [ret_others, ret_in_args, ret_returned] = await Promise.all([ + addTab(results.others, results.query, true), + addTab(results.in_args, results.query, false), + addTab(results.returned, results.query, false), + ]); // Navigate to the relevant tab if the current tab is empty, like in case users search // for "-> String". If they had selected another tab previously, they have to click on @@ -2822,7 +2861,7 @@ ${item.displayPath}${name}\ * and display the results. * @param {boolean} [forced] */ - function search(forced) { + async function search(forced) { const query = parseQuery(searchState.input.value.trim()); let filterCrates = getFilterCrates(); @@ -2850,8 +2889,8 @@ ${item.displayPath}${name}\ // recent search query is added to the browser history. updateSearchHistory(buildUrl(query.original, filterCrates)); - showResults( - execQuery(query, filterCrates, window.currentCrate), + await showResults( + await execQuery(query, filterCrates, window.currentCrate), params.go_to_first, filterCrates); } @@ -2920,7 +2959,7 @@ ${item.displayPath}${name}\ pathIndex = type[PATH_INDEX_DATA]; generics = buildItemSearchTypeAll( type[GENERICS_DATA], - lowercasePaths + lowercasePaths, ); if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { bindings = new Map(type[BINDINGS_DATA].map(binding => { @@ -2955,6 +2994,7 @@ ${item.displayPath}${name}\ id: pathIndex, ty: TY_GENERIC, path: null, + exactPath: null, generics, bindings, }; @@ -2964,6 +3004,7 @@ ${item.displayPath}${name}\ id: null, ty: null, path: null, + exactPath: null, generics, bindings, }; @@ -2973,6 +3014,7 @@ ${item.displayPath}${name}\ id: buildTypeMapIndex(item.name, isAssocType), ty: item.ty, path: item.path, + exactPath: item.exactPath, generics, bindings, }; @@ -3030,101 +3072,49 @@ ${item.displayPath}${name}\ * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * - * @param {{ - * string: string, - * offset: number, - * backrefQueue: FunctionSearchType[] - * }} itemFunctionDecoder * @param {Array<{name: string, ty: number}>} lowercasePaths - * @param {Map} * * @return {null|FunctionSearchType} */ - function buildFunctionSearchType(itemFunctionDecoder, lowercasePaths) { - const c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - itemFunctionDecoder.offset += 1; - const [zero, ua, la, ob, cb] = ["0", "@", "`", "{", "}"].map(c => c.charCodeAt(0)); - // `` ` `` is used as a sentinel because it's fewer bytes than `null`, and decodes to zero - // `0` is a backref - if (c === la) { - return null; - } - // sixteen characters after "0" are backref - if (c >= zero && c < ua) { - return itemFunctionDecoder.backrefQueue[c - zero]; - } - if (c !== ob) { - throw ["Unexpected ", c, " in function: expected ", "{", "; this is a bug"]; - } - // call after consuming `{` - function decodeList() { - let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - const ret = []; - while (c !== cb) { - ret.push(decode()); - c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - } - itemFunctionDecoder.offset += 1; // eat cb - return ret; - } - // consumes and returns a list or integer - function decode() { - let n = 0; - let c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - if (c === ob) { - itemFunctionDecoder.offset += 1; - return decodeList(); - } - while (c < la) { - n = (n << 4) | (c & 0xF); - itemFunctionDecoder.offset += 1; - c = itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset); - } - // last character >= la - n = (n << 4) | (c & 0xF); - const [sign, value] = [n & 1, n >> 1]; - itemFunctionDecoder.offset += 1; - return sign ? -value : value; - } - const functionSearchType = decodeList(); - const INPUTS_DATA = 0; - const OUTPUT_DATA = 1; - let inputs, output; - if (typeof functionSearchType[INPUTS_DATA] === "number") { - inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; - } else { - inputs = buildItemSearchTypeAll( - functionSearchType[INPUTS_DATA], - lowercasePaths - ); - } - if (functionSearchType.length > 1) { - if (typeof functionSearchType[OUTPUT_DATA] === "number") { - output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; + function buildFunctionSearchTypeCallback(lowercasePaths) { + return functionSearchType => { + if (functionSearchType === 0) { + return null; + } + const INPUTS_DATA = 0; + const OUTPUT_DATA = 1; + let inputs, output; + if (typeof functionSearchType[INPUTS_DATA] === "number") { + inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)]; } else { - output = buildItemSearchTypeAll( - functionSearchType[OUTPUT_DATA], - lowercasePaths + inputs = buildItemSearchTypeAll( + functionSearchType[INPUTS_DATA], + lowercasePaths, ); } - } else { - output = []; - } - const where_clause = []; - const l = functionSearchType.length; - for (let i = 2; i < l; ++i) { - where_clause.push(typeof functionSearchType[i] === "number" - ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] - : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); - } - const ret = { - inputs, output, where_clause, + if (functionSearchType.length > 1) { + if (typeof functionSearchType[OUTPUT_DATA] === "number") { + output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)]; + } else { + output = buildItemSearchTypeAll( + functionSearchType[OUTPUT_DATA], + lowercasePaths, + ); + } + } else { + output = []; + } + const where_clause = []; + const l = functionSearchType.length; + for (let i = 2; i < l; ++i) { + where_clause.push(typeof functionSearchType[i] === "number" + ? [buildItemSearchType(functionSearchType[i], lowercasePaths)] + : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths)); + } + return { + inputs, output, where_clause, + }; }; - itemFunctionDecoder.backrefQueue.unshift(ret); - if (itemFunctionDecoder.backrefQueue.length > 16) { - itemFunctionDecoder.backrefQueue.pop(); - } - return ret; } /** @@ -3245,6 +3235,185 @@ ${item.displayPath}${name}\ return functionTypeFingerprint[(fullId * 4) + 3]; } + class VlqHexDecoder { + constructor(string, cons) { + this.string = string; + this.cons = cons; + this.offset = 0; + this.backrefQueue = []; + } + // call after consuming `{` + decodeList() { + const cb = "}".charCodeAt(0); + let c = this.string.charCodeAt(this.offset); + const ret = []; + while (c !== cb) { + ret.push(this.decode()); + c = this.string.charCodeAt(this.offset); + } + this.offset += 1; // eat cb + return ret; + } + // consumes and returns a list or integer + decode() { + const [ob, la] = ["{", "`"].map(c => c.charCodeAt(0)); + let n = 0; + let c = this.string.charCodeAt(this.offset); + if (c === ob) { + this.offset += 1; + return this.decodeList(); + } + while (c < la) { + n = (n << 4) | (c & 0xF); + this.offset += 1; + c = this.string.charCodeAt(this.offset); + } + // last character >= la + n = (n << 4) | (c & 0xF); + const [sign, value] = [n & 1, n >> 1]; + this.offset += 1; + return sign ? -value : value; + } + next() { + const c = this.string.charCodeAt(this.offset); + const [zero, ua, la] = ["0", "@", "`"].map(c => c.charCodeAt(0)); + // sixteen characters after "0" are backref + if (c >= zero && c < ua) { + this.offset += 1; + return this.backrefQueue[c - zero]; + } + // special exception: 0 doesn't use backref encoding + // it's already one character, and it's always nullish + if (c === la) { + this.offset += 1; + return this.cons(0); + } + const result = this.cons(this.decode()); + this.backrefQueue.unshift(result); + if (this.backrefQueue.length > 16) { + this.backrefQueue.pop(); + } + return result; + } + } + class RoaringBitmap { + constructor(str) { + const strdecoded = atob(str); + const u8array = new Uint8Array(strdecoded.length); + for (let j = 0; j < strdecoded.length; ++j) { + u8array[j] = strdecoded.charCodeAt(j); + } + const has_runs = u8array[0] === 0x3b; + const size = has_runs ? + ((u8array[2] | (u8array[3] << 8)) + 1) : + ((u8array[4] | (u8array[5] << 8) | (u8array[6] << 16) | (u8array[7] << 24))); + let i = has_runs ? 4 : 8; + let is_run; + if (has_runs) { + const is_run_len = Math.floor((size + 7) / 8); + is_run = u8array.slice(i, i + is_run_len); + i += is_run_len; + } else { + is_run = new Uint8Array(); + } + this.keys = []; + this.cardinalities = []; + for (let j = 0; j < size; ++j) { + this.keys.push(u8array[i] | (u8array[i + 1] << 8)); + i += 2; + this.cardinalities.push((u8array[i] | (u8array[i + 1] << 8)) + 1); + i += 2; + } + this.containers = []; + let offsets = null; + if (!has_runs || this.keys.length >= 4) { + offsets = []; + for (let j = 0; j < size; ++j) { + offsets.push(u8array[i] | (u8array[i + 1] << 8) | (u8array[i + 2] << 16) | + (u8array[i + 3] << 24)); + i += 4; + } + } + for (let j = 0; j < size; ++j) { + if (offsets && offsets[j] !== i) { + console.log(this.containers); + throw new Error(`corrupt bitmap ${j}: ${i} / ${offsets[j]}`); + } + if (is_run[j >> 3] & (1 << (j & 0x7))) { + const runcount = (u8array[i] | (u8array[i + 1] << 8)); + i += 2; + this.containers.push(new RoaringBitmapRun( + runcount, + u8array.slice(i, i + (runcount * 4)), + )); + i += runcount * 4; + } else if (this.cardinalities[j] >= 4096) { + this.containers.push(new RoaringBitmapBits(u8array.slice(i, i + 8192))); + i += 8192; + } else { + const end = this.cardinalities[j] * 2; + this.containers.push(new RoaringBitmapArray( + this.cardinalities[j], + u8array.slice(i, i + end), + )); + i += end; + } + } + } + contains(keyvalue) { + const key = keyvalue >> 16; + const value = keyvalue & 0xFFFF; + for (let i = 0; i < this.keys.length; ++i) { + if (this.keys[i] === key) { + return this.containers[i].contains(value); + } + } + return false; + } + } + + class RoaringBitmapRun { + constructor(runcount, array) { + this.runcount = runcount; + this.array = array; + } + contains(value) { + const l = this.runcount * 4; + for (let i = 0; i < l; i += 4) { + const start = this.array[i] | (this.array[i + 1] << 8); + const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8); + if (value >= start && value <= (start + lenm1)) { + return true; + } + } + return false; + } + } + class RoaringBitmapArray { + constructor(cardinality, array) { + this.cardinality = cardinality; + this.array = array; + } + contains(value) { + const l = this.cardinality * 2; + for (let i = 0; i < l; i += 2) { + const start = this.array[i] | (this.array[i + 1] << 8); + if (value === start) { + return true; + } + } + return false; + } + } + class RoaringBitmapBits { + constructor(array) { + this.array = array; + } + contains(value) { + return !!(this.array[value >> 3] & (1 << (value & 7))); + } + } + /** * Convert raw search index into in-memory search index. * @@ -3252,6 +3421,8 @@ ${item.displayPath}${name}\ */ function buildIndex(rawSearchIndex) { searchIndex = []; + searchIndexDeprecated = new Map(); + searchIndexEmptyDesc = new Map(); const charA = "A".charCodeAt(0); let currentIndex = 0; let id = 0; @@ -3271,26 +3442,50 @@ ${item.displayPath}${name}\ id = 0; for (const [crate, crateCorpus] of rawSearchIndex) { + // a string representing the lengths of each description shard + // a string representing the list of function types + const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); + let descShard = { + crate, + shard: 0, + start: 0, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + const descShardList = [ descShard ]; + + // Deprecated items and items with no description + searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c)); + searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e)); + let descIndex = 0; + // This object should have exactly the same set of fields as the "row" // object defined below. Your JavaScript runtime will thank you. // https://mathiasbynens.be/notes/shapes-ics const crateRow = { - crate: crate, + crate, ty: 3, // == ExternCrate name: crate, path: "", + descShard, + descIndex, + exactPath: "", desc: crateCorpus.doc, parent: undefined, type: null, - id: id, + id, word: crate, normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""), - deprecated: null, + bitIndex: 0, implDisambiguator: null, }; id += 1; searchIndex.push(crateRow); currentIndex += 1; + if (!searchIndexEmptyDesc.get(crate).contains(0)) { + descIndex += 1; + } // a String of one character item type codes const itemTypes = crateCorpus.t; @@ -3302,19 +3497,12 @@ ${item.displayPath}${name}\ // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present, // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11 const itemPaths = new Map(crateCorpus.q); - // an array of (String) descriptions - const itemDescs = crateCorpus.d; + // An array of [(Number) item index, (Number) path index] + // Used to de-duplicate inlined and re-exported stuff + const itemReexports = new Map(crateCorpus.r); // an array of (Number) the parent path index + 1 to `paths`, or 0 if none const itemParentIdxs = crateCorpus.i; - // a string representing the list of function types - const itemFunctionDecoder = { - string: crateCorpus.f, - offset: 0, - backrefQueue: [], - }; - // an array of (Number) indices for the deprecated items - const deprecatedItems = new Set(crateCorpus.c); - // an array of (Number) indices for the deprecated items + // a map Number, string for impl disambiguators const implDisambiguator = new Map(crateCorpus.b); // an array of [(Number) item type, // (String) name] @@ -3326,6 +3514,12 @@ ${item.displayPath}${name}\ // an array of [{name: String, ty: Number}] const lowercasePaths = []; + // a string representing the list of function types + const itemFunctionDecoder = new VlqHexDecoder( + crateCorpus.f, + buildFunctionSearchTypeCallback(lowercasePaths), + ); + // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = paths.length; @@ -3339,9 +3533,10 @@ ${item.displayPath}${name}\ path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; lastPath = path; } + const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path; - lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path}); - paths[i] = {ty: ty, name: name, path: path}; + lowercasePaths.push({ty, name: name.toLowerCase(), path, exactPath}); + paths[i] = {ty, name, path, exactPath}; } // convert `item*` into an object form, and construct word indices. @@ -3354,12 +3549,26 @@ ${item.displayPath}${name}\ lastPath = ""; len = itemTypes.length; for (let i = 0; i < len; ++i) { + const bitIndex = i + 1; + if (descIndex >= descShard.len && + !searchIndexEmptyDesc.get(crate).contains(bitIndex)) { + descShard = { + crate, + shard: descShard.shard + 1, + start: descShard.start + descShard.len, + len: itemDescShardDecoder.next(), + promise: null, + resolve: null, + }; + descIndex = 0; + descShardList.push(descShard); + } let word = ""; if (typeof itemNames[i] === "string") { word = itemNames[i].toLowerCase(); } const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; - const type = buildFunctionSearchType(itemFunctionDecoder, lowercasePaths); + const type = itemFunctionDecoder.next(); if (type !== null) { if (type) { const fp = functionTypeFingerprint.subarray(id * 4, (id + 1) * 4); @@ -3380,22 +3589,27 @@ ${item.displayPath}${name}\ // This object should have exactly the same set of fields as the "crateRow" // object defined above. const row = { - crate: crate, + crate, ty: itemTypes.charCodeAt(i) - charA, name: itemNames[i], - path: path, - desc: itemDescs[i], + path, + descShard, + descIndex, + exactPath: itemReexports.has(i) ? itemPaths.get(itemReexports.get(i)) : path, parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, type, - id: id, + id, word, normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""), - deprecated: deprecatedItems.has(i), + bitIndex, implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null, }; id += 1; searchIndex.push(row); lastPath = row.path; + if (!searchIndexEmptyDesc.get(crate).contains(bitIndex)) { + descIndex += 1; + } } if (aliases) { @@ -3419,6 +3633,7 @@ ${item.displayPath}${name}\ } } currentIndex += itemTypes.length; + searchState.descShards.set(crate, descShardList); } // Drop the (rather large) hash table used for reusing function items TYPES_POOL = new Map(); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index bda7b3c647e7e..73c543567c078 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -211,14 +211,14 @@ function updateSidebarWidth() { if (desktopSidebarWidth && desktopSidebarWidth !== "null") { document.documentElement.style.setProperty( "--desktop-sidebar-width", - desktopSidebarWidth + "px" + desktopSidebarWidth + "px", ); } const srcSidebarWidth = getSettingValue("src-sidebar-width"); if (srcSidebarWidth && srcSidebarWidth !== "null") { document.documentElement.style.setProperty( "--src-sidebar-width", - srcSidebarWidth + "px" + srcSidebarWidth + "px", ); } } diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index ca9a78f51b38e..035376bace9c7 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -99,14 +99,11 @@ static_files! { src_script_js => "static/js/src-script.js", storage_js => "static/js/storage.js", scrape_examples_js => "static/js/scrape-examples.js", - wheel_svg => "static/images/wheel.svg", - clipboard_svg => "static/images/clipboard.svg", copyright => "static/COPYRIGHT.txt", license_apache => "static/LICENSE-APACHE.txt", license_mit => "static/LICENSE-MIT.txt", rust_logo_svg => "static/images/rust-logo.svg", rust_favicon_svg => "static/images/favicon.svg", - rust_favicon_png_16 => "static/images/favicon-16x16.png", rust_favicon_png_32 => "static/images/favicon-32x32.png", fira_sans_regular => "static/fonts/FiraSans-Regular.woff2", fira_sans_medium => "static/fonts/FiraSans-Medium.woff2", diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 0f3debae66c77..1e01cd70b963f 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -6,13 +6,9 @@ {# #} {# #} {{page.title}} {# #} - {# #} + {# #} {# #} {# #} {% else %} - {# #} {# #} {# #}
{{+ display_krate_version_extra}}
{# #} {% endif %} {% else %} -
+
{# #}

Files

{# #}
{# #} {% endif %} {{ sidebar|safe }} {# #} - + {# #}
{# #} {% if page.css_class != "src" %}
{% endif %}
around the whole document, but we don't have that. */ + position: relative; +} + +a.doc-anchor { + color: black; + display: none; + position: absolute; + left: -20px; + /* We add this padding so that when the cursor moves from the heading's text to the anchor, + the anchor doesn't disappear. */ + padding-right: 5px; + /* And this padding is used to make the anchor larger and easier to click on. */ + padding-left: 3px; +} +*:hover > .doc-anchor { + display: block; +} + + /* Code */ pre, code { diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 8a5d647f19b08..07425fed36b00 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 8a5d647f19b08998612146b1cb2ca47083db63e0 +Subproject commit 07425fed36b00e60341c5e29e28d37d40cbd4451 diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 12a421f3c454b..c9c0ee4067f1b 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -3,6 +3,7 @@ - [What is rustc?](what-is-rustc.md) - [Command-line Arguments](command-line-arguments.md) - [Codegen Options](codegen-options/index.md) +- [Jobserver](jobserver.md) - [Lints](lints/index.md) - [Lint Levels](lints/levels.md) - [Lint Groups](lints/groups.md) @@ -21,6 +22,7 @@ - [arm64ec-pc-windows-msvc](platform-support/arm64ec-pc-windows-msvc.md) - [\*-apple-tvos](platform-support/apple-tvos.md) - [\*-apple-watchos\*](platform-support/apple-watchos.md) + - [aarch64-apple-visionos\*](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [arm-none-eabi](platform-support/arm-none-eabi.md) @@ -52,8 +54,13 @@ - [powerpc64-ibm-aix](platform-support/aix.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - - [riscv32*-unknown-none-elf](platform-support/riscv32imac-unknown-none-elf.md) + - [riscv32*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) + - [thumbv6m-none-eabi](./platform-support/thumbv6m-none-eabi.md) + - [thumbv7m-none-eabi](./platform-support/thumbv7m-none-eabi.md) + - [thumbv7em-none-eabi\*](./platform-support/thumbv7em-none-eabi.md) + - [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md) + - [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md) - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - [*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) @@ -76,6 +83,7 @@ - [Profile-guided Optimization](profile-guided-optimization.md) - [Instrumentation-based Code Coverage](instrument-coverage.md) - [Linker-plugin-based LTO](linker-plugin-lto.md) +- [Checking conditional configurations](check-cfg.md) - [Exploit Mitigations](exploit-mitigations.md) - [Symbol Mangling](symbol-mangling/index.md) - [v0 Symbol Format](symbol-mangling/v0.md) diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/rustc/src/check-cfg.md similarity index 95% rename from src/doc/unstable-book/src/compiler-flags/check-cfg.md rename to src/doc/rustc/src/check-cfg.md index 13027eeaf4ff7..37708bda1f3dc 100644 --- a/src/doc/unstable-book/src/compiler-flags/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -1,10 +1,4 @@ -# `check-cfg` - -The tracking issue for this feature is: [#82450](https://github.com/rust-lang/rust/issues/82450). - ------------------------- - -This feature enables checking of conditional configuration. +# Checking conditional configurations `rustc` accepts the `--check-cfg` option, which specifies whether to check conditions and how to check them. The `--check-cfg` option takes a value, called the _check cfg specification_. @@ -37,7 +31,7 @@ present in the list of expected values. If `"value"` is not in it, then `rustc` the future.* To check for the _none_ value (ie `#[cfg(foo)]`) one can use the `none()` predicate inside -`values()`: `values(none())`. It can be followed or precessed by any number of `"value"`. +`values()`: `values(none())`. It can be followed or preceded by any number of `"value"`. To enable checking of values, but to provide an *none*/empty set of expected values (ie. expect `#[cfg(name)]`), use these forms: @@ -77,7 +71,7 @@ Those well known names and values follows the same stability as what they refer Well known names and values checking is always enabled as long as at least one `--check-cfg` argument is present. -As of `2024-02-15T`, the list of known names is as follows: +As of `2024-04-06T`, the list of known names is as follows: @@ -107,6 +101,7 @@ As of `2024-02-15T`, the list of known names is as follows: - `target_thread_local` - `target_vendor` - `test` + - `ub_checks` - `unix` - `windows` @@ -162,7 +157,7 @@ fn poke_platypus() {} fn tame_lion() {} #[cfg(windows = "unix")] // This condition is UNEXPECTED, as while 'windows' is a well known - // condition name, it doens't expect any values + // condition name, it doesn't expect any values fn tame_windows() {} ``` diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 4a4f1ae98e407..babe2bc342bb0 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -42,6 +42,18 @@ generated code, but may be slower to compile. The default value, if not specified, is 16 for non-incremental builds. For incremental builds the default is 256 which allows caching to be more granular. +## collapse-macro-debuginfo + +This flag controls whether code locations from a macro definition are collapsed into a single +location associated with that macro's call site, when generating debuginfo for this crate. + +This option, if passed, overrides both default collapsing behavior and `#[collapse_debuginfo]` +attributes in code. + +* `y`, `yes`, `on`, `true`: collapse code locations in debuginfo. +* `n`, `no`, `off` or `false`: do not collapse code locations in debuginfo. +* `external`: collapse code locations in debuginfo only if the macro comes from a different crate. + ## control-flow-guard This flag controls whether LLVM enables the Windows [Control Flow @@ -479,6 +491,26 @@ then `-C target-feature=+crt-static` "wins" over `-C relocation-model=pic`, and the linker is instructed (`-static`) to produce a statically linked but not position-independent executable. +## relro-level + +This flag controls what level of RELRO (Relocation Read-Only) is enabled. RELRO is an exploit +mitigation which makes the Global Offset Table (GOT) read-only. + +Supported values for this option are: + +- `off`: Dynamically linked functions are resolved lazily and the GOT is writable. +- `partial`: Dynamically linked functions are resolved lazily and written into the Procedure + Linking Table (PLT) part of the GOT (`.got.plt`). The non-PLT part of the GOT (`.got`) is made + read-only and both are moved to prevent writing from buffer overflows. +- `full`: Dynamically linked functions are resolved at the start of program execution and the + Global Offset Table (`.got`/`.got.plt`) is populated eagerly and then made read-only. The GOT is + also moved to prevent writing from buffer overflows. Full RELRO uses more memory and increases + process startup time. + +This flag is ignored on platforms where RELRO is not supported (targets which do not use the ELF +binary format), such as Windows or macOS. Each rustc target has its own default for RELRO. rustc +enables Full RELRO by default on platforms where it is supported. + ## remark This flag lets you print remarks for optimization passes. @@ -548,14 +580,23 @@ data from binaries during linking. Supported values for this option are: -- `none` - debuginfo and symbols (if they exist) are copied to the produced - binary or separate files depending on the target (e.g. `.pdb` files in case - of MSVC). +- `none` - debuginfo and symbols are not modified. - `debuginfo` - debuginfo sections and debuginfo symbols from the symbol table - section are stripped at link time and are not copied to the produced binary - or separate files. + section are stripped at link time and are not copied to the produced binary. + This should leave backtraces mostly-intact but may make using a debugger like + gdb or lldb ineffectual. Prior to 1.79, this unintentionally disabled the + generation of `*.pdb` files on MSVC, resulting in the absence of symbols. - `symbols` - same as `debuginfo`, but the rest of the symbol table section is - stripped as well if the linker supports it. + stripped as well, depending on platform support. On platforms which depend on + this symbol table for backtraces, profiling, and similar, this can affect + them so negatively as to make the trace incomprehensible. Programs which may + be combined with others, such as CLI pipelines and developer tooling, or even + anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`. + +Note that, at any level, removing debuginfo only necessarily impacts "friendly" +introspection. `-Cstrip` cannot be relied on as a meaningful security or +obfuscation measure, as disassemblers and decompilers can extract considerable +information even in the absence of symbols. ## symbol-mangling-version diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 5e02453e23667..7c605333c25d4 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -18,6 +18,16 @@ The value can either be a single identifier or two identifiers separated by `=`. For examples, `--cfg 'verbose'` or `--cfg 'feature="serde"'`. These correspond to `#[cfg(verbose)]` and `#[cfg(feature = "serde")]` respectively. + +## `--check-cfg`: enables checking conditional configurations + +This flag will enable checking conditional configurations. +Refer to the [Checking conditional configurations](check-cfg.md) of this book +for further details and explanation. + +For examples, `--check-cfg 'cfg(verbose)'` or `--check-cfg 'cfg(feature, values("serde"))'`. +These correspond to `#[cfg(verbose)]` and `#[cfg(feature = "serde")]` respectively. + ## `-L`: add a directory to the library search path diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 185a3ba5dbd41..32dc992c42fac 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -348,11 +348,7 @@ $ llvm-cov report \ ## `-Z coverage-options=` -This unstable option provides finer control over some aspects of coverage -instrumentation. Pass one or more of the following values, separated by commas. - -- `branch` or `no-branch` - - Enables or disables branch coverage instrumentation. +[This unstable option is described in the Unstable Book.](../unstable-book/compiler-flags/coverage-options.html) ## Other references diff --git a/src/doc/rustc/src/jobserver.md b/src/doc/rustc/src/jobserver.md new file mode 100644 index 0000000000000..da82b0df7c629 --- /dev/null +++ b/src/doc/rustc/src/jobserver.md @@ -0,0 +1,86 @@ +# Jobserver + +Internally, `rustc` may take advantage of parallelism. `rustc` will coordinate +with the build system calling it if a [GNU Make jobserver] is passed in the +`MAKEFLAGS` environment variable. Other flags may have an effect as well, such +as [`CARGO_MAKEFLAGS`]. If a jobserver is not passed, then `rustc` will choose +the number of jobs to use. + +Starting with Rust 1.76.0, `rustc` will warn if a jobserver appears to be +available but is not accessible, e.g.: + +```console +$ echo 'fn main() {}' | MAKEFLAGS=--jobserver-auth=3,4 rustc - +warning: failed to connect to jobserver from environment variable `MAKEFLAGS="--jobserver-auth=3,4"`: cannot open file descriptor 3 from the jobserver environment variable value: Bad file descriptor (os error 9) + | + = note: the build environment is likely misconfigured +``` + +## Integration with build systems + +The following subsections contain recommendations on how to integrate `rustc` +with build systems so that the jobserver is handled appropriately. + +### GNU Make + +When calling `rustc` from GNU Make, it is recommended that all `rustc` +invocations are marked as recursive in the `Makefile` (by prefixing the command +line with the `+` indicator), so that GNU Make enables the jobserver for them. +For instance: + + + +```make +x: + +@echo 'fn main() {}' | rustc - +``` + +In particular, GNU Make 4.3 (a widely used version as of 2024) passes a simple +pipe jobserver in `MAKEFLAGS` even when it was not made available for the child +process, which in turn means `rustc` will warn about it. For instance, if the +`+` indicator is removed from the example above and GNU Make is called with e.g. +`make -j2`, then the aforementioned warning will trigger. + +For calls to `rustc` inside `$(shell ...)` inside a recursive Make, one can +disable the jobserver manually by clearing the `MAKEFLAGS` variable, e.g.: + +```make +S := $(shell MAKEFLAGS= rustc --print sysroot) + +x: + @$(MAKE) y + +y: + @echo $(S) +``` + +### CMake + +CMake 3.28 supports the `JOB_SERVER_AWARE` option in its [`add_custom_target`] +command, e.g.: + +```cmake +cmake_minimum_required(VERSION 3.28) +project(x) +add_custom_target(x + JOB_SERVER_AWARE TRUE + COMMAND echo 'fn main() {}' | rustc - +) +``` + +For earlier versions, when using CMake with the Makefile generator, one +workaround is to have [`$(MAKE)`] somewhere in the command so that GNU Make +treats it as a recursive Make call, e.g.: + +```cmake +cmake_minimum_required(VERSION 3.22) +project(x) +add_custom_target(x + COMMAND DUMMY_VARIABLE=$(MAKE) echo 'fn main() {}' | rustc - +) +``` + +[GNU Make jobserver]: https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html +[`CARGO_MAKEFLAGS`]: https://doc.rust-lang.org/cargo/reference/environment-variables.html +[`add_custom_target`]: https://cmake.org/cmake/help/latest/command/add_custom_target.html +[`$(MAKE)`]: https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md index 9daa0810126b7..32083b2f731d8 100644 --- a/src/doc/rustc/src/json.md +++ b/src/doc/rustc/src/json.md @@ -262,6 +262,36 @@ information, even if the diagnostics have been suppressed (such as with an } ``` +## Unused Dependency Notifications + +The options `--json=unused-externs` and `--json=unused-externs-silent` in +conjunction with the `unused-crate-dependencies` lint will emit JSON structures +reporting any crate dependencies (specified with `--extern`) which never had any +symbols referenced. These are intended to be consumed by the build system which +can then emit diagnostics telling the user to remove the unused dependencies +from `Cargo.toml` (or whatever build-system file defines dependencies). + +The JSON structure is: +```json +{ + "lint_level": "deny", /* Level of the warning */ + "unused_names": [ + "foo" /* Names of unused crates, as specified with --extern foo=libfoo.rlib */ + ], +} +``` + +The warn/deny/forbid lint level (as defined either on the command line or in the +source) dictates the `lint_level` in the JSON. With `unused-externs`, a +`deny` or `forbid` level diagnostic will also cause `rustc` to exit with a +failure exit code. + +`unused-externs-silent` will report the diagnostic the same way, but will not +cause `rustc` to exit with failure - it's up to the consumer to flag failure +appropriately. (This is needed by Cargo which shares the same dependencies +across multiple build targets, so it should only report an unused dependency if +its not used by any of the targets.) + [option-emit]: command-line-arguments.md#option-emit [option-error-format]: command-line-arguments.md#option-error-format [option-json]: command-line-arguments.md#option-json diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 2ebbf5e15e66b..764798a80e6d2 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -89,9 +89,9 @@ target | notes `aarch64-apple-darwin` | ARM64 macOS (11.0+, Big Sur+) `aarch64-pc-windows-msvc` | ARM64 Windows MSVC `aarch64-unknown-linux-musl` | ARM64 Linux with musl 1.2.3 -`arm-unknown-linux-gnueabi` | ARMv6 Linux (kernel 3.2, glibc 2.17) -`arm-unknown-linux-gnueabihf` | ARMv6 Linux, hardfloat (kernel 3.2, glibc 2.17) -`armv7-unknown-linux-gnueabihf` | ARMv7-A Linux, hardfloat (kernel 3.2, glibc 2.17) +`arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2, glibc 2.17) +`arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2, glibc 2.17) +`armv7-unknown-linux-gnueabihf` | Armv7-A Linux, hardfloat (kernel 3.2, glibc 2.17) [`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17) @@ -118,6 +118,7 @@ The `std` column in the table below has the following meanings: * ✓ indicates the full standard library is available. * \* indicates the target only supports [`no_std`] development. +* ? indicates the standard library support is unknown or a work-in-progress. [`no_std`]: https://rust-embedded.github.io/book/intro/no-std.html @@ -137,53 +138,55 @@ target | std | notes `aarch64-fuchsia` | ✓ | Alias for `aarch64-unknown-fuchsia` [`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia [`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android +[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | ARM64 OpenHarmony `aarch64-unknown-none-softfloat` | * | Bare ARM64, softfloat `aarch64-unknown-none` | * | Bare ARM64, hardfloat -[`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | * | ARM64 UEFI -[`arm-linux-androideabi`](platform-support/android.md) | ✓ | ARMv6 Android -`arm-unknown-linux-musleabi` | ✓ | ARMv6 Linux with musl 1.2.3 -`arm-unknown-linux-musleabihf` | ✓ | ARMv6 Linux with musl 1.2.3, hardfloat -[`armebv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare ARMv7-R, Big Endian -[`armebv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare ARMv7-R, Big Endian, hardfloat -`armv5te-unknown-linux-gnueabi` | ✓ | ARMv5TE Linux (kernel 4.4, glibc 2.23) -`armv5te-unknown-linux-musleabi` | ✓ | ARMv5TE Linux with musl 1.2.3 -[`armv7-linux-androideabi`](platform-support/android.md) | ✓ | ARMv7-A Android -`armv7-unknown-linux-gnueabi` | ✓ | ARMv7-A Linux (kernel 4.15, glibc 2.27) -`armv7-unknown-linux-musleabi` | ✓ | ARMv7-A Linux with musl 1.2.3 -`armv7-unknown-linux-musleabihf` | ✓ | ARMv7-A Linux with musl 1.2.3, hardfloat -[`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | ARMv7-A OpenHarmony -[`armv7a-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare ARMv7-A -[`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare ARMv7-R -[`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare ARMv7-R, hardfloat +[`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | ARM64 UEFI +[`arm-linux-androideabi`](platform-support/android.md) | ✓ | Armv6 Android +`arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3 +`arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.3, hardfloat +[`armebv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian +[`armebv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat +`armv5te-unknown-linux-gnueabi` | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23) +`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3 +[`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android +`armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15, glibc 2.27) +`armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3 +`armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat +[`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | Armv7-A OpenHarmony +[`armv7a-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare Armv7-A +[`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R +[`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat `i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE [^x86_32-floats-x87] `i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 3.2, glibc 2.17) [^x86_32-floats-x87] `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI] +[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI] `i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] -[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | * | 32-bit UEFI +[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] -[`riscv32imac-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA) -[`riscv32i-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32I ISA) -[`riscv32im-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | | Bare RISC-V (RV32IM ISA) -[`riscv32imc-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA) -[`riscv32imafc-unknown-none-elf`](platform-support/riscv32imac-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA) +[`riscv32imac-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA) +[`riscv32i-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32I ISA) +[`riscv32im-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IM ISA) +[`riscv32imc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA) +[`riscv32imafc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA) `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA) `riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA) `sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4, glibc 2.23) `sparcv9-sun-solaris` | ✓ | SPARC Solaris 11, illumos -[`thumbv6m-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare ARMv6-M -[`thumbv7em-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare ARMv7E-M -[`thumbv7em-none-eabihf`](platform-support/arm-none-eabi.md) | * | Bare ARMV7E-M, hardfloat -[`thumbv7m-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare ARMv7-M -[`thumbv7neon-linux-androideabi`](platform-support/android.md) | ✓ | Thumb2-mode ARMv7-A Android with NEON -`thumbv7neon-unknown-linux-gnueabihf` | ✓ | Thumb2-mode ARMv7-A Linux with NEON (kernel 4.4, glibc 2.23) -[`thumbv8m.base-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare ARMv8-M Baseline -[`thumbv8m.main-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare ARMv8-M Mainline -[`thumbv8m.main-none-eabihf`](platform-support/arm-none-eabi.md) | * | Bare ARMv8-M Mainline, hardfloat +[`thumbv6m-none-eabi`](platform-support/thumbv6m-none-eabi.md) | * | Bare Armv6-M +[`thumbv7em-none-eabi`](platform-support/thumbv7em-none-eabi.md) | * | Bare Armv7E-M +[`thumbv7em-none-eabihf`](platform-support/thumbv7em-none-eabi.md) | * | Bare Armv7E-M, hardfloat +[`thumbv7m-none-eabi`](platform-support/thumbv7m-none-eabi.md) | * | Bare Armv7-M +[`thumbv7neon-linux-androideabi`](platform-support/android.md) | ✓ | Thumb2-mode Armv7-A Android with NEON +`thumbv7neon-unknown-linux-gnueabihf` | ✓ | Thumb2-mode Armv7-A Linux with NEON (kernel 4.4, glibc 2.23) +[`thumbv8m.base-none-eabi`](platform-support/thumbv8m.base-none-eabi.md) | * | Bare Armv8-M Baseline +[`thumbv8m.main-none-eabi`](platform-support/thumbv8m.main-none-eabi.md) | * | Bare Armv8-M Mainline +[`thumbv8m.main-none-eabihf`](platform-support/thumbv8m.main-none-eabi.md) | * | Bare Armv8-M Mainline, hardfloat `wasm32-unknown-emscripten` | ✓ | WebAssembly via Emscripten `wasm32-unknown-unknown` | ✓ | WebAssembly `wasm32-wasi` | ✓ | WebAssembly with WASI (undergoing a [rename to `wasm32-wasip1`][wasi-rename]) @@ -195,13 +198,15 @@ target | std | notes [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android `x86_64-pc-solaris` | ✓ | 64-bit Solaris 11, illumos +[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 64-bit x86 MinGW (Windows 10+), LLVM ABI `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27) [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | x86_64 OpenHarmony [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat `x86_64-unknown-redox` | ✓ | Redox OS -[`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | * | 64-bit UEFI +[`x86_64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 64-bit UEFI [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue]. + [wasi-rename]: https://github.com/rust-lang/compiler-team/issues/607 [Fortanix ABI]: https://edp.fortanix.com/ @@ -241,9 +246,10 @@ target | std | host | notes [`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ? | | ARM64 tvOS Simulator [`aarch64-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS [`aarch64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS Simulator +[`aarch64-apple-visionos`](platform-support/apple-visionos.md) | ✓ | | ARM64 Apple visionOS +[`aarch64-apple-visionos-sim`](platform-support/apple-visionos.md) | ✓ | | ARM64 Apple visionOS Simulator [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * | | ARM64 Nintendo Switch, Horizon -[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS | [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS | `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD @@ -258,27 +264,27 @@ target | std | host | notes `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) [`aarch64_be-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD (big-endian) -[`arm64_32-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | ARM Apple WatchOS 64-bit with 32-bit pointers -[`armeb-unknown-linux-gnueabi`](platform-support/armeb-unknown-linux-gnueabi.md) | ✓ | ? | ARM BE8 the default ARM big-endian architecture since [ARMv6](https://developer.arm.com/documentation/101754/0616/armlink-Reference/armlink-Command-line-Options/--be8?lang=en). -[`armv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Bare ARMv4T -`armv4t-unknown-linux-gnueabi` | ? | | ARMv4T Linux -[`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Bare ARMv5TE -`armv5te-unknown-linux-uclibceabi` | ? | | ARMv5TE Linux with uClibc -`armv6-unknown-freebsd` | ✓ | ✓ | ARMv6 FreeBSD -[`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | ARMv6 NetBSD w/hard-float -[`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? | | ARMv6K Nintendo 3DS, Horizon (Requires devkitARM toolchain) -[`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ | | ARMv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain) -[`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | ARMv7-A Linux with uClibc, softfloat -[`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | ARMv7-A Linux with uClibc, hardfloat -`armv7-unknown-freebsd` | ✓ | ✓ | ARMv7-A FreeBSD -[`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | ARMv7-A NetBSD w/hard-float -`armv7-wrs-vxworks-eabihf` | ? | | ARMv7-A for VxWorks +[`arm64_32-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Arm Apple WatchOS 64-bit with 32-bit pointers +[`armeb-unknown-linux-gnueabi`](platform-support/armeb-unknown-linux-gnueabi.md) | ✓ | ? | Arm BE8 the default Arm big-endian architecture since [Armv6](https://developer.arm.com/documentation/101754/0616/armlink-Reference/armlink-Command-line-Options/--be8?lang=en). +[`armv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Bare Armv4T +`armv4t-unknown-linux-gnueabi` | ? | | Armv4T Linux +[`armv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Bare Armv5TE +`armv5te-unknown-linux-uclibceabi` | ? | | Armv5TE Linux with uClibc +`armv6-unknown-freebsd` | ✓ | ✓ | Armv6 FreeBSD +[`armv6-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv6 NetBSD w/hard-float +[`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? | | Armv6k Nintendo 3DS, Horizon (Requires devkitARM toolchain) +[`armv7-sony-vita-newlibeabihf`](platform-support/armv7-sony-vita-newlibeabihf.md) | ✓ | | Armv7-A Cortex-A9 Sony PlayStation Vita (requires VITASDK toolchain) +[`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | Armv7-A Linux with uClibc, softfloat +[`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat +`armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD +[`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float +`armv7-wrs-vxworks-eabihf` | ? | | Armv7-A for VxWorks [`armv7a-kmc-solid_asp3-eabi`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3 [`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat -[`armv7a-none-eabihf`](platform-support/arm-none-eabi.md) | * | | Bare ARMv7-A, hardfloat -[`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | ARMv7-A Apple WatchOS -`armv7s-apple-ios` | ✓ | | ARMv7-A Apple-A6 Apple iOS -[`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare ARMv8-R, hardfloat +[`armv7a-none-eabihf`](platform-support/arm-none-eabi.md) | * | | Bare Armv7-A, hardfloat +[`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Armv7-A Apple WatchOS +`armv7s-apple-ios` | ✓ | | Armv7-A Apple-A6 Apple iOS +[`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat `avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core` `bpfeb-unknown-none` | * | | BPF (big endian) `bpfel-unknown-none` | * | | BPF (little endian) @@ -290,7 +296,6 @@ target | std | host | notes [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] -[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | [^x86_32-floats-return-ABI] `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku [^x86_32-floats-return-ABI] [`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI] [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI] @@ -338,6 +343,7 @@ target | std | host | notes `riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33) `riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl 1.2.3 + RISCV32 support patches) [`riscv32im-risc0-zkvm-elf`](platform-support/riscv32im-risc0-zkvm-elf.md) | ? | | RISC Zero's zero-knowledge Virtual Machine (RV32IM ISA) +[`riscv32ima-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IMA ISA) [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF @@ -354,18 +360,17 @@ target | std | host | notes [`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+ [`sparc64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/sparc64 [`sparc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/sparc64 -[`thumbv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Thumb-mode Bare ARMv4T -[`thumbv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Thumb-mode Bare ARMv5TE +[`thumbv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Thumb-mode Bare Armv4T +[`thumbv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Thumb-mode Bare Armv5TE `thumbv7a-pc-windows-msvc` | ? | | `thumbv7a-uwp-windows-msvc` | ✓ | | -`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.3 +`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.3 [`wasm32-wasip2`](platform-support/wasm32-wasip2.md) | ✓ | | WebAssembly [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS | -[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | [`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3 `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku diff --git a/src/doc/rustc/src/platform-support/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md new file mode 100644 index 0000000000000..9874126e42fe1 --- /dev/null +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -0,0 +1,53 @@ +# aarch64-apple-visionos\* + +- aarch64-apple-visionos +- aarch64-apple-visionos-sim + +**Tier: 3** + +Apple visionOS targets: + +- Apple visionOS on arm64 +- Apple visionOS Simulator on arm64 + +## Target maintainers + +- [@agg23](https://github.com/agg23) +- [@madsmtm](https://github.com/madsmtm) + +## Requirements + +These targets are cross-compiled. +To build these targets Xcode 15 or higher on macOS is required, along with LLVM 18. + +## Building the target + +The targets can be built by enabling them for a `rustc` build, for example: + +```toml +[build] +build-stage = 1 +target = ["aarch64-apple-visionos-sim"] +``` + +## Building Rust programs + +_Note: Building for this target requires the corresponding visionOS SDK, as provided by Xcode 15+._ + +Rust programs can be built for these targets, if `rustc` has been built with support for them, for example: + +```text +rustc --target aarch64-apple-visionos-sim your-code.rs +``` + +## Testing + +There is no support for running the Rust testsuite on visionOS or the simulators. + +There is no easy way to run simple programs on visionOS or the visionOS simulators. Static library builds can be embedded into visionOS applications. + +## Cross-compilation toolchains and C code + +This target can be cross-compiled from x86_64 or aarch64 macOS hosts. + +Other hosts are not supported for cross-compilation, but might work when also providing the required Xcode SDK. diff --git a/src/doc/rustc/src/platform-support/arm-none-eabi.md b/src/doc/rustc/src/platform-support/arm-none-eabi.md index 6335a6405a102..0b1b10e4762e7 100644 --- a/src/doc/rustc/src/platform-support/arm-none-eabi.md +++ b/src/doc/rustc/src/platform-support/arm-none-eabi.md @@ -1,25 +1,131 @@ # `{arm,thumb}*-none-eabi(hf)?` -**Tier: 2** -- [arm(eb)?v7r-none-eabi(hf)?](armv7r-none-eabi.md) -- armv7a-none-eabi -- thumbv6m-none-eabi -- thumbv7m-none-eabi -- thumbv7em-none-eabi(hf)? -- thumbv8m.base-none-eabi -- thumbv8m.main-none-eabi(hf)? - -**Tier: 3** -- [{arm,thumb}v4t-none-eabi](armv4t-none-eabi.md) -- [{arm,thumb}v5te-none-eabi](armv5te-none-eabi.md) -- armv7a-none-eabihf -- [armv8r-none-eabihf](armv8r-none-eabihf.md) - -Bare-metal target for 32-bit ARM CPUs. - -If a target has a `*hf` variant, that variant uses the hardware floating-point -ABI and enables some minimum set of floating-point features based on the FPU(s) -available in that processor family. +## Tier 2 Target List + +- Arm A-Profile Architectures + - `armv7a-none-eabi` +- Arm R-Profile Architectures + - [`armv7r-none-eabi` and `armv7r-none-eabihf`](armv7r-none-eabi.md) + - [`armebv7r-none-eabi` and `armebv7r-none-eabihf`](armv7r-none-eabi.md) +- Arm M-Profile Architectures + - [`thumbv6m-none-eabi`](thumbv6m-none-eabi.md) + - [`thumbv7m-none-eabi`](thumbv7m-none-eabi.md) + - [`thumbv7em-none-eabi` and `thumbv7em-none-eabihf`](thumbv7em-none-eabi.md) + - [`thumbv8m.base-none-eabi`](thumbv8m.base-none-eabi.md) + - [`thumbv8m.main-none-eabi` and `thumbv8m.main-none-eabihf`](thumbv8m.main-none-eabi.md) +- *Legacy* Arm Architectures + - None + +## Tier 3 Target List + +- Arm A-Profile Architectures + - `armv7a-none-eabihf` +- Arm R-Profile Architectures + - [`armv8r-none-eabihf`](armv8r-none-eabihf.md) +- Arm M-Profile Architectures + - None +- *Legacy* Arm Architectures + - [`armv4t-none-eabi` and `thumbv4t-none-eabi`](armv4t-none-eabi.md) + - [`armv5te-none-eabi` and `thumbv5te-none-eabi`](armv5te-none-eabi.md) + +## Common Target Details + +This documentation covers details that apply to a range of bare-metal targets +for 32-bit Arm CPUs. In addition, target specific details may be covered in +their own document. + +There are two 32-bit instruction set architectures (ISAs) defined by Arm: + +- The [*A32 ISA*][a32-isa], with fixed-width 32-bit instructions. Previously + known as the *Arm* ISA, this originated with the original ARM1 of 1985 and has + been updated by various revisions to the architecture specifications ever + since. +- The [*T32 ISA*][t32-isa], with a mix of 16-bit and 32-bit width instructions. + Note that this term includes both the original 16-bit width *Thumb* ISA + introduced with the Armv4T architecture in 1994, and the later 16/32-bit sized + *Thumb-2* ISA introduced with the Armv6T2 architecture in 2003. Again, these + ISAs have been revised by subsequent revisions to the relevant Arm + architecture specifications. + +There is also a 64-bit ISA with fixed-width 32-bit instructions called the *A64 +ISA*, but targets which implement that instruction set generally start with +`aarch64*` and are discussed elsewhere. + +Rust targets starting with `arm*` generate Arm (A32) code by default, whilst +targets named `thumb*` generate Thumb (T32) code by default. Most Arm chips +support both Thumb mode and Arm mode, with the notable exception that M-profile +processors (`thumbv*m*-none-eabi*` targets) *only* support Thumb-mode. + +Rust targets ending with `eabi` use the so-called *soft-float ABI*: functions +which take `f32` or `f64` as arguments will have those values packed into +integer registers. This means that an FPU is not required from an ABI +perspective, but within a function floating-point instructions may still be used +if the code is compiled with a `target-cpu` or `target-feature` option that +enables FPU support. + +Rust targets ending in `eabihf` use the so-called *hard-float ABI*: functions +which take `f32` or `f64` as arguments will have them passed via FPU registers. +These targets therefore require the availability of an FPU and will assume some +baseline level of floating-point support is available (which can vary depending +on the target). More advanced floating-point instructions may be generated if +the code is compiled with a `target-cpu` or `target-feature` option that enables +such additional FPU support. For example, if a given hard-float target has +baseline *single-precision* (`f32`) support in hardware, there may be +`target-cpu` or `target-feature` options that tell LLVM to assume your processor +in fact also has *double-precision* (`f64`) support. + +You may of course use the `f32` and `f64` types in your code, regardless of the +ABI being used, or the level of support your processor has for performing such +operations in hardware. Any floating-point operations that LLVM assumes your +processor cannot support will be lowered to library calls (like `__aeabi_dadd`) +which perform the floating-point operation in software using integer +instructions. + +[t32-isa]: https://developer.arm.com/Architectures/T32%20Instruction%20Set%20Architecture +[a32-isa]: https://developer.arm.com/Architectures/A32%20Instruction%20Set%20Architecture + +## Target CPU and Target Feature options + +It is possible to tell Rust (or LLVM) that you have a specific model of Arm +processor, using the [`-C target-cpu`][target-cpu] option. You can also control +whether Rust (or LLVM) will include instructions that target optional hardware +features, e.g. hardware floating-point, or Advanced SIMD operations, using [`-C +target-feature`][target-feature]. + +It is important to note that selecting a *target-cpu* will typically enable +*all* the optional features available from Arm on that model of CPU and your +particular implementation of that CPU may not have those features available. In +that case, you can use `-C target-feature=-option` to turn off the specific CPU +features you do not have available, leaving you with the optimized instruction +scheduling and support for the features you do have. More details are available +in the detailed target-specific documentation. + +**Note:** Many target-features are currently unstable and subject to change, and +if you use them you should disassemble the compiler output and manually inspect +it to ensure only appropriate instructions for your CPU have been generated. + +If you wish to use the *target-cpu* and *target-feature* options, you can add +them to your `.cargo/config.toml` file alongside any other flags your project +uses (likely linker related ones): + +```toml +rustflags = [ + # Usual Arm bare-metal linker setup + "-Clink-arg=-Tlink.x", + "-Clink-arg=--nmagic", + # tell Rust we have a Cortex-M55 + "-Ctarget-cpu=cortex-m55", + # tell Rust our Cortex-M55 doesn't have Floating-Point M-Profile Vector + # Extensions (but it does have everything else a Cortex-M55 could have). + "-Ctarget-feature=-mve.fp" +] + +[build] +target = "thumbv8m.main-none-eabihf" +``` + +[target-cpu]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-cpu +[target-feature]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-feature ## Requirements @@ -45,14 +151,11 @@ according to the specific device you are using. Pass `-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use `your_script.ld` during linking. -Targets named `thumb*` instead of `arm*` -generate Thumb-mode code by default. M-profile processors (`thumbv*m*-*` -targets) only support Thumb-mode code. -For the `arm*` targets, Thumb-mode code generation can be enabled by using -`-C target-feature=+thumb-mode`. Using the unstable -`#![feature(arm_target_feature)]`, the attribute -`#[target_feature(enable = "thumb-mode")]` can be applied to individual -`unsafe` functions to cause those functions to be compiled to Thumb-mode code. +For the `arm*` targets, Thumb-mode code generation can be enabled by using `-C +target-feature=+thumb-mode`. Using the unstable +`#![feature(arm_target_feature)]`, the attribute `#[target_feature(enable = +"thumb-mode")]` can be applied to individual `unsafe` functions to cause those +functions to be compiled to Thumb-mode code. ## Building Rust Programs @@ -69,16 +172,27 @@ build-std = ["core"] ``` Most of `core` should work as expected, with the following notes: -* If the target is not `*hf`, then floating-point operations are emulated in - software. + +* Floating-point operations are emulated in software unless LLVM is told to + enable FPU support (either by using an `eabihf` target, specifying a + `target-cpu` with FPU support, or using a `target-feature` to support for a + specific kind of FPU) * Integer division is also emulated in software on some targets, depending on - the CPU. -* Architectures prior to ARMv7 don't have atomic instructions. + the target, `target-cpu` and `target-feature`s. +* Older Arm architectures (e.g. Armv4, Armv5TE and Armv6-M) are limited to basic + [`load`][atomic-load] and [`store`][atomic-store] operations, and not more + advanced operations like [`fetch_add`][fetch-add] or + [`compare_exchange`][compare-exchange]. `alloc` is also supported, as long as you provide your own global allocator. Rust programs are output as ELF files. +[atomic-load]: https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU32.html#method.load +[atomic-store]: https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU32.html#method.store +[fetch-add]: https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU32.html#method.fetch_add +[compare-exchange]: https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU32.html#method.compare_exchange + ## Testing This is a cross-compiled target that you will need to emulate during testing. diff --git a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md index 1bb2c677b0809..2e82753586261 100644 --- a/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md +++ b/src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md @@ -11,48 +11,69 @@ applications on AArch64 Windows 11. See + and . +* Assembly for AArch64 might be reusable for Arm64EC, but there are many + caveats. For full details see [Microsoft's documentation on the Arm64EC ABI](https://learn.microsoft.com/en-us/windows/arm/arm64ec-abi) + but in brief: + - Arm64EC uses a subset of AArch64 registers. + - Arm64EC uses a different name mangling scheme than AArch64. + - Arm64EC requires entry and exit thunks be generated for some functions. + - Indirect calls must be done via a call checker. + - Control Flow Guard and stack checks use different functions than AArch64. ## Building the target You can build Rust with support for the targets by adding it to the `target` -list in `config.toml` and disabling `std`: +list in `config.toml`: ```toml [build] target = [ "arm64ec-pc-windows-msvc" ] - -[target.arm64ec-pc-windows-msvc] -no-std = true ``` ## Building Rust programs Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you will either need to build Rust with the target enabled (see -"Building the target" above), or build your own copy of `core` by using -`build-std` or similar. +"Building the target" above), or build your own copy using `build-std` or +similar. ## Testing Tests can be run on AArch64 Windows 11 devices. -Since this is a `no_std` target, the Rust test suite is not supported. - ## Cross-compilation toolchains and C code -C code can be built using the Arm64-targetting MSVC toolchain. +C code can be built using the Arm64-targeting MSVC or Clang toolchain. To compile: diff --git a/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md b/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md index 32d3440f1dc98..e7e3fd01c4dc7 100644 --- a/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md +++ b/src/doc/rustc/src/platform-support/armeb-unknown-linux-gnueabi.md @@ -1,22 +1,22 @@ # armeb-unknown-linux-gnueabi **Tier: 3** -Target for cross-compiling Linux user-mode applications targeting the ARM BE8 architecture. +Target for cross-compiling Linux user-mode applications targeting the Arm BE8 architecture. ## Overview -BE8 architecture retains the same little-endian ordered code-stream used by conventional little endian ARM systems, however the data accesses are in big-endian. BE8 is used primarily in high-performance networking applications where the ability to read packets in their native "Network Byte Order" is important (many network protocols transmit data in big-endian byte order for their wire formats). +BE8 architecture retains the same little-endian ordered code-stream used by conventional little endian Arm systems, however the data accesses are in big-endian. BE8 is used primarily in high-performance networking applications where the ability to read packets in their native "Network Byte Order" is important (many network protocols transmit data in big-endian byte order for their wire formats). ## History -BE8 architecture is the default big-endian architecture for ARM since [ARMv6](https://developer.arm.com/documentation/101754/0616/armlink-Reference/armlink-Command-line-Options/--be8?lang=en). It's predecessor, used for ARMv4 and ARMv5 devices was [BE32](https://developer.arm.com/documentation/dui0474/j/linker-command-line-options/--be32). On ARMv6 architecture, endianness can be configured via [system registers](https://developer.arm.com/documentation/ddi0290/g/unaligned-and-mixed-endian-data-access-support/mixed-endian-access-support/interaction-between-the-bus-protocol-and-the-core-endianness). However, BE32 was withdrawn for [ARMv7](https://developer.arm.com/documentation/ddi0406/cb/Appendixes/Deprecated-and-Obsolete-Features/Obsolete-features/Support-for-BE-32-endianness-model) onwards. +BE8 architecture is the default big-endian architecture for Arm since [Armv6](https://developer.arm.com/documentation/101754/0616/armlink-Reference/armlink-Command-line-Options/--be8?lang=en). It's predecessor, used for Armv4 and Armv5 devices was [BE32](https://developer.arm.com/documentation/dui0474/j/linker-command-line-options/--be32). On Armv6 architecture, endianness can be configured via [system registers](https://developer.arm.com/documentation/ddi0290/g/unaligned-and-mixed-endian-data-access-support/mixed-endian-access-support/interaction-between-the-bus-protocol-and-the-core-endianness). However, BE32 was withdrawn for [Armv7](https://developer.arm.com/documentation/ddi0406/cb/Appendixes/Deprecated-and-Obsolete-Features/Obsolete-features/Support-for-BE-32-endianness-model) onwards. ## Target Maintainers * [@WorksButNotTested](https://github.com/WorksButNotTested) ## Requirements -The target is cross-compiled. This target supports `std` in the normal way (indeed only nominal changes are required from the standard ARM configuration). +The target is cross-compiled. This target supports `std` in the normal way (indeed only nominal changes are required from the standard Arm configuration). ## Target definition -The target definition can be seen [here](https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec/armeb_unknown_linux_gnueabi.rs). In particular, it should be noted that the `features` specify that this target is built for the ARMv8 core. Though this can likely be modified as required. +The target definition can be seen [here](https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec/armeb_unknown_linux_gnueabi.rs). In particular, it should be noted that the `features` specify that this target is built for the Armv8 core. Though this can likely be modified as required. ## Building the target Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this target. diff --git a/src/doc/rustc/src/platform-support/armv4t-none-eabi.md b/src/doc/rustc/src/platform-support/armv4t-none-eabi.md index 29c47db8351c7..f4c8dd46f1d03 100644 --- a/src/doc/rustc/src/platform-support/armv4t-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv4t-none-eabi.md @@ -2,12 +2,12 @@ Tier 3 -Bare-metal target for any cpu in the ARMv4T architecture family, supporting -ARM/Thumb code interworking (aka `a32`/`t32`), with ARM code as the default code +Bare-metal target for any cpu in the Armv4T architecture family, supporting +ARM/Thumb code interworking (aka `A32`/`T32`), with ARM code as the default code generation. In particular this supports the Game Boy Advance (GBA), but there's nothing -GBA-specific with this target, so any ARMv4T device should work fine. +GBA-specific with this target, so any Armv4T device should work fine. See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all `arm-none-eabi` targets. diff --git a/src/doc/rustc/src/platform-support/armv5te-none-eabi.md b/src/doc/rustc/src/platform-support/armv5te-none-eabi.md index 37284ba720997..41621e070bb48 100644 --- a/src/doc/rustc/src/platform-support/armv5te-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv5te-none-eabi.md @@ -2,11 +2,11 @@ **Tier: 3** -Bare-metal target for any cpu in the ARMv5TE architecture family, supporting -ARM/Thumb code interworking (aka `a32`/`t32`), with `a32` code as the default code +Bare-metal target for any cpu in the Armv5TE architecture family, supporting +ARM/Thumb code interworking (aka `A32`/`T32`), with `A32` code as the default code generation. -The `thumbv5te-none-eabi` target is the same as this one, but the instruction set defaults to `t32`. +The `thumbv5te-none-eabi` target is the same as this one, but the instruction set defaults to `T32`. See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all `arm-none-eabi` targets. diff --git a/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md b/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md index 540e5a4af938e..160986aeae94f 100644 --- a/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md +++ b/src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md @@ -2,7 +2,7 @@ **Tier: 3** -The Nintendo 3DS platform, which has an ARMv6K processor, and its associated +The Nintendo 3DS platform, which has an Armv6k processor, and its associated operating system (`horizon`). Rust support for this target is not affiliated with Nintendo, and is not derived diff --git a/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md b/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md index e351ea0013003..f22a20835c1af 100644 --- a/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md +++ b/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabi.md @@ -2,7 +2,7 @@ **Tier: 3** -This target supports ARMv7 softfloat CPUs and uses the uclibc-ng standard library. This is a common configuration on many consumer routers (e.g., Netgear R7000, Asus RT-AC68U). +This target supports Armv7-A softfloat CPUs and uses the uclibc-ng standard library. This is a common configuration on many consumer routers (e.g., Netgear R7000, Asus RT-AC68U). ## Target maintainers @@ -16,7 +16,7 @@ This target supports host tools and std. ## Building the target -You will need to download or build a `'C'` cross toolchain that targets ARMv7 softfloat and that uses the uclibc-ng standard library. If your target hardware is something like a router or an embedded device, keep in mind that manufacturer supplied SDKs for this class of CPU could be outdated and potentially unsuitable for bootstrapping rust. +You will need to download or build a `'C'` cross toolchain that targets Armv7-A softfloat and that uses the uclibc-ng standard library. If your target hardware is something like a router or an embedded device, keep in mind that manufacturer supplied SDKs for this class of CPU could be outdated and potentially unsuitable for bootstrapping rust. [Here](https://github.com/lancethepants/tomatoware-toolchain) is a sample toolchain that is built using [buildroot](https://buildroot.org/). It uses modern toolchain components, older thus universal kernel headers (2.6.36.4), and is used for a project called [Tomatoware](https://github.com/lancethepants/tomatoware). This toolchain is patched so that its sysroot is located at /mmc (e.g., /mmc/bin, /mmc/lib, /mmc/include). This is useful in scenarios where the root filesystem is read-only but you are able attach external storage loaded with user applications. Tomatoware is an example of this that even allows you to run various compilers and developer tools natively on the target device. @@ -46,7 +46,7 @@ The following assumes you are using the Tomatoware toolchain and environment. Ad ### Native compilation -Since this target supports host tools, you can natively build rust applications directly on your target device. This can be convenient because it removes the complexities of cross compiling and you can immediately test and deploy your binaries. One downside is that compiling on your ARMv7 CPU will probably be much slower than cross compilation on your x86 machine. +Since this target supports host tools, you can natively build rust applications directly on your target device. This can be convenient because it removes the complexities of cross compiling and you can immediately test and deploy your binaries. One downside is that compiling on your Armv7-A CPU will probably be much slower than cross compilation on your x86 machine. To setup native compilation: diff --git a/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md b/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md index 1f029406367a2..1c8acc09c7746 100644 --- a/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md +++ b/src/doc/rustc/src/platform-support/armv7-unknown-linux-uclibceabihf.md @@ -2,7 +2,7 @@ **Tier: 3** -This tier supports the ARMv7 processor running a Linux kernel and uClibc-ng standard library. It provides full support for rust and the rust standard library. +This tier supports the Armv7-A processor running a Linux kernel and uClibc-ng standard library. It provides full support for rust and the rust standard library. ## Designated Developers diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md index 670cead9e006b..5f0dc6a7115b9 100644 --- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md @@ -2,7 +2,7 @@ **Tier: 2** -Bare-metal target for CPUs in the ARMv7-R architecture family, supporting +Bare-metal target for CPUs in the Armv7-R architecture family, supporting dual ARM/Thumb mode, with ARM mode as the default. Processors in this family include the [Arm Cortex-R4, 5, 7, and 8][cortex-r]. diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md index 588e5d7c99449..6f80a06020ff7 100644 --- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md +++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md @@ -2,7 +2,7 @@ **Tier: 3** -Bare-metal target for CPUs in the ARMv8-R architecture family, supporting +Bare-metal target for CPUs in the Armv8-R architecture family, supporting dual ARM/Thumb mode, with ARM mode as the default. Processors in this family include the Arm [Cortex-R52][cortex-r52] diff --git a/src/doc/rustc/src/platform-support/netbsd.md b/src/doc/rustc/src/platform-support/netbsd.md index 3891d6d3148da..ef9337befa643 100644 --- a/src/doc/rustc/src/platform-support/netbsd.md +++ b/src/doc/rustc/src/platform-support/netbsd.md @@ -13,7 +13,7 @@ are currently defined running NetBSD: | Target name | NetBSD Platform | |--------------------------------|-----------------| -| `amd64-unknown-netbsd` | [amd64 / x86_64 systems](https://wiki.netbsd.org/ports/amd64/) | +| `x86_64-unknown-netbsd` | [amd64 / x86_64 systems](https://wiki.netbsd.org/ports/amd64/) | | `armv7-unknown-netbsd-eabihf` | [32-bit ARMv7 systems with hard-float](https://wiki.netbsd.org/ports/evbarm/) | | `armv6-unknown-netbsd-eabihf` | [32-bit ARMv6 systems with hard-float](https://wiki.netbsd.org/ports/evbarm/) | | `aarch64-unknown-netbsd` | [64-bit ARM systems, little-endian](https://wiki.netbsd.org/ports/evbarm/) | @@ -22,7 +22,7 @@ are currently defined running NetBSD: | `i686-unknown-netbsd` | [32-bit i386 with SSE](https://wiki.netbsd.org/ports/i386/) | | `mipsel-unknown-netbsd` | [32-bit mips, requires mips32 cpu support](https://wiki.netbsd.org/ports/evbmips/) | | `powerpc-unknown-netbsd` | [Various 32-bit PowerPC systems, e.g. MacPPC](https://wiki.netbsd.org/ports/macppc/) | -| `riscv64gc-unknown-netbsd` | [64-bit RISC-V](https://wiki.netbsd.org/ports/riscv/) +| `riscv64gc-unknown-netbsd` | [64-bit RISC-V](https://wiki.netbsd.org/ports/riscv/) | | `sparc64-unknown-netbsd` | [Sun UltraSPARC systems](https://wiki.netbsd.org/ports/sparc64/) | All use the "native" `stdc++` library which goes along with the natively @@ -43,7 +43,7 @@ bug reporting system. ## Requirements -The `amd64-unknown-netbsd` artifacts is being distributed by the +The `x86_64-unknown-netbsd` artifacts is being distributed by the rust project. The other targets are built by the designated developers (see above), @@ -95,7 +95,7 @@ capable systems we build and test `firefox` (amd64, i386, aarch64). ## Building Rust programs -Rust ships pre-compiled artifacts for the `amd64-unknown-netbsd` +Rust ships pre-compiled artifacts for the `x86_64-unknown-netbsd` target. For the other systems mentioned above, using the `pkgsrc` route is diff --git a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md index a6246fa3bd804..ed55bcf4f35e5 100644 --- a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md +++ b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md @@ -1,6 +1,6 @@ # \*-pc-windows-gnullvm -**Tier: 3** +**Tier: 2 (without host tools)** Windows targets similar to `*-pc-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of GCC/Binutils. @@ -12,38 +12,52 @@ Target triples available so far: ## Target maintainers - [@mati865](https://github.com/mati865) +- [@thomcc](https://github.com/thomcc) ## Requirements -The easiest way to obtain these targets is cross-compilation but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend. +The easiest way to obtain these targets is cross-compilation, but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend. Std support is expected to be on pair with `*-pc-windows-gnu`. Binaries for this target should be at least on pair with `*-pc-windows-gnu` in terms of requirements and functionality. Those targets follow Windows calling convention for `extern "C"`. -Like with any other Windows target created binaries are in PE format. +Like with any other Windows target, created binaries are in PE format. ## Building the target -For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring cross compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors. -Native bootstrapping builds require rather fragile hacks until host artifacts are available so I won't describe them here. +These targets can be easily cross-compiled +using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain or [MSYS2 CLANG*](https://www.msys2.org/docs/environments/) environments. +Just fill `[target.*]` sections for both build and resulting compiler and set installation prefix in `config.toml`. +Then run `./x.py install`. +In my case I had ran `./x.py install --host x86_64-pc-windows-gnullvm --target x86_64-pc-windows-gnullvm` inside MSYS2 MINGW64 shell +so `x86_64-pc-windows-gnu` was my build toolchain. + +Native bootstrapping is doable in two ways: +- cross-compile gnullvm host toolchain and use it as build toolchain for the next build, +- copy libunwind libraries and rename them to mimic libgcc like here: https://github.com/msys2/MINGW-packages/blob/68e640756df2df6df6afa60f025e3f936e7b977c/mingw-w64-rust/PKGBUILD#L108-L109, stage0 compiler will be mostly broken but good enough to build the next stage. + +The second option might stop working anytime, so it's not recommended. ## Building Rust programs -Rust does not yet ship pre-compiled artifacts for this target. To compile for -this target, you will either need to build Rust with the target enabled (see -"Building the target" above), or build your own copy of `core` by using -`build-std` or similar. +Rust does ship a pre-compiled std library for those targets. +That means one can easily cross-compile for those targets from other hosts if C proper toolchain is installed. + +Alternatively full toolchain can be built as described in the previous section. ## Testing Created binaries work fine on Windows or Wine using native hardware. Testing AArch64 on x86_64 is problematic though and requires spending some time with QEMU. -Once these targets bootstrap themselves on native hardware they should pass Rust testsuite. +Most of x86_64 testsuite does pass when cross-compiling, +with exception for `rustdoc` and `ui-fulldeps` that fail with and error regarding a missing library, +they do pass in native builds though. +The only failing test is std's `process::tests::test_proc_thread_attributes` for unknown reason. ## Cross-compilation toolchains and C code -Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM based C toolchains are used. +Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM-based C toolchains are used. Those include: - [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) - [MSYS2 with CLANG* environment](https://www.msys2.org/docs/environments) diff --git a/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md new file mode 100644 index 0000000000000..9a27a568b5703 --- /dev/null +++ b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md @@ -0,0 +1,38 @@ +# `riscv32{i,im,ima,imc,imac,imafc}-unknown-none-elf` + +**Tier: 2** + +Bare-metal target for RISC-V CPUs with the RV32I, RV32IM, RV32IMC, RV32IMAFC and RV32IMAC ISAs. + +**Tier: 3** + +Bare-metal target for RISC-V CPUs with the RV32IMA ISA. + +## Target maintainers + +* Rust Embedded Working Group, [RISC-V team](https://github.com/rust-embedded/wg#the-risc-v-team) + +## Requirements + +The target is cross-compiled, and uses static linking. No external toolchain +is required and the default `rust-lld` linker works, but you must specify +a linker script. The [`riscv-rt`] crate provides a suitable one. The +[`riscv-rust-quickstart`] repository gives an example of an RV32 project. + +[`riscv-rt`]: https://crates.io/crates/riscv-rt +[`riscv-rust-quickstart`]: https://github.com/riscv-rust/riscv-rust-quickstart + +## Building the target + +This target is included in Rust and can be installed via `rustup`. + +## Testing + +This is a cross-compiled `no-std` target, which must be run either in a simulator +or by programming them onto suitable hardware. It is not possible to run the +Rust test-suite on this target. + +## Cross-compilation toolchains and C code + +This target supports C code. If interlinking with C or C++, you may need to use +`riscv64-unknown-elf-gcc` as a linker instead of `rust-lld`. diff --git a/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md deleted file mode 100644 index edfe07fc0537b..0000000000000 --- a/src/doc/rustc/src/platform-support/riscv32i-unknown-none-elf.md +++ /dev/null @@ -1 +0,0 @@ -riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md deleted file mode 100644 index edfe07fc0537b..0000000000000 --- a/src/doc/rustc/src/platform-support/riscv32im-unknown-none-elf.md +++ /dev/null @@ -1 +0,0 @@ -riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md deleted file mode 100644 index 739b12bad8b47..0000000000000 --- a/src/doc/rustc/src/platform-support/riscv32imac-unknown-none-elf.md +++ /dev/null @@ -1,34 +0,0 @@ -# `riscv32{i,im,imc,imac,imafc}-unknown-none-elf` - -**Tier: 2** - -Bare-metal target for RISC-V CPUs with the RV32I, RV32IM, RV32IMC, RV32IMAFC and RV32IMAC ISAs. - -## Target maintainers - -* Rust Embedded Working Group, [RISC-V team](https://github.com/rust-embedded/wg#the-risc-v-team) - -## Requirements - -The target is cross-compiled, and uses static linking. No external toolchain -is required and the default `rust-lld` linker works, but you must specify -a linker script. The [`riscv-rt`] crate provides a suitable one. The -[`riscv-rust-quickstart`] repository gives an example of an RV32 project. - -[`riscv-rt`]: https://crates.io/crates/riscv-rt -[`riscv-rust-quickstart`]: https://github.com/riscv-rust/riscv-rust-quickstart - -## Building the target - -This target is included in Rust and can be installed via `rustup`. - -## Testing - -This is a cross-compiled `no-std` target, which must be run either in a simulator -or by programming them onto suitable hardware. It is not possible to run the -Rust test-suite on this target. - -## Cross-compilation toolchains and C code - -This target supports C code. If interlinking with C or C++, you may need to use -`riscv64-unknown-elf-gcc` as a linker instead of `rust-lld`. diff --git a/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md deleted file mode 100644 index edfe07fc0537b..0000000000000 --- a/src/doc/rustc/src/platform-support/riscv32imafc-unknown-none-elf.md +++ /dev/null @@ -1 +0,0 @@ -riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md deleted file mode 100644 index edfe07fc0537b..0000000000000 --- a/src/doc/rustc/src/platform-support/riscv32imc-unknown-none-elf.md +++ /dev/null @@ -1 +0,0 @@ -riscv32imac-unknown-none-elf.md diff --git a/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md new file mode 100644 index 0000000000000..f4ed6201bbdc9 --- /dev/null +++ b/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md @@ -0,0 +1,62 @@ +# `thumbv6m-none-eabi` + +**Tier: 2** + +Bare-metal target for CPUs in the [Armv6-M] architecture family, supporting a +subset of the [T32 ISA][t32-isa]. + +Processors in this family include the: + +* [Arm Cortex-M0][cortex-m0] +* [Arm Cortex-M0+][cortex-m0plus] +* [Arm Cortex-M1][cortex-m1] + +See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all +`arm-none-eabi` targets. + +This target uses the soft-float ABI: functions which take `f32` or `f64` as +arguments will have those values packed into integer registers. This is the +only option because there is no FPU support in [Armv6-M]. + +[t32-isa]: https://developer.arm.com/Architectures/T32%20Instruction%20Set%20Architecture +[Armv6-M]: https://developer.arm.com/documentation/ddi0419/latest/ +[cortex-m0]: https://developer.arm.com/Processors/Cortex-M0 +[cortex-m0plus]: https://developer.arm.com/Processors/Cortex-M0+ +[cortex-m1]: https://developer.arm.com/Processors/Cortex-M1 + +## Target maintainers + +* [Rust Embedded Devices Working Group Cortex-M + Team](https://github.com/rust-embedded), `cortex-m@teams.rust-embedded.org` + +## Target CPU and Target Feature options + +See [the bare-metal Arm +docs](arm-none-eabi.md#target-cpu-and-target-feature-options) for details on how +to use these flags. + +### Table of supported CPUs + +| CPU | FPU | Target CPU | Target Features | +| ---------- | --- | --------------- | --------------------- | +| Cortex-M0 | No | `cortex-m0` | None | +| Cortex-M0+ | No | `cortex-m0plus` | None | +| Cortex-M1 | No | `cortex-m1` | None | + +### Arm Cortex-M0 + +The target CPU option is `cortex-m0`. + +There are no relevant feature flags, and the FPU is not available. + +### Arm Cortex-M0+ + +The target CPU option is `cortex-m0plus`. + +There are no relevant feature flags, and the FPU is not available. + +### Arm Cortex-M1 + +The target CPU option is `cortex-m1`. + +There are no relevant feature flags, and the FPU is not available. diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md new file mode 100644 index 0000000000000..f25ef0383b185 --- /dev/null +++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md @@ -0,0 +1,74 @@ +# `thumbv7em-none-eabi` and `thumbv7em-none-eabihf` + +**Tier: 2** + +Bare-metal target for CPUs in the [Armv7E-M] architecture family, supporting a +subset of the [T32 ISA][t32-isa]. + +Processors in this family include the: + +* [Arm Cortex-M4][cortex-m4] and Arm Cortex-M4F +* [Arm Cortex-M7][cortex-m7] and Arm Cortex-M7F + +See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all +`arm-none-eabi` targets, in particular the difference between the `eabi` and +`eabihf` ABI. + +[t32-isa]: https://developer.arm.com/Architectures/T32%20Instruction%20Set%20Architecture +[Armv7E-M]: https://developer.arm.com/documentation/ddi0403/latest/ +[cortex-m4]: https://developer.arm.com/Processors/Cortex-M4 +[cortex-m7]: https://developer.arm.com/Processors/Cortex-M7 + +## Target maintainers + +* [Rust Embedded Devices Working Group Cortex-M + Team](https://github.com/rust-embedded), `cortex-m@teams.rust-embedded.org` + +## Target CPU and Target Feature options + +See [the bare-metal Arm +docs](arm-none-eabi.md#target-cpu-and-target-feature-options) for details on how +to use these flags. + +### Table of supported CPUs for `thumbv7em-none-eabi` + +| CPU | FPU | DSP | Target CPU | Target Features | +| ---------- | --- | --- | ----------- | --------------- | +| Any | No | Yes | None | None | +| Cortex-M4 | No | Yes | `cortex-m4` | `+soft-float` | +| Cortex-M4F | SP | Yes | `cortex-m4` | None | +| Cortex-M7 | No | Yes | `cortex-m7` | `+soft-float` | +| Cortex-M7F | SP | Yes | `cortex-m7` | `-fp64` | +| Cortex-M7F | DP | Yes | `cortex-m7` | None | + +### Table of supported CPUs for `thumbv7em-none-eabihf` + +| CPU | FPU | DSP | Target CPU | Target Features | +| ---------- | --- | --- | ----------- | --------------- | +| Any | SP | Yes | None | None | +| Cortex-M4F | SP | Yes | `cortex-m4` | None | +| Cortex-M7F | SP | Yes | `cortex-m7` | `-fp64` | +| Cortex-M7F | DP | Yes | `cortex-m7` | None | + +### Arm Cortex-M4 and Arm Cortex-M4F + +The target CPU is `cortex-m4`. + +* All Cortex-M4 have DSP extensions + * support is controlled by the `dsp` *target-feature* + * enabled by default with this *target* +* Cortex-M4F has a single precision FPU + * support is enabled by default with this *target-cpu* + * disable support using the `+soft-float` feature (`eabi` only) + +### Arm Cortex-M7 and Arm Cortex-M7F + +The target CPU is `cortex-m7`. + +* All Cortex-M7 have DSP extensions + * support is controlled by the `dsp` *target-feature* + * enabled by default with this *target* +* Cortex-M7F have either a single-precision or double-precision FPU + * double-precision support is enabled by default with this *target-cpu* + * opt-out by using the `-f64` *target-feature* + * disable support entirely using the `+soft-float` feature (`eabi` only) diff --git a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md new file mode 100644 index 0000000000000..b258033bb0fa7 --- /dev/null +++ b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md @@ -0,0 +1,44 @@ +# `thumbv7m-none-eabi` + +**Tier: 2** + +Bare-metal target for CPUs in the [Armv7-M] architecture family, supporting a +subset of the [T32 ISA][t32-isa]. + +Processors in this family include the: + +* [Arm Cortex-M3][cortex-m3] + +See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all +`arm-none-eabi` targets. + +This target uses the soft-float ABI: functions which take `f32` or `f64` as +arguments will have those values packed into integer registers. This is the +only option because there is no FPU support in [Armv7-M]. + +[t32-isa]: https://developer.arm.com/Architectures/T32%20Instruction%20Set%20Architecture +[Armv7-M]: https://developer.arm.com/documentation/ddi0403/latest/ +[cortex-m3]: https://developer.arm.com/Processors/Cortex-M3 + +## Target maintainers + +* [Rust Embedded Devices Working Group Cortex-M + Team](https://github.com/rust-embedded), `cortex-m@teams.rust-embedded.org` + +## Target CPU and Target Feature options + +See [the bare-metal Arm +docs](arm-none-eabi.md#target-cpu-and-target-feature-options) for details on how +to use these flags. + +### Table of supported CPUs + +| CPU | FPU | Target CPU | Target Features | +| ---------- | --- | ----------- | --------------------- | +| Cortex-M3 | No | `cortex-m3` | None | + +### Arm Cortex-M3 + +The target CPU option is `cortex-m3`. + +There are no relevant feature flags, and the FPU is not available. diff --git a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md new file mode 100644 index 0000000000000..0ae4e3e94bd8c --- /dev/null +++ b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md @@ -0,0 +1,44 @@ +# `thumbv8m.base-none-eabi` + +**Tier: 2** + +Bare-metal target for CPUs in the Baseline [Armv8-M] architecture family, +supporting a subset of the [T32 ISA][t32-isa]. + +Processors in this family include the: + +* [Arm Cortex-M23][cortex-m23] + +See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all +`arm-none-eabi` targets. + +This target uses the soft-float ABI: functions which take `f32` or `f64` as +arguments will have those values packed into integer registers. This is the +only option because there is no FPU support in [Armv8-M] Baseline. + +[t32-isa]: https://developer.arm.com/Architectures/T32%20Instruction%20Set%20Architecture +[Armv8-M]: https://developer.arm.com/documentation/ddi0553/latest/ +[cortex-m23]: https://developer.arm.com/Processors/Cortex-M23 + +## Target maintainers + +* [Rust Embedded Devices Working Group Cortex-M + Team](https://github.com/rust-embedded), `cortex-m@teams.rust-embedded.org` + +## Target CPU and Target Feature options + +See [the bare-metal Arm +docs](arm-none-eabi.md#target-cpu-and-target-feature-options) for details on how +to use these flags. + +### Table of supported CPUs + +| CPU | FPU | Target CPU | Target Features | +| ----------- | --- | ------------ | --------------------- | +| Cortex-M23 | No | `cortex-m23` | None | + +### Arm Cortex-M23 + +The target CPU option is `cortex-m23`. + +There are no relevant feature flags, and the FPU is not available. diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md new file mode 100644 index 0000000000000..4e696f9c30461 --- /dev/null +++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md @@ -0,0 +1,135 @@ +# `thumbv8m.main-none-eabi` and `thumbv8m.main-none-eabihf` + +**Tier: 2** + +Bare-metal target for CPUs in the Mainline [Armv8-M] architecture family, +supporting a subset of the [T32 ISA][t32-isa]. + +Processors in this family include the: + +* [Arm Cortex-M33][cortex-m33] +* [Arm Cortex-M35P][cortex-m35p] +* [Arm Cortex-M55][cortex-m55] +* [Arm Cortex-M85][cortex-m85] + +See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all +`arm-none-eabi` targets, in particular the difference between the `eabi` and +`eabihf` ABI. + +[t32-isa]: https://developer.arm.com/Architectures/T32%20Instruction%20Set%20Architecture +[Armv8-M]: https://developer.arm.com/documentation/ddi0553/latest/ +[cortex-m33]: https://developer.arm.com/Processors/Cortex-M33 +[cortex-m35p]: https://developer.arm.com/Processors/Cortex-M35P +[cortex-m55]: https://developer.arm.com/Processors/Cortex-M55 +[cortex-m85]: https://developer.arm.com/Processors/Cortex-M85 + +## Target maintainers + +* [Rust Embedded Devices Working Group Cortex-M + Team](https://github.com/rust-embedded), `cortex-m@teams.rust-embedded.org` + +## Target CPU and Target Feature options + +See [the bare-metal Arm +docs](arm-none-eabi.md#target-cpu-and-target-feature-options) for details on how +to use these flags. + +### Table of supported CPUs for `thumbv8m.main-none-eabi` + +| CPU | FPU | DSP | MVE | Target CPU | Target Features | +| ----------- | --- | --- | --------- | ------------- | --------------------- | +| Unspecified | No | No | No | None | None | +| Cortex-M33 | No | No | No | `cortex-m33` | `+soft-float,-dsp` | +| Cortex-M33 | No | Yes | No | `cortex-m33` | `+soft-float` | +| Cortex-M33 | SP | No | No | `cortex-m33` | `-dsp` | +| Cortex-M33 | SP | Yes | No | `cortex-m33` | None | +| Cortex-M35P | No | No | No | `cortex-m35p` | `+soft-float,-dsp` | +| Cortex-M35P | No | Yes | No | `cortex-m35p` | `+soft-float` | +| Cortex-M35P | SP | No | No | `cortex-m35p` | `-dsp` | +| Cortex-M35P | SP | Yes | No | `cortex-m35p` | None | +| Cortex-M55 | No | Yes | No | `cortex-m55` | `+soft-float,-mve` | +| Cortex-M55 | DP | Yes | No | `cortex-m55` | `-mve` | +| Cortex-M55 | No | Yes | Int | `cortex-m55` | `+soft-float,-mve.fp` | +| Cortex-M55 | DP | Yes | Int | `cortex-m55` | `-mve.fp` | +| Cortex-M55 | DP | Yes | Int+Float | `cortex-m55` | None | +| Cortex-M85 | No | Yes | No | `cortex-m85` | `+soft-float,-mve` | +| Cortex-M85 | DP | Yes | No | `cortex-m85` | `-mve` | +| Cortex-M85 | No | Yes | Int | `cortex-m85` | `+soft-float,-mve.fp` | +| Cortex-M85 | DP | Yes | Int | `cortex-m85` | `-mve.fp` | +| Cortex-M85 | DP | Yes | Int+Float | `cortex-m85` | None | + +### Table of supported CPUs for `thumbv8m.main-none-eabihf` + +| CPU | FPU | DSP | MVE | Target CPU | Target Features | +| ----------- | --- | --- | --------- | ------------- | --------------------- | +| Unspecified | SP | No | No | None | None | +| Cortex-M33 | SP | No | No | `cortex-m33` | `-dsp` | +| Cortex-M33 | SP | Yes | No | `cortex-m33` | None | +| Cortex-M33P | SP | No | No | `cortex-m35p` | `-dsp` | +| Cortex-M33P | SP | Yes | No | `cortex-m35p` | None | +| Cortex-M55 | DP | Yes | No | `cortex-m55` | `-mve` | +| Cortex-M55 | DP | Yes | Int | `cortex-m55` | `-mve.fp` | +| Cortex-M55 | DP | Yes | Int+Float | `cortex-m55` | None | +| Cortex-M85 | DP | Yes | No | `cortex-m85` | `-mve` | +| Cortex-M85 | DP | Yes | Int | `cortex-m85` | `-mve.fp` | +| Cortex-M85 | DP | Yes | Int+Float | `cortex-m85` | None | + +### Arm Cortex-M33 + +The target CPU is `cortex-m33`. + +* Has optional DSP extensions + * support is controlled by the `dsp` *target-feature* + * enabled by default with this *target-cpu* +* Has an optional single precision FPU + * support is enabled by default with this *target-cpu* + * disable support using the `+soft-float` feature (`eabi` only) + +### Arm Cortex-M35P + +The target CPU is `cortex-m35p`. + +* Has optional DSP extensions + * support is controlled by the `dsp` *target-feature* + * enabled by default with this *target-cpu* +* Has an optional single precision FPU + * support is enabled by default with this *target-cpu* + * disable support using the `+soft-float` feature (`eabi` only) + +### Arm Cortex-M55 + +The target CPU is `cortex-m55`. + +* Has DSP extensions + * support is controlled by the `dsp` *target-feature* + * enabled by default with this *target-cpu* +* Has an optional double-precision FPU that also supports half-precision FP16 + values + * support is enabled by default with this *target-cpu* + * disable support using the `+soft-float` feature (`eabi` only) +* Has optional support for M-Profile Vector Extensions + * Also known as *Helium Technology* + * Available with only integer support, or both integer/float support + * The appropriate feature for the MVE is either `mve` (integer) or `mve.fp` + (float) + * `mve.fp` is enabled by default on this target CPU + * disable using `-mve.fp` (disable float MVE) or `-mve` (disable all MVE) + +### Arm Cortex-M85 + +The target CPU is `cortex-m85`. + +* Has DSP extensions + * support is controlled by the `dsp` *target-feature* + * enabled by default with this *target-cpu* +* Has an optional double-precision FPU that also supports half-precision FP16 + values + * support is enabled by default with this *target-cpu* + * disable support using the `+soft-float` feature (`eabi` only) +* Has optional support for M-Profile Vector Extensions + * Also known as *Helium Technology* + * Available with only integer support, or both integer/float support + * The appropriate feature for the MVE is either `mve` (integer) or `mve.fp` + (float) + * `mve.fp` is enabled by default on this target CPU + * disable using `-mve.fp` (disable float MVE) or `-mve` (disable all MVE) diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md index a1ca81d1fecb3..4faa198873536 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md @@ -59,7 +59,7 @@ languages compiled to WebAssembly, for example C/C++. Any ABI differences or mismatches are considered bugs that need to be fixed. By default the WASI targets in Rust ship in rustup with a precompiled copy of -[`wasi-libc`] meaning that a WebAssembly-targetting-Clang is not required to +[`wasi-libc`] meaning that a WebAssembly-targeting-Clang is not required to use the WASI targets from Rust. If there is no actual interoperation with C then `rustup target add wasm32-wasip1` is all that's needed to get started with WASI. @@ -73,19 +73,21 @@ be used instead. ## Building the target -To build this target a compiled version of [`wasi-libc`] is required to be -present at build time. This can be installed through -[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk) as well. This is the -configured with: +To build this target first acquire a copy of +[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22 +is the minimum needed. -```toml -[target.wasm32-wasip1] -wasi-root = ".../wasi-libc/sysroot" +Next configure the `WASI_SDK_PATH` environment variable to point to where this +is installed. For example: + +```text +export WASI_SDK_PATH=/path/to/wasi-sdk-22.0 ``` -Additionally users will need to enable LLD when building Rust from source as -LLVM's `wasm-ld` driver for LLD is required when linking WebAssembly code -together. +Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld` +driver for LLD is required when linking WebAssembly code together. Rust's build +system will automatically pick up any necessary binaries and programs from +`WASI_SDK_PATH`. ## Building Rust programs @@ -112,8 +114,10 @@ This target can be cross-compiled from any hosts. ## Testing -Currently the WASI target is not tested in rust-lang/rust CI. This means that -tests in the repository are not guaranteed to pass. This is theoretically -possibly by installing a standalone WebAssembly runtime and using it as a -"runner" for all tests, but there are various failures that will need to be -waded through to adjust tests to work on the WASI target. +This target is tested in rust-lang/rust CI on all merges. A subset of tests are +run in the `test-various` builder such as the UI tests and libcore tests. This +can be tested locally, for example, with: + +```text +./x.py test --target wasm32-wasip1 tests/ui +``` diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip2.md b/src/doc/rustc/src/platform-support/wasm32-wasip2.md index 4466d2c084053..a385600cc224b 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip2.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip2.md @@ -22,9 +22,34 @@ This target is cross-compiled. The target supports `std` fully. ## Platform requirements -The WebAssembly runtime should support the wasi preview 2 API set. +The WebAssembly runtime should support the wasi preview 2 API set. Runtimes also +are required to support components since this target outputs a component as +opposed to a core wasm module. As of the time of this writing Wasmtime 17 and +above is able to run this target natively with no extra flags. -This target is not a stable target. This means that there are only a few engines -which implement wasi preview 2, for example: +## Building the target -* Wasmtime - `-W component-model` +To build this target first acquire a copy of +[`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk/). At this time version 22 +is the minimum needed. + +Next configure the `WASI_SDK_PATH` environment variable to point to where this +is installed. For example: + +```text +export WASI_SDK_PATH=/path/to/wasi-sdk-22.0 +``` + +Next be sure to enable LLD when building Rust from source as LLVM's `wasm-ld` +driver for LLD is required when linking WebAssembly code together. Rust's build +system will automatically pick up any necessary binaries and programs from +`WASI_SDK_PATH`. + +## Testing + +This target is not tested in CI at this time. Locally it can be tested with a +`wasmtime` binary in `PATH` like so: + +```text +./x.py test --target wasm32-wasip2 tests/ui +``` diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index fe5cb529c2638..822f341b370af 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -131,6 +131,20 @@ This flag accepts the same values as `rustc --cfg`, and uses it to configure compilation. The example above uses `feature`, but any of the `cfg` values are acceptable. +## `--check-cfg`: check configuration flags + +This flag accepts the same values as `rustc --check-cfg`, and uses it to +check configuration flags. + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs --check-cfg='cfg(my_cfg, values("foo", "bar"))' +``` + +The example above check every well known names and values (`target_os`, `doc`, `test`, ...) +and check the values of `my_cfg`: `foo` and `bar`. + ## `--extern`: specify a dependency's location Using this flag looks like this: diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 7d573ac950d8e..c05077befdb3a 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -417,7 +417,7 @@ warning: 1 warning emitted This lint is **warn-by-default**. It detects explicit links that are the same as computed automatic links. -This usually means the explicit links are removeable. For example: +This usually means the explicit links are removable. For example: ```rust #![warn(rustdoc::redundant_explicit_links)] // note: unnecessary - warns by default. diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 41602dec44c4f..bdb55de8d634e 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -618,22 +618,6 @@ crate being documented (`foobar`) and a path to output the calls To scrape examples from test code, e.g. functions marked `#[test]`, then add the `--scrape-tests` flag. -### `--check-cfg`: check configuration flags - - * Tracking issue: [#82450](https://github.com/rust-lang/rust/issues/82450) - -This flag accepts the same values as `rustc --check-cfg`, and uses it to check configuration flags. - -Using this flag looks like this: - -```bash -$ rustdoc src/lib.rs -Z unstable-options \ - --check-cfg='cfg(feature, values("foo", "bar"))' -``` - -The example above check every well known names and values (`target_os`, `doc`, `test`, ...) -and check the values of `feature`: `foo` and `bar`. - ### `--generate-link-to-definition`: Generate links on types in source code * Tracking issue: [#89095](https://github.com/rust-lang/rust/issues/89095) diff --git a/src/doc/rustdoc/src/what-is-rustdoc.md b/src/doc/rustdoc/src/what-is-rustdoc.md index 7179ee0cf0370..cb4ec51caf208 100644 --- a/src/doc/rustdoc/src/what-is-rustdoc.md +++ b/src/doc/rustdoc/src/what-is-rustdoc.md @@ -34,6 +34,9 @@ the main page is located in `doc/lib/index.html`. If you open that up in a web browser, you will see a page with a search bar, and "Crate lib" at the top, with no contents. +You can also use `cargo doc` to generate documentation for the whole project. +See [Using rustdoc with Cargo](#using-rustdoc-with-cargo). + ## Configuring rustdoc There are two problems with this: first, why does it @@ -79,7 +82,13 @@ docs. Instead of the `rustdoc` command, we could have done this: $ cargo doc ``` -Internally, this calls out to `rustdoc` like this: +If you want `cargo` to automatically open the generated documentation, you can use: + +```bash +$ cargo doc --open +``` + +Internally, `cargo doc` calls out to `rustdoc` like this: ```bash $ rustdoc --crate-name docs src/lib.rs -o /docs/target/doc -L diff --git a/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md b/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md index 56c14b1638a6f..1a367b8274b96 100644 --- a/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md +++ b/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md @@ -168,4 +168,4 @@ render differently in this case: ``` `1.` and `2.` will be displayed as is in the rendered documentation (ie, `[a]` and `[b][c]`) -whereas `3.` and `4.` will be replaced by a link targetting `e` for `[d](e)` and `g` for `[f]`. +whereas `3.` and `4.` will be replaced by a link targeting `e` for `[d](e)` and `g` for `[f]`. diff --git a/src/doc/unstable-book/src/compiler-flags/codegen-options.md b/src/doc/unstable-book/src/compiler-flags/codegen-options.md index 31dfcdb199ab9..cc51554706d07 100644 --- a/src/doc/unstable-book/src/compiler-flags/codegen-options.md +++ b/src/doc/unstable-book/src/compiler-flags/codegen-options.md @@ -10,6 +10,10 @@ In addition to the stable set of linker flavors, the following unstable values a - `ptx`: use [`rust-ptx-linker`](https://github.com/denzp/rust-ptx-linker) for Nvidia NVPTX GPGPU support. - `bpf`: use [`bpf-linker`](https://github.com/alessandrod/bpf-linker) for eBPF support. +- `llbc`: for linking in llvm bitcode. Install the preview rustup components`llvm-bitcode-linker` + and `llvm-tools` to use as a self-contained linker by passing + `-Zunstable-options -Clink-self-contained=+linker` together with `-Clinker-flavor=llbc`. + Can currently only be used for Nvidia NVPTX targets (`nvptx64-nvidia-cuda`). Additionally, a set of more precise linker flavors also exists, for example allowing targets to declare that they use the LLD linker by default. The following values are currently unstable, and diff --git a/src/doc/unstable-book/src/compiler-flags/coverage-options.md b/src/doc/unstable-book/src/compiler-flags/coverage-options.md index 450573cc6c746..2127883355025 100644 --- a/src/doc/unstable-book/src/compiler-flags/coverage-options.md +++ b/src/doc/unstable-book/src/compiler-flags/coverage-options.md @@ -5,4 +5,13 @@ This option controls details of the coverage instrumentation performed by Multiple options can be passed, separated by commas. Valid options are: -- `branch` or `no-branch`: Enables or disables branch coverage instrumentation. +- `block`, `branch`, `mcdc`: + Sets the level of coverage instrumentation. + Setting the level will override any previously-specified level. + - `block` (default): + Blocks in the control-flow graph will be instrumented for coverage. + - `branch`: + In addition to block coverage, also enables branch coverage instrumentation. + - `mcdc`: + In addition to block and branch coverage, also enables MC/DC instrumentation. + (Branch coverage instrumentation may differ in some cases.) diff --git a/src/doc/unstable-book/src/compiler-flags/external-clangrt.md b/src/doc/unstable-book/src/compiler-flags/external-clangrt.md new file mode 100644 index 0000000000000..76b78d733e515 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/external-clangrt.md @@ -0,0 +1,6 @@ +# `external-clangrt` + +This option controls whether the compiler links in its own runtime library for +[sanitizers](./sanitizer.md). Passing this flag makes the compiler *not* link +its own library. For more information, see the section in the sanitizers doc on +[working with other languages.](./sanitizer.md#working-with-other-languages) 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 0000000000000..643fcf7c6d7e0 --- /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/compiler-flags/remap-path-scope.md b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md index 424f1128e3b5c..65219dc68e976 100644 --- a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md +++ b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md @@ -10,11 +10,9 @@ This flag accepts a comma-separated list of values and may be specified multiple - `macro` - apply remappings to the expansion of `std::file!()` macro. This is where paths in embedded panic messages come from - `diagnostics` - apply remappings to printed compiler diagnostics -- `unsplit-debuginfo` - apply remappings to debug information only when they are written to compiled executables or libraries, but not when they are in split debuginfo files -- `split-debuginfo` - apply remappings to debug information only when they are written to split debug information files, but not in compiled executables or libraries -- `split-debuginfo-path` - apply remappings to the paths pointing to split debug information files. Does nothing when these files are not generated. -- `object` - an alias for `macro,unsplit-debuginfo,split-debuginfo-path`. This ensures all paths in compiled executables or libraries are remapped, but not elsewhere. -- `all` and `true` - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. +- `debuginfo` - apply remappings to debug informations +- `object` - apply remappings to all paths in compiled executables or libraries, but not elsewhere. Currently an alias for `macro,debuginfo`. +- `all` - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`. ## Example ```sh diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index c8fd154a00ec9..72b44e002b4d4 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -45,6 +45,9 @@ To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=cfi`, `-Zsanitizer=dataflow`,`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`, `-Zsanitizer=memtag`, `-Zsanitizer=shadow-call-stack`, or `-Zsanitizer=thread`. You might also need the `--target` and `build-std` flags. +If you're working with other languages that are also instrumented with sanitizers, +you might need the `external-clangrt` flag. See the section on +[working with other languages](#working-with-other-languages). Example: ```shell @@ -853,6 +856,18 @@ functionality][build-std]. [build-std]: ../../cargo/reference/unstable.html#build-std +# Working with other languages + +Sanitizers rely on compiler runtime libraries to function properly. Rust links +in its own compiler runtime which might conflict with runtimes required by +languages such as C++. Since Rust's runtime doesn't always contain the symbols +required by C++ instrumented code, you might need to skip linking it so another +runtime can be linked instead. + +A separate unstable option `-Zexternal-clangrt` can be used to make rustc skip +linking the compiler runtime for the sanitizer. This will require you to link +in an external runtime, such as from clang instead. + # Build scripts and procedural macros Use of sanitizers together with build scripts and procedural macros is diff --git a/src/doc/unstable-book/src/compiler-flags/shell-argfiles.md b/src/doc/unstable-book/src/compiler-flags/shell-argfiles.md index 4f3c780972de5..9e765301206ed 100644 --- a/src/doc/unstable-book/src/compiler-flags/shell-argfiles.md +++ b/src/doc/unstable-book/src/compiler-flags/shell-argfiles.md @@ -8,4 +8,4 @@ arguments from argfiles specified with `@shell:`. Because this feature controls the parsing of input arguments, the `-Zshell-argfiles` flag must be present before the argument specifying the -shell-style arguemnt file. +shell-style argument file. diff --git a/src/doc/unstable-book/src/compiler-flags/ub-checks.md b/src/doc/unstable-book/src/compiler-flags/ub-checks.md new file mode 100644 index 0000000000000..528c868d7adf3 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/ub-checks.md @@ -0,0 +1,17 @@ +# `ub-checks` + +The tracking issue for this feature is: [#123499](https://github.com/rust-lang/rust/issues/123499). + +-------------------- + +The `-Zub-checks` compiler flag enables additional runtime checks that detect some causes of Undefined Behavior at runtime. +By default, `-Zub-checks` flag inherits the value of `-Cdebug-assertions`. + +All checks are generated on a best-effort basis; even if we have a check implemented for some cause of Undefined Behavior, it may be possible for the check to not fire. +If a dependency is compiled with `-Zub-checks=no` but the final binary or library is compiled with `-Zub-checks=yes`, UB checks reached by the dependency are likely to be optimized out. + +When `-Zub-checks` detects UB, a non-unwinding panic is produced. +That means that we will not unwind the stack and will not call any `Drop` impls, but we will execute the configured panic hook. +We expect that unsafe code has been written which relies on code not unwinding which may have UB checks inserted. +Ergo, an unwinding panic could easily turn works-as-intended UB into a much bigger problem. +Calling the panic hook theoretically has the same implications, but we expect that the standard library panic hook will be stateless enough to be always called, and that if a user has configured a panic hook that the hook may be very helpful to debugging the detected UB. diff --git a/src/doc/unstable-book/src/compiler-flags/wasm-c-abi.md b/src/doc/unstable-book/src/compiler-flags/wasm-c-abi.md new file mode 100644 index 0000000000000..138a98dbe3dcd --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/wasm-c-abi.md @@ -0,0 +1,10 @@ +# `wasm-c-abi` + +This option controls whether Rust uses the spec-compliant C ABI when compiling +for the `wasm32-unknown-unknown` target. + +This makes it possible to be ABI-compatible with all other spec-compliant Wasm +like Rusts `wasm32-wasi`. + +This compiler flag is perma-unstable, as it will be enabled by default in the +future with no option to fall back to the old non-spec-compliant ABI. diff --git a/src/doc/unstable-book/src/language-features/adt-const-params.md b/src/doc/unstable-book/src/language-features/adt-const-params.md new file mode 100644 index 0000000000000..208bf5e4c1533 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/adt-const-params.md @@ -0,0 +1,35 @@ +# `adt_const_params` + +The tracking issue for this feature is: [#95174] + +[#95174]: https://github.com/rust-lang/rust/issues/95174 + +------------------------ + +Allows for using more complex types for const parameters, such as structs or enums. + +```rust +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(ConstParamTy, PartialEq, Eq)] +enum Foo { + A, + B, + C, +} + +#[derive(ConstParamTy, PartialEq, Eq)] +struct Bar { + flag: bool, +} + +fn is_foo_a_and_bar_true() -> bool { + match (F, B.flag) { + (Foo::A, true) => true, + _ => false, + } +} +``` 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 59acbc73db461..43e11b6d57dcb 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`,
`i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`,
`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/doc/unstable-book/src/language-features/coroutines.md b/src/doc/unstable-book/src/language-features/coroutines.md index f8e5a22fbd5c8..9fb07594650f4 100644 --- a/src/doc/unstable-book/src/language-features/coroutines.md +++ b/src/doc/unstable-book/src/language-features/coroutines.md @@ -26,13 +26,13 @@ tweaks to the overall design. A syntactical example of a coroutine is: ```rust -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; fn main() { - let mut coroutine = || { + let mut coroutine = #[coroutine] || { yield 1; return "foo" }; @@ -48,7 +48,8 @@ fn main() { } ``` -Coroutines are closure-like literals which can contain a `yield` statement. The +Coroutines are closure-like literals which are annotated with `#[coroutine]` +and can contain a `yield` statement. The `yield` statement takes an optional expression of a value to yield out of the coroutine. All coroutine literals implement the `Coroutine` trait in the `std::ops` module. The `Coroutine` trait has one main method, `resume`, which @@ -58,13 +59,13 @@ An example of the control flow of coroutines is that the following example prints all numbers in order: ```rust -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::Coroutine; use std::pin::Pin; fn main() { - let mut coroutine = || { + let mut coroutine = #[coroutine] || { println!("2"); yield; println!("4"); @@ -78,9 +79,9 @@ fn main() { } ``` -At this time the main intended use case of coroutines is an implementation -primitive for async/await syntax, but coroutines will likely be extended to -ergonomic implementations of iterators and other primitives in the future. +At this time the main use case of coroutines is an implementation +primitive for `async`/`await` and `gen` syntax, but coroutines +will likely be extended to other primitives in the future. Feedback on the design and usage is always appreciated! ### The `Coroutine` trait @@ -163,14 +164,14 @@ which point all state is saved off in the coroutine and a value is returned. Let's take a look at an example to see what's going on here: ```rust -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::Coroutine; use std::pin::Pin; fn main() { let ret = "foo"; - let mut coroutine = move || { + let mut coroutine = #[coroutine] move || { yield 1; return ret }; @@ -183,7 +184,7 @@ fn main() { This coroutine literal will compile down to something similar to: ```rust -#![feature(arbitrary_self_types, coroutines, coroutine_trait)] +#![feature(arbitrary_self_types, coroutine_trait)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; diff --git a/src/doc/unstable-book/src/language-features/inline-const-pat.md b/src/doc/unstable-book/src/language-features/inline-const-pat.md index 5f0f7547a0a89..c6f54d79cfce8 100644 --- a/src/doc/unstable-book/src/language-features/inline-const-pat.md +++ b/src/doc/unstable-book/src/language-features/inline-const-pat.md @@ -2,8 +2,6 @@ The tracking issue for this feature is: [#76001] -See also [`inline_const`](inline-const.md) - ------ This feature allows you to use inline constant expressions in pattern position: diff --git a/src/doc/unstable-book/src/language-features/inline-const.md b/src/doc/unstable-book/src/language-features/inline-const.md deleted file mode 100644 index 7be70eed6cedc..0000000000000 --- a/src/doc/unstable-book/src/language-features/inline-const.md +++ /dev/null @@ -1,32 +0,0 @@ -# `inline_const` - -The tracking issue for this feature is: [#76001] - -See also [`inline_const_pat`](inline-const-pat.md) - ------- - -This feature allows you to use inline constant expressions. For example, you can -turn this code: - -```rust -# fn add_one(x: i32) -> i32 { x + 1 } -const MY_COMPUTATION: i32 = 1 + 2 * 3 / 4; - -fn main() { - let x = add_one(MY_COMPUTATION); -} -``` - -into this code: - -```rust -#![feature(inline_const)] - -# fn add_one(x: i32) -> i32 { x + 1 } -fn main() { - let x = add_one(const { 1 + 2 * 3 / 4 }); -} -``` - -[#76001]: https://github.com/rust-lang/rust/issues/76001 diff --git a/src/doc/unstable-book/src/language-features/postfix-match.md b/src/doc/unstable-book/src/language-features/postfix-match.md new file mode 100644 index 0000000000000..cd6b6a7442c5a --- /dev/null +++ b/src/doc/unstable-book/src/language-features/postfix-match.md @@ -0,0 +1,22 @@ +# `postfix-match` + +`postfix-match` adds the feature for matching upon values postfix +the expressions that generate the values. + +```rust,edition2021 +#![feature(postfix_match)] + +enum Foo { + Bar, + Baz +} + +fn get_foo() -> Foo { + Foo::Bar +} + +get_foo().match { + Foo::Bar => {}, + Foo::Baz => panic!(), +} +``` diff --git a/src/doc/unstable-book/src/library-features/async-fn-traits.md b/src/doc/unstable-book/src/library-features/async-fn-traits.md index e1c3f067e5b2b..a0edb3c7dd273 100644 --- a/src/doc/unstable-book/src/library-features/async-fn-traits.md +++ b/src/doc/unstable-book/src/library-features/async-fn-traits.md @@ -10,4 +10,4 @@ for creating custom closure-like types that return futures. [`AsyncFn*`]: ../../std/ops/trait.AsyncFn.html The main difference to the `Fn*` family of traits is that `AsyncFn` can return a future -that borrows from itself (`FnOnce::Output` has no lifetime parameters, while `AsyncFn::CallFuture` does). +that borrows from itself (`FnOnce::Output` has no lifetime parameters, while `AsyncFnMut::CallRefFuture` does). diff --git a/src/doc/unstable-book/src/the-unstable-book.md b/src/doc/unstable-book/src/the-unstable-book.md index 0f4fb405669a1..63134f7ae28c4 100644 --- a/src/doc/unstable-book/src/the-unstable-book.md +++ b/src/doc/unstable-book/src/the-unstable-book.md @@ -5,13 +5,13 @@ each one organized by a "feature flag." That is, when using an unstable feature of Rust, you must use a flag, like this: ```rust -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; fn main() { - let mut coroutine = || { + let mut coroutine = #[coroutine] || { yield 1; return "foo" }; diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 9fc95fd09ba9f..40a25f13fcbdd 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -39,6 +39,7 @@ complete -c x.py -n "__fish_use_subcommand" -f -a "fix" -d 'Run cargo fix' complete -c x.py -n "__fish_use_subcommand" -f -a "fmt" -d 'Run rustfmt' complete -c x.py -n "__fish_use_subcommand" -f -a "doc" -d 'Build documentation' complete -c x.py -n "__fish_use_subcommand" -f -a "test" -d 'Build and run some test suites' +complete -c x.py -n "__fish_use_subcommand" -f -a "miri" -d 'Build and run some test suites *in Miri*' complete -c x.py -n "__fish_use_subcommand" -f -a "bench" -d 'Build and run some benchmarks' complete -c x.py -n "__fish_use_subcommand" -f -a "clean" -d 'Clean out build directories' complete -c x.py -n "__fish_use_subcommand" -f -a "dist" -d 'Build distribution artifacts' @@ -46,6 +47,7 @@ complete -c x.py -n "__fish_use_subcommand" -f -a "install" -d 'Install distribu complete -c x.py -n "__fish_use_subcommand" -f -a "run" -d 'Run tools contained in this repository' complete -c x.py -n "__fish_use_subcommand" -f -a "setup" -d 'Set up the environment for development' complete -c x.py -n "__fish_use_subcommand" -f -a "suggest" -d 'Suggest a subset of tests to run, based on modified files' +complete -c x.py -n "__fish_use_subcommand" -f -a "vendor" -d 'Vendor dependencies' complete -c x.py -n "__fish_seen_subcommand_from build" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_seen_subcommand_from build" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" complete -c x.py -n "__fish_seen_subcommand_from build" -l build -d 'build target of the stage0 compiler' -r -f @@ -261,7 +263,6 @@ complete -c x.py -n "__fish_seen_subcommand_from doc" -l llvm-profile-generate - complete -c x.py -n "__fish_seen_subcommand_from doc" -l enable-bolt-settings -d 'Enable BOLT link flags' complete -c x.py -n "__fish_seen_subcommand_from doc" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x.py -n "__fish_seen_subcommand_from doc" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c x.py -n "__fish_seen_subcommand_from test" -l skip -d 'skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times' -r -F complete -c x.py -n "__fish_seen_subcommand_from test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r complete -c x.py -n "__fish_seen_subcommand_from test" -l rustc-args -d 'extra options to pass the compiler when running tests' -r complete -c x.py -n "__fish_seen_subcommand_from test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)' -r @@ -274,6 +275,7 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l build -d 'build target complete -c x.py -n "__fish_seen_subcommand_from test" -l host -d 'host targets to build' -r -f complete -c x.py -n "__fish_seen_subcommand_from test" -l target -d 'target targets to build' -r -f complete -c x.py -n "__fish_seen_subcommand_from test" -l exclude -d 'build paths to exclude' -r -F +complete -c x.py -n "__fish_seen_subcommand_from test" -l skip -d 'build paths to skip' -r -F complete -c x.py -n "__fish_seen_subcommand_from test" -l rustc-error-format -r -f complete -c x.py -n "__fish_seen_subcommand_from test" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" complete -c x.py -n "__fish_seen_subcommand_from test" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f @@ -308,6 +310,45 @@ complete -c x.py -n "__fish_seen_subcommand_from test" -l llvm-profile-generate complete -c x.py -n "__fish_seen_subcommand_from test" -l enable-bolt-settings -d 'Enable BOLT link flags' complete -c x.py -n "__fish_seen_subcommand_from test" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x.py -n "__fish_seen_subcommand_from test" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r +complete -c x.py -n "__fish_seen_subcommand_from miri" -l rustc-args -d 'extra options to pass the compiler when running tests' -r +complete -c x.py -n "__fish_seen_subcommand_from miri" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l exclude -d 'build paths to exclude' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l rustc-error-format -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_seen_subcommand_from miri" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}" +complete -c x.py -n "__fish_seen_subcommand_from miri" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from miri" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_seen_subcommand_from miri" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_seen_subcommand_from miri" -l no-fail-fast -d 'run all tests regardless of failure' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l no-doc -d 'do not run doc tests' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l doc -d 'only run doc tests' +complete -c x.py -n "__fish_seen_subcommand_from miri" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_seen_subcommand_from miri" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_seen_subcommand_from miri" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_seen_subcommand_from miri" -s h -l help -d 'Print help (see more with \'--help\')' complete -c x.py -n "__fish_seen_subcommand_from bench" -l test-args -r complete -c x.py -n "__fish_seen_subcommand_from bench" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_seen_subcommand_from bench" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" @@ -550,3 +591,39 @@ complete -c x.py -n "__fish_seen_subcommand_from suggest" -l llvm-profile-genera complete -c x.py -n "__fish_seen_subcommand_from suggest" -l enable-bolt-settings -d 'Enable BOLT link flags' complete -c x.py -n "__fish_seen_subcommand_from suggest" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x.py -n "__fish_seen_subcommand_from suggest" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l sync -d 'Additional `Cargo.toml` to sync and vendor' -r -F +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l exclude -d 'build paths to exclude' -r -F +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l rustc-error-format -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_seen_subcommand_from vendor" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny '',warn '',default ''}" +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always '',never '',auto ''}" +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l llvm-skip-rebuild -d 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml' -r -f -a "{true '',false ''}" +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l versioned-dirs -d 'Always include version in subdir name' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_seen_subcommand_from vendor" -s h -l help -d 'Print help (see more with \'--help\')' diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index 6359b7ff086a9..f3d1d372c7337 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -66,6 +66,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('fmt', 'fmt', [CompletionResultType]::ParameterValue, 'Run rustfmt') [CompletionResult]::new('doc', 'doc', [CompletionResultType]::ParameterValue, 'Build documentation') [CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, 'Build and run some test suites') + [CompletionResult]::new('miri', 'miri', [CompletionResultType]::ParameterValue, 'Build and run some test suites *in Miri*') [CompletionResult]::new('bench', 'bench', [CompletionResultType]::ParameterValue, 'Build and run some benchmarks') [CompletionResult]::new('clean', 'clean', [CompletionResultType]::ParameterValue, 'Clean out build directories') [CompletionResult]::new('dist', 'dist', [CompletionResultType]::ParameterValue, 'Build distribution artifacts') @@ -73,6 +74,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('run', 'run', [CompletionResultType]::ParameterValue, 'Run tools contained in this repository') [CompletionResult]::new('setup', 'setup', [CompletionResultType]::ParameterValue, 'Set up the environment for development') [CompletionResult]::new('suggest', 'suggest', [CompletionResultType]::ParameterValue, 'Suggest a subset of tests to run, based on modified files') + [CompletionResult]::new('vendor', 'vendor', [CompletionResultType]::ParameterValue, 'Vendor dependencies') break } 'x.py;build' { @@ -333,7 +335,6 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { break } 'x.py;test' { - [CompletionResult]::new('--skip', 'skip', [CompletionResultType]::ParameterName, 'skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times') [CompletionResult]::new('--test-args', 'test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)') [CompletionResult]::new('--rustc-args', 'rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running tests') [CompletionResult]::new('--extra-checks', 'extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell)') @@ -346,6 +347,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--host', 'host', [CompletionResultType]::ParameterName, 'host targets to build') [CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'target targets to build') [CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'build paths to exclude') + [CompletionResult]::new('--skip', 'skip', [CompletionResultType]::ParameterName, 'build paths to skip') [CompletionResult]::new('--rustc-error-format', 'rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') [CompletionResult]::new('--on-fail', 'on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') [CompletionResult]::new('--stage', 'stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') @@ -386,6 +388,52 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') break } + 'x.py;miri' { + [CompletionResult]::new('--test-args', 'test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)') + [CompletionResult]::new('--rustc-args', 'rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running tests') + [CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', 'build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', 'build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', 'host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'build paths to exclude') + [CompletionResult]::new('--skip', 'skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', 'rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', 'on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', 'stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', 'keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', 'keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', 'src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', 'j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', 'jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', 'warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', 'error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', 'color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--llvm-skip-rebuild', 'llvm-skip-rebuild', [CompletionResultType]::ParameterName, 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml') + [CompletionResult]::new('--rust-profile-generate', 'rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', 'reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('--no-fail-fast', 'no-fail-fast', [CompletionResultType]::ParameterName, 'run all tests regardless of failure') + [CompletionResult]::new('--no-doc', 'no-doc', [CompletionResultType]::ParameterName, 'do not run doc tests') + [CompletionResult]::new('--doc', 'doc', [CompletionResultType]::ParameterName, 'only run doc tests') + [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', 'incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', 'include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', 'dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', 'bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } 'x.py;bench' { [CompletionResult]::new('--test-args', 'test-args', [CompletionResultType]::ParameterName, 'test-args') [CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') @@ -677,6 +725,49 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') break } + 'x.py;vendor' { + [CompletionResult]::new('--sync', 'sync', [CompletionResultType]::ParameterName, 'Additional `Cargo.toml` to sync and vendor') + [CompletionResult]::new('--config', 'config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', 'build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', 'build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', 'host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', 'target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--exclude', 'exclude', [CompletionResultType]::ParameterName, 'build paths to exclude') + [CompletionResult]::new('--skip', 'skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', 'rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', 'on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', 'stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', 'keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', 'keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', 'src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', 'j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', 'jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', 'warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', 'error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', 'color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--llvm-skip-rebuild', 'llvm-skip-rebuild', [CompletionResultType]::ParameterName, 'whether rebuilding llvm should be skipped, overriding `skip-rebuld` in config.toml') + [CompletionResult]::new('--rust-profile-generate', 'rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', 'rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', 'llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', 'reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('--versioned-dirs', 'versioned-dirs', [CompletionResultType]::ParameterName, 'Always include version in subdir name') + [CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', 'incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', 'include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', 'dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', 'json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', 'bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', 'llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', 'enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', 'skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } }) $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index e1436dcde6704..82cacb52ffeef 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -42,6 +42,9 @@ _x.py() { bootstrap,install) cmd="bootstrap__install" ;; + bootstrap,miri) + cmd="bootstrap__miri" + ;; bootstrap,run) cmd="bootstrap__run" ;; @@ -54,6 +57,9 @@ _x.py() { bootstrap,test) cmd="bootstrap__test" ;; + bootstrap,vendor) + cmd="bootstrap__vendor" + ;; *) ;; esac @@ -61,7 +67,7 @@ _x.py() { case "${cmd}" in x.py) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]... build check clippy fix fmt doc test bench clean dist install run setup suggest" + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]... build check clippy fix fmt doc test miri bench clean dist install run setup suggest vendor" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1290,6 +1296,124 @@ _x.py() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + x.py__miri) + opts="-v -i -j -h --no-fail-fast --test-args --rustc-args --no-doc --doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --test-args) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-args) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --config) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --build-dir) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --build) + COMPREPLY=("${cur}") + return 0 + ;; + --host) + COMPREPLY=("${cur}") + return 0 + ;; + --target) + COMPREPLY=("${cur}") + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + return 0 + ;; + --src) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + return 0 + ;; + -j) + COMPREPLY=("${cur}") + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --llvm-skip-rebuild) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rust-profile-use) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --llvm-profile-use) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; x.py__run) opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then @@ -1625,16 +1749,12 @@ _x.py() { return 0 ;; x.py__test) - opts="-v -i -j -h --no-fail-fast --skip --test-args --rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --no-fail-fast --test-args --rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi case "${prev}" in - --skip) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; --test-args) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -1683,6 +1803,124 @@ _x.py() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + return 0 + ;; + --src) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + return 0 + ;; + -j) + COMPREPLY=("${cur}") + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --llvm-skip-rebuild) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rust-profile-use) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --llvm-profile-use) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x.py__vendor) + opts="-v -i -j -h --sync --versioned-dirs --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --llvm-skip-rebuild --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --sync) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --config) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --build-dir) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --build) + COMPREPLY=("${cur}") + return 0 + ;; + --host) + COMPREPLY=("${cur}") + return 0 + ;; + --target) + COMPREPLY=("${cur}") + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --rustc-error-format) COMPREPLY=("${cur}") return 0 diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index ea7e4ba6758f5..12e96dbd40a31 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -335,7 +335,6 @@ _arguments "${_arguments_options[@]}" \ ;; (test) _arguments "${_arguments_options[@]}" \ -'*--skip=[skips tests matching SUBSTRING, if supported by test tool. May be passed multiple times]:SUBSTRING:_files' \ '*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS: ' \ '*--rustc-args=[extra options to pass the compiler when running tests]:ARGS: ' \ '--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell)]:EXTRA_CHECKS: ' \ @@ -348,6 +347,7 @@ _arguments "${_arguments_options[@]}" \ '--host=[host targets to build]:HOST:( )' \ '--target=[target targets to build]:TARGET:( )' \ '*--exclude=[build paths to exclude]:PATH:_files' \ +'*--skip=[build paths to skip]:PATH:_files' \ '--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \ '--on-fail=[command to run on failure]:CMD:_cmdstring' \ '--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \ @@ -389,6 +389,54 @@ _arguments "${_arguments_options[@]}" \ '*::paths -- paths for the subcommand:_files' \ && ret=0 ;; +(miri) +_arguments "${_arguments_options[@]}" \ +'*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS: ' \ +'*--rustc-args=[extra options to pass the compiler when running tests]:ARGS: ' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:( )' \ +'--host=[host targets to build]:HOST:( )' \ +'--target=[target targets to build]:TARGET:( )' \ +'*--exclude=[build paths to exclude]:PATH:_files' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:( )' \ +'--jobs=[number of jobs to run in parallel]:JOBS:( )' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:( )' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \ +'*--set=[override options in config.toml]:section.option=value:( )' \ +'--no-fail-fast[run all tests regardless of failure]' \ +'--no-doc[do not run doc tests]' \ +'--doc[only run doc tests]' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; (bench) _arguments "${_arguments_options[@]}" \ '*--test-args=[]:TEST_ARGS: ' \ @@ -694,6 +742,51 @@ _arguments "${_arguments_options[@]}" \ '--help[Print help (see more with '\''--help'\'')]' \ '*::paths -- paths for the subcommand:_files' \ && ret=0 +;; +(vendor) +_arguments "${_arguments_options[@]}" \ +'*--sync=[Additional \`Cargo.toml\` to sync and vendor]:SYNC:_files' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:( )' \ +'--host=[host targets to build]:HOST:( )' \ +'--target=[target targets to build]:TARGET:( )' \ +'*--exclude=[build paths to exclude]:PATH:_files' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:( )' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:( )' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:( )' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:( )' \ +'--jobs=[number of jobs to run in parallel]:JOBS:( )' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:( )' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--llvm-skip-rebuild=[whether rebuilding llvm should be skipped, overriding \`skip-rebuld\` in config.toml]:VALUE:(true false)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT: ' \ +'*--set=[override options in config.toml]:section.option=value:( )' \ +'--versioned-dirs[Always include version in subdir name]' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 ;; esac ;; @@ -710,6 +803,7 @@ _x.py_commands() { 'fmt:Run rustfmt' \ 'doc:Build documentation' \ 'test:Build and run some test suites' \ +'miri:Build and run some test suites *in Miri*' \ 'bench:Build and run some benchmarks' \ 'clean:Clean out build directories' \ 'dist:Build distribution artifacts' \ @@ -717,6 +811,7 @@ _x.py_commands() { 'run:Run tools contained in this repository' \ 'setup:Set up the environment for development' \ 'suggest:Suggest a subset of tests to run, based on modified files' \ +'vendor:Vendor dependencies' \ ) _describe -t commands 'x.py commands' commands "$@" } @@ -770,6 +865,11 @@ _x.py__install_commands() { local commands; commands=() _describe -t commands 'x.py install commands' commands "$@" } +(( $+functions[_x.py__miri_commands] )) || +_x.py__miri_commands() { + local commands; commands=() + _describe -t commands 'x.py miri commands' commands "$@" +} (( $+functions[_x.py__run_commands] )) || _x.py__run_commands() { local commands; commands=() @@ -790,6 +890,11 @@ _x.py__test_commands() { local commands; commands=() _describe -t commands 'x.py test commands' commands "$@" } +(( $+functions[_x.py__vendor_commands] )) || +_x.py__vendor_commands() { + local commands; commands=() + _describe -t commands 'x.py vendor commands' commands "$@" +} if [ "$funcstack[1]" = "_x.py" ]; then _x.py "$@" diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index 7d7277d240877..227695cdadd58 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -245,7 +245,14 @@ def __init__(self, valobj): fields = valobj.type.fields() assert len(fields) == 1 field = list(fields)[0] - self._value = str(valobj[field.name]) + + inner_valobj = valobj[field.name] + + inner_fields = inner_valobj.type.fields() + assert len(inner_fields) == 1 + inner_field = list(inner_fields)[0] + + self._value = str(inner_valobj[inner_field.name]) def to_string(self): return self._value diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands index ed66ecf30729e..4be2dba34f6f8 100644 --- a/src/etc/lldb_commands +++ b/src/etc/lldb_commands @@ -15,5 +15,8 @@ type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)C type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust type summary add -F lldb_lookup.summary_lookup -e -x -h "^core::num::([a-z_]+::)*NonZero.+$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::([a-z_]+::)+)PathBuf$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust type category enable Rust diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index 36c7d82b34ac7..a93b42e1cc449 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -58,6 +58,11 @@ def summary_lookup(valobj, dict): if rust_type == RustType.STD_NONZERO_NUMBER: return StdNonZeroNumberSummaryProvider(valobj, dict) + if rust_type == RustType.STD_PATHBUF: + return StdPathBufSummaryProvider(valobj, dict) + if rust_type == RustType.STD_PATH: + return StdPathSummaryProvider(valobj, dict) + return "" diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index cfb3f0a4eaebe..1c43977a501a0 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -173,6 +173,35 @@ def StdStrSummaryProvider(valobj, dict): return '"%s"' % data +def StdPathBufSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdPathBufSummaryProvider] for " + str(valobj.GetName()) + return StdOsStringSummaryProvider(valobj.GetChildMemberWithName("inner"), dict) + + +def StdPathSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdPathSummaryProvider] for " + str(valobj.GetName()) + length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned() + if length == 0: + return '""' + + data_ptr = valobj.GetChildMemberWithName("data_ptr") + + start = data_ptr.GetValueAsUnsigned() + error = SBError() + process = data_ptr.GetProcess() + data = process.ReadMemory(start, length, error) + if PY3: + try: + data = data.decode(encoding='UTF-8') + except UnicodeDecodeError: + return '%r' % data + return '"%s"' % data + + class StructSyntheticProvider: """Pretty-printer for structs and struct enum variants""" @@ -743,7 +772,12 @@ def has_children(self): def StdNonZeroNumberSummaryProvider(valobj, _dict): # type: (SBValue, dict) -> str - objtype = valobj.GetType() - field = objtype.GetFieldAtIndex(0) - element = valobj.GetChildMemberWithName(field.name) - return element.GetValue() + inner = valobj.GetChildAtIndex(0) + inner_inner = inner.GetChildAtIndex(0) + + # FIXME: Avoid printing as character literal, + # see https://github.com/llvm/llvm-project/issues/65076. + if inner_inner.GetTypeName() in ['char', 'unsigned char']: + return str(inner_inner.GetValueAsSigned()) + else: + return inner_inner.GetValue() diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis index 5e0c21a13a9fe..8a441cf209356 100644 --- a/src/etc/natvis/libcore.natvis +++ b/src/etc/natvis/libcore.natvis @@ -42,7 +42,10 @@ - {__0} + {__0.__0} + + __0.__0 + diff --git a/src/etc/rust_types.py b/src/etc/rust_types.py index bf512bc99b8f2..c0415a3cdcfe4 100644 --- a/src/etc/rust_types.py +++ b/src/etc/rust_types.py @@ -32,25 +32,29 @@ class RustType(object): STD_REF_MUT = "StdRefMut" STD_REF_CELL = "StdRefCell" STD_NONZERO_NUMBER = "StdNonZeroNumber" + STD_PATH = "StdPath" + STD_PATHBUF = "StdPathBuf" -STD_STRING_REGEX = re.compile(r"^(alloc::(\w+::)+)String$") +STD_STRING_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)String$") STD_STR_REGEX = re.compile(r"^&(mut )?str$") STD_SLICE_REGEX = re.compile(r"^&(mut )?\[.+\]$") -STD_OS_STRING_REGEX = re.compile(r"^(std::ffi::(\w+::)+)OsString$") -STD_VEC_REGEX = re.compile(r"^(alloc::(\w+::)+)Vec<.+>$") -STD_VEC_DEQUE_REGEX = re.compile(r"^(alloc::(\w+::)+)VecDeque<.+>$") -STD_BTREE_SET_REGEX = re.compile(r"^(alloc::(\w+::)+)BTreeSet<.+>$") -STD_BTREE_MAP_REGEX = re.compile(r"^(alloc::(\w+::)+)BTreeMap<.+>$") -STD_HASH_MAP_REGEX = re.compile(r"^(std::collections::(\w+::)+)HashMap<.+>$") -STD_HASH_SET_REGEX = re.compile(r"^(std::collections::(\w+::)+)HashSet<.+>$") -STD_RC_REGEX = re.compile(r"^(alloc::(\w+::)+)Rc<.+>$") -STD_ARC_REGEX = re.compile(r"^(alloc::(\w+::)+)Arc<.+>$") -STD_CELL_REGEX = re.compile(r"^(core::(\w+::)+)Cell<.+>$") -STD_REF_REGEX = re.compile(r"^(core::(\w+::)+)Ref<.+>$") -STD_REF_MUT_REGEX = re.compile(r"^(core::(\w+::)+)RefMut<.+>$") -STD_REF_CELL_REGEX = re.compile(r"^(core::(\w+::)+)RefCell<.+>$") -STD_NONZERO_NUMBER_REGEX = re.compile(r"^core::num::([a-z_]+::)*NonZero.+$") +STD_OS_STRING_REGEX = re.compile(r"^(std::ffi::([a-z_]+::)+)OsString$") +STD_VEC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Vec<.+>$") +STD_VEC_DEQUE_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)VecDeque<.+>$") +STD_BTREE_SET_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)BTreeSet<.+>$") +STD_BTREE_MAP_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)BTreeMap<.+>$") +STD_HASH_MAP_REGEX = re.compile(r"^(std::collections::([a-z_]+::)+)HashMap<.+>$") +STD_HASH_SET_REGEX = re.compile(r"^(std::collections::([a-z_]+::)+)HashSet<.+>$") +STD_RC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Rc<.+>$") +STD_ARC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Arc<.+>$") +STD_CELL_REGEX = re.compile(r"^(core::([a-z_]+::)+)Cell<.+>$") +STD_REF_REGEX = re.compile(r"^(core::([a-z_]+::)+)Ref<.+>$") +STD_REF_MUT_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefMut<.+>$") +STD_REF_CELL_REGEX = re.compile(r"^(core::([a-z_]+::)+)RefCell<.+>$") +STD_NONZERO_NUMBER_REGEX = re.compile(r"^(core::([a-z_]+::)+)NonZero<.+>$") +STD_PATHBUF_REGEX = re.compile(r"^(std::([a-z_]+::)+)PathBuf$") +STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$") TUPLE_ITEM_REGEX = re.compile(r"__\d+$") @@ -75,6 +79,8 @@ class RustType(object): RustType.STD_REF_CELL: STD_REF_CELL_REGEX, RustType.STD_CELL: STD_CELL_REGEX, RustType.STD_NONZERO_NUMBER: STD_NONZERO_NUMBER_REGEX, + RustType.STD_PATHBUF: STD_PATHBUF_REGEX, + RustType.STD_PATH: STD_PATH_REGEX, } def is_tuple_fields(fields): diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index bd0fbef998b2b..31222f213d800 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -9,10 +9,10 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } askama = { version = "0.12", default-features = false, features = ["config"] } +base64 = "0.21.7" itertools = "0.12" indexmap = "2" minifier = "0.3.0" -once_cell = "1.10.0" regex = "1" rustdoc-json-types = { path = "../rustdoc-json-types" } serde_json = "1.0" @@ -20,7 +20,7 @@ serde = { version = "1.0", features = ["derive"] } smallvec = "1.8.1" tempfile = "3" tracing = "0.1" -tracing-tree = "0.2.0" +tracing-tree = "0.3.0" threadpool = "1.8.1" [dependencies.tracing-subscriber] diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index fbc2c3c5af459..daf63998461ee 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -1,747 +1,367 @@ +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::{Region, RegionVid, TypeFoldable}; -use rustc_trait_selection::traits::auto_trait::{self, AutoTraitResult}; - -use std::fmt::Debug; - -use super::*; - -#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] -enum RegionTarget<'tcx> { - Region(Region<'tcx>), - RegionVid(RegionVid), -} - -#[derive(Default, Debug, Clone)] -struct RegionDeps<'tcx> { - larger: FxHashSet>, - smaller: FxHashSet>, -} - -pub(crate) struct AutoTraitFinder<'a, 'tcx> { - pub(crate) cx: &'a mut core::DocContext<'tcx>, +use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc_middle::bug; +use rustc_middle::ty::{self, Region, Ty}; +use rustc_span::def_id::DefId; +use rustc_span::symbol::{kw, Symbol}; +use rustc_trait_selection::traits::auto_trait::{self, RegionTarget}; + +use thin_vec::ThinVec; + +use crate::clean::{self, simplify, Lifetime}; +use crate::clean::{ + clean_generic_param_def, clean_middle_ty, clean_predicate, clean_trait_ref_with_bindings, + clean_ty_generics, +}; +use crate::core::DocContext; + +#[instrument(level = "debug", skip(cx))] +pub(crate) fn synthesize_auto_trait_impls<'tcx>( + cx: &mut DocContext<'tcx>, + item_def_id: DefId, +) -> Vec { + let tcx = cx.tcx; + let param_env = tcx.param_env(item_def_id); + let ty = tcx.type_of(item_def_id).instantiate_identity(); + + let finder = auto_trait::AutoTraitFinder::new(tcx); + let mut auto_trait_impls: Vec<_> = cx + .auto_traits + .clone() + .into_iter() + .filter_map(|trait_def_id| { + synthesize_auto_trait_impl( + cx, + ty, + trait_def_id, + param_env, + item_def_id, + &finder, + DiscardPositiveImpls::No, + ) + }) + .collect(); + // We are only interested in case the type *doesn't* implement the `Sized` trait. + if !ty.is_sized(tcx, param_env) + && let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() + && let Some(impl_item) = synthesize_auto_trait_impl( + cx, + ty, + sized_trait_def_id, + param_env, + item_def_id, + &finder, + DiscardPositiveImpls::Yes, + ) + { + auto_trait_impls.push(impl_item); + } + auto_trait_impls } -impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> -where - 'tcx: 'a, // should be an implied bound; rustc bug #98852. -{ - pub(crate) fn new(cx: &'a mut core::DocContext<'tcx>) -> Self { - AutoTraitFinder { cx } +#[instrument(level = "debug", skip(cx, finder))] +fn synthesize_auto_trait_impl<'tcx>( + cx: &mut DocContext<'tcx>, + ty: Ty<'tcx>, + trait_def_id: DefId, + param_env: ty::ParamEnv<'tcx>, + item_def_id: DefId, + finder: &auto_trait::AutoTraitFinder<'tcx>, + discard_positive_impls: DiscardPositiveImpls, +) -> Option { + let tcx = cx.tcx; + let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty])); + if !cx.generated_synthetics.insert((ty, trait_def_id)) { + debug!("already generated, aborting"); + return None; } - fn generate_for_trait( - &mut self, - ty: Ty<'tcx>, - trait_def_id: DefId, - param_env: ty::ParamEnv<'tcx>, - item_def_id: DefId, - f: &auto_trait::AutoTraitFinder<'tcx>, - // If this is set, show only negative trait implementations, not positive ones. - discard_positive_impl: bool, - ) -> Option { - let tcx = self.cx.tcx; - let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty])); - if !self.cx.generated_synthetics.insert((ty, trait_def_id)) { - debug!("get_auto_trait_impl_for({trait_ref:?}): already generated, aborting"); - return None; - } + let result = finder.find_auto_trait_generics(ty, param_env, trait_def_id, |info| { + clean_param_env(cx, item_def_id, info.full_user_env, info.region_data, info.vid_to_region) + }); - let result = f.find_auto_trait_generics(ty, param_env, trait_def_id, |info| { - let region_data = info.region_data; + let (generics, polarity) = match result { + auto_trait::AutoTraitResult::PositiveImpl(generics) => { + if let DiscardPositiveImpls::Yes = discard_positive_impls { + return None; + } - let names_map = tcx - .generics_of(item_def_id) - .params - .iter() - .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => Some(param.name), - _ => None, - }) - .map(|name| (name, Lifetime(name))) - .collect(); - let lifetime_predicates = Self::handle_lifetimes(®ion_data, &names_map); - let new_generics = self.param_env_to_generics( - item_def_id, - info.full_user_env, - lifetime_predicates, - info.vid_to_region, + (generics, ty::ImplPolarity::Positive) + } + auto_trait::AutoTraitResult::NegativeImpl => { + // For negative impls, we use the generic params, but *not* the predicates, + // from the original type. Otherwise, the displayed impl appears to be a + // conditional negative impl, when it's really unconditional. + // + // For example, consider the struct Foo(*mut T). Using + // the original predicates in our impl would cause us to generate + // `impl !Send for Foo`, which makes it appear that Foo + // implements Send where T is not copy. + // + // Instead, we generate `impl !Send for Foo`, which better + // expresses the fact that `Foo` never implements `Send`, + // regardless of the choice of `T`. + let mut generics = clean_ty_generics( + cx, + tcx.generics_of(item_def_id), + ty::GenericPredicates::default(), ); + generics.where_predicates.clear(); - debug!( - "find_auto_trait_generics(item_def_id={:?}, trait_def_id={:?}): \ - finished with {:?}", - item_def_id, trait_def_id, new_generics - ); + (generics, ty::ImplPolarity::Negative) + } + auto_trait::AutoTraitResult::ExplicitImpl => return None, + }; + + Some(clean::Item { + name: None, + attrs: Default::default(), + item_id: clean::ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, + kind: Box::new(clean::ImplItem(Box::new(clean::Impl { + unsafety: hir::Unsafety::Normal, + generics, + trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref, ThinVec::new())), + for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None), + items: Vec::new(), + polarity, + kind: clean::ImplKind::Auto, + }))), + cfg: None, + inline_stmt_id: None, + }) +} - new_generics - }); +#[derive(Debug)] +enum DiscardPositiveImpls { + Yes, + No, +} - let polarity; - let new_generics = match result { - AutoTraitResult::PositiveImpl(new_generics) => { - polarity = ty::ImplPolarity::Positive; - if discard_positive_impl { - return None; +#[instrument(level = "debug", skip(cx, region_data, vid_to_region))] +fn clean_param_env<'tcx>( + cx: &mut DocContext<'tcx>, + item_def_id: DefId, + param_env: ty::ParamEnv<'tcx>, + region_data: RegionConstraintData<'tcx>, + vid_to_region: FxIndexMap>, +) -> clean::Generics { + let tcx = cx.tcx; + let generics = tcx.generics_of(item_def_id); + + let params: ThinVec<_> = generics + .params + .iter() + .inspect(|param| { + if cfg!(debug_assertions) { + debug_assert!(!param.is_anonymous_lifetime() && !param.is_host_effect()); + if let ty::GenericParamDefKind::Type { synthetic, .. } = param.kind { + debug_assert!(!synthetic && param.name != kw::SelfUpper); } - new_generics } - AutoTraitResult::NegativeImpl => { - polarity = ty::ImplPolarity::Negative; - - // For negative impls, we use the generic params, but *not* the predicates, - // from the original type. Otherwise, the displayed impl appears to be a - // conditional negative impl, when it's really unconditional. - // - // For example, consider the struct Foo(*mut T). Using - // the original predicates in our impl would cause us to generate - // `impl !Send for Foo`, which makes it appear that Foo - // implements Send where T is not copy. - // - // Instead, we generate `impl !Send for Foo`, which better - // expresses the fact that `Foo` never implements `Send`, - // regardless of the choice of `T`. - let raw_generics = clean_ty_generics( - self.cx, - tcx.generics_of(item_def_id), - ty::GenericPredicates::default(), - ); - let params = raw_generics.params; - - Generics { params, where_predicates: ThinVec::new() } - } - AutoTraitResult::ExplicitImpl => return None, - }; - - Some(Item { - name: None, - attrs: Default::default(), - item_id: ItemId::Auto { trait_: trait_def_id, for_: item_def_id }, - kind: Box::new(ImplItem(Box::new(Impl { - unsafety: hir::Unsafety::Normal, - generics: new_generics, - trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), - for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None, None), - items: Vec::new(), - polarity, - kind: ImplKind::Auto, - }))), - cfg: None, - inline_stmt_id: None, }) - } - - pub(crate) fn get_auto_trait_impls(&mut self, item_def_id: DefId) -> Vec { - let tcx = self.cx.tcx; - let param_env = tcx.param_env(item_def_id); - let ty = tcx.type_of(item_def_id).instantiate_identity(); - let f = auto_trait::AutoTraitFinder::new(tcx); - - debug!("get_auto_trait_impls({ty:?})"); - let auto_traits: Vec<_> = self.cx.auto_traits.to_vec(); - let mut auto_traits: Vec = auto_traits - .into_iter() - .filter_map(|trait_def_id| { - self.generate_for_trait(ty, trait_def_id, param_env, item_def_id, &f, false) - }) - .collect(); - // We are only interested in case the type *doesn't* implement the Sized trait. - if !ty.is_sized(tcx, param_env) { - // In case `#![no_core]` is used, `sized_trait` returns nothing. - if let Some(item) = tcx.lang_items().sized_trait().and_then(|sized_trait_did| { - self.generate_for_trait(ty, sized_trait_did, param_env, item_def_id, &f, true) - }) { - auto_traits.push(item); - } - } - auto_traits - } - - fn get_lifetime(region: Region<'_>, names_map: &FxHashMap) -> Lifetime { - region_name(region) - .map(|name| { - names_map - .get(&name) - .unwrap_or_else(|| panic!("Missing lifetime with name {name:?} for {region:?}")) + // We're basing the generics of the synthetic auto trait impl off of the generics of the + // implementing type. Its generic parameters may have defaults, don't copy them over: + // Generic parameter defaults are meaningless in impls. + .map(|param| clean_generic_param_def(param, clean::ParamDefaults::No, cx)) + .collect(); + + // FIXME(#111101): Incorporate the explicit predicates of the item here... + let item_predicates: FxIndexSet<_> = + tcx.param_env(item_def_id).caller_bounds().iter().collect(); + let where_predicates = param_env + .caller_bounds() + .iter() + // FIXME: ...which hopefully allows us to simplify this: + .filter(|pred| { + !item_predicates.contains(pred) + || pred + .as_trait_clause() + .is_some_and(|pred| tcx.lang_items().sized_trait() == Some(pred.def_id())) + }) + .map(|pred| { + tcx.fold_regions(pred, |r, _| match *r { + // FIXME: Don't `unwrap_or`, I think we should panic if we encounter an infer var that + // we can't map to a concrete region. However, `AutoTraitFinder` *does* leak those kinds + // of `ReVar`s for some reason at the time of writing. See `rustdoc-ui/` tests. + // This is in dire need of an investigation into `AutoTraitFinder`. + ty::ReVar(vid) => vid_to_region.get(&vid).copied().unwrap_or(r), + ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, + // FIXME(#120606): `AutoTraitFinder` can actually leak placeholder regions which feels + // incorrect. Needs investigation. + ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReErased => { + bug!("unexpected region kind: {r:?}") + } }) - .unwrap_or(&Lifetime::statik()) - .clone() - } - - /// This method calculates two things: Lifetime constraints of the form `'a: 'b`, - /// and region constraints of the form `RegionVid: 'a` - /// - /// This is essentially a simplified version of lexical_region_resolve. However, - /// handle_lifetimes determines what *needs be* true in order for an impl to hold. - /// lexical_region_resolve, along with much of the rest of the compiler, is concerned - /// with determining if a given set up constraints/predicates *are* met, given some - /// starting conditions (e.g., user-provided code). For this reason, it's easier - /// to perform the calculations we need on our own, rather than trying to make - /// existing inference/solver code do what we want. - fn handle_lifetimes<'cx>( - regions: &RegionConstraintData<'cx>, - names_map: &FxHashMap, - ) -> ThinVec { - // Our goal is to 'flatten' the list of constraints by eliminating - // all intermediate RegionVids. At the end, all constraints should - // be between Regions (aka region variables). This gives us the information - // we need to create the Generics. - let mut finished: FxHashMap<_, Vec<_>> = Default::default(); - - let mut vid_map: FxHashMap, RegionDeps<'_>> = Default::default(); - - // Flattening is done in two parts. First, we insert all of the constraints - // into a map. Each RegionTarget (either a RegionVid or a Region) maps - // to its smaller and larger regions. Note that 'larger' regions correspond - // to sub-regions in Rust code (e.g., in 'a: 'b, 'a is the larger region). - for (constraint, _) in ®ions.constraints { - match *constraint { - Constraint::VarSubVar(r1, r2) => { - { - let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default(); - deps1.larger.insert(RegionTarget::RegionVid(r2)); - } + }) + .flat_map(|pred| clean_predicate(pred, cx)) + .chain(clean_region_outlives_constraints(®ion_data, generics)) + .collect(); + + let mut generics = clean::Generics { params, where_predicates }; + simplify::sized_bounds(cx, &mut generics); + generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + generics +} - let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default(); - deps2.smaller.insert(RegionTarget::RegionVid(r1)); - } - Constraint::RegSubVar(region, vid) => { - let deps = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); - deps.smaller.insert(RegionTarget::Region(region)); - } - Constraint::VarSubReg(vid, region) => { - let deps = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); - deps.larger.insert(RegionTarget::Region(region)); - } - Constraint::RegSubReg(r1, r2) => { - // The constraint is already in the form that we want, so we're done with it - // Desired order is 'larger, smaller', so flip then - if region_name(r1) != region_name(r2) { - finished - .entry(region_name(r2).expect("no region_name found")) - .or_default() - .push(r1); - } - } +/// Clean region outlives constraints to where-predicates. +/// +/// This is essentially a simplified version of `lexical_region_resolve`. +/// +/// However, here we determine what *needs to be* true in order for an impl to hold. +/// `lexical_region_resolve`, along with much of the rest of the compiler, is concerned +/// with determining if a given set up constraints / predicates *are* met, given some +/// starting conditions like user-provided code. +/// +/// For this reason, it's easier to perform the calculations we need on our own, +/// rather than trying to make existing inference/solver code do what we want. +fn clean_region_outlives_constraints<'tcx>( + regions: &RegionConstraintData<'tcx>, + generics: &'tcx ty::Generics, +) -> ThinVec { + // Our goal is to "flatten" the list of constraints by eliminating all intermediate + // `RegionVids` (region inference variables). At the end, all constraints should be + // between `Region`s. This gives us the information we need to create the where-predicates. + // This flattening is done in two parts. + + let mut outlives_predicates = FxIndexMap::<_, Vec<_>>::default(); + let mut map = FxIndexMap::, auto_trait::RegionDeps<'_>>::default(); + + // (1) We insert all of the constraints into a map. + // Each `RegionTarget` (a `RegionVid` or a `Region`) maps to its smaller and larger regions. + // Note that "larger" regions correspond to sub regions in the surface language. + // E.g., in `'a: 'b`, `'a` is the larger region. + for (constraint, _) in ®ions.constraints { + match *constraint { + Constraint::VarSubVar(vid1, vid2) => { + let deps1 = map.entry(RegionTarget::RegionVid(vid1)).or_default(); + deps1.larger.insert(RegionTarget::RegionVid(vid2)); + + let deps2 = map.entry(RegionTarget::RegionVid(vid2)).or_default(); + deps2.smaller.insert(RegionTarget::RegionVid(vid1)); } - } - - // Here, we 'flatten' the map one element at a time. - // All of the element's sub and super regions are connected - // to each other. For example, if we have a graph that looks like this: - // - // (A, B) - C - (D, E) - // Where (A, B) are subregions, and (D,E) are super-regions - // - // then after deleting 'C', the graph will look like this: - // ... - A - (D, E ...) - // ... - B - (D, E, ...) - // (A, B, ...) - D - ... - // (A, B, ...) - E - ... - // - // where '...' signifies the existing sub and super regions of an entry - // When two adjacent ty::Regions are encountered, we've computed a final - // constraint, and add it to our list. Since we make sure to never re-add - // deleted items, this process will always finish. - while !vid_map.is_empty() { - let target = *vid_map.keys().next().expect("Keys somehow empty"); - let deps = vid_map.remove(&target).expect("Entry somehow missing"); - - for smaller in deps.smaller.iter() { - for larger in deps.larger.iter() { - match (smaller, larger) { - (&RegionTarget::Region(r1), &RegionTarget::Region(r2)) => { - if region_name(r1) != region_name(r2) { - finished - .entry(region_name(r2).expect("no region name found")) - .or_default() - .push(r1) // Larger, smaller - } - } - (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { - let smaller_deps = v.into_mut(); - smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); - } - } - (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*larger) { - let deps = v.into_mut(); - deps.smaller.insert(*smaller); - deps.smaller.remove(&target); - } - } - (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { - let smaller_deps = v.into_mut(); - smaller_deps.larger.insert(*larger); - smaller_deps.larger.remove(&target); - } - - if let Entry::Occupied(v) = vid_map.entry(*larger) { - let larger_deps = v.into_mut(); - larger_deps.smaller.insert(*smaller); - larger_deps.smaller.remove(&target); - } - } - } + Constraint::RegSubVar(region, vid) => { + let deps = map.entry(RegionTarget::RegionVid(vid)).or_default(); + deps.smaller.insert(RegionTarget::Region(region)); + } + Constraint::VarSubReg(vid, region) => { + let deps = map.entry(RegionTarget::RegionVid(vid)).or_default(); + deps.larger.insert(RegionTarget::Region(region)); + } + Constraint::RegSubReg(r1, r2) => { + // The constraint is already in the form that we want, so we're done with it + // The desired order is [larger, smaller], so flip them. + if early_bound_region_name(r1) != early_bound_region_name(r2) { + outlives_predicates + .entry(early_bound_region_name(r2).expect("no region_name found")) + .or_default() + .push(r1); } } } - - let lifetime_predicates = names_map - .iter() - .flat_map(|(name, lifetime)| { - let empty = Vec::new(); - let bounds: FxHashSet = finished - .get(name) - .unwrap_or(&empty) - .iter() - .map(|region| GenericBound::Outlives(Self::get_lifetime(*region, names_map))) - .collect(); - - if bounds.is_empty() { - return None; - } - Some(WherePredicate::RegionPredicate { - lifetime: lifetime.clone(), - bounds: bounds.into_iter().collect(), - }) - }) - .collect(); - - lifetime_predicates } - fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxHashSet { - let bound_predicate = pred.kind(); - let tcx = self.cx.tcx; - let regions = - match bound_predicate.skip_binder() { - ty::ClauseKind::Trait(poly_trait_pred) => tcx - .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_trait_pred)), - ty::ClauseKind::Projection(poly_proj_pred) => tcx - .collect_referenced_late_bound_regions(bound_predicate.rebind(poly_proj_pred)), - _ => return FxHashSet::default(), - }; - - regions - .into_iter() - .filter_map(|br| { - match br { - // We only care about named late bound regions, as we need to add them - // to the 'for<>' section - ty::BrNamed(def_id, name) => Some(GenericParamDef::lifetime(def_id, name)), - _ => None, - } - }) - .collect() - } - - fn make_final_bounds( - &self, - ty_to_bounds: FxHashMap>, - ty_to_fn: FxHashMap)>, - lifetime_to_bounds: FxHashMap>, - ) -> Vec { - ty_to_bounds - .into_iter() - .flat_map(|(ty, mut bounds)| { - if let Some((ref poly_trait, ref output)) = ty_to_fn.get(&ty) { - let mut new_path = poly_trait.trait_.clone(); - let last_segment = new_path.segments.pop().expect("segments were empty"); - - let (old_input, old_output) = match last_segment.args { - GenericArgs::AngleBracketed { args, .. } => { - let types = args - .iter() - .filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty.clone()), - _ => None, - }) - .collect(); - (types, None) + // (2) Here, we "flatten" the map one element at a time. All of the elements' sub and super + // regions are connected to each other. For example, if we have a graph that looks like this: + // + // (A, B) - C - (D, E) + // + // where (A, B) are sub regions, and (D,E) are super regions. + // Then, after deleting 'C', the graph will look like this: + // + // ... - A - (D, E, ...) + // ... - B - (D, E, ...) + // (A, B, ...) - D - ... + // (A, B, ...) - E - ... + // + // where '...' signifies the existing sub and super regions of an entry. When two adjacent + // `Region`s are encountered, we've computed a final constraint, and add it to our list. + // Since we make sure to never re-add deleted items, this process will always finish. + while !map.is_empty() { + let target = *map.keys().next().unwrap(); + let deps = map.swap_remove(&target).unwrap(); + + for smaller in &deps.smaller { + for larger in &deps.larger { + match (smaller, larger) { + (&RegionTarget::Region(smaller), &RegionTarget::Region(larger)) => { + if early_bound_region_name(smaller) != early_bound_region_name(larger) { + outlives_predicates + .entry( + early_bound_region_name(larger).expect("no region name found"), + ) + .or_default() + .push(smaller) } - GenericArgs::Parenthesized { inputs, output } => (inputs, output), - }; - - let output = output.as_ref().cloned().map(Box::new); - if old_output.is_some() && old_output != output { - panic!("Output mismatch for {ty:?} {old_output:?} {output:?}"); - } - - let new_params = GenericArgs::Parenthesized { inputs: old_input, output }; - - new_path - .segments - .push(PathSegment { name: last_segment.name, args: new_params }); - - bounds.insert(GenericBound::TraitBound( - PolyTrait { - trait_: new_path, - generic_params: poly_trait.generic_params.clone(), - }, - hir::TraitBoundModifier::None, - )); - } - if bounds.is_empty() { - return None; - } - - let mut bounds_vec = bounds.into_iter().collect(); - self.sort_where_bounds(&mut bounds_vec); - - Some(WherePredicate::BoundPredicate { - ty, - bounds: bounds_vec, - bound_params: Vec::new(), - }) - }) - .chain(lifetime_to_bounds.into_iter().filter(|(_, bounds)| !bounds.is_empty()).map( - |(lifetime, bounds)| { - let mut bounds_vec = bounds.into_iter().collect(); - self.sort_where_bounds(&mut bounds_vec); - WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec } - }, - )) - .collect() - } - - /// Converts the calculated `ParamEnv` and lifetime information to a [`clean::Generics`](Generics), suitable for - /// display on the docs page. Cleaning the `Predicates` produces sub-optimal [`WherePredicate`]s, - /// so we fix them up: - /// - /// * Multiple bounds for the same type are coalesced into one: e.g., `T: Copy`, `T: Debug` - /// becomes `T: Copy + Debug` - /// * `Fn` bounds are handled specially - instead of leaving it as `T: Fn(), = - /// K`, we use the dedicated syntax `T: Fn() -> K` - /// * We explicitly add a `?Sized` bound if we didn't find any `Sized` predicates for a type - fn param_env_to_generics( - &mut self, - item_def_id: DefId, - param_env: ty::ParamEnv<'tcx>, - mut existing_predicates: ThinVec, - vid_to_region: FxHashMap>, - ) -> Generics { - debug!( - "param_env_to_generics(item_def_id={:?}, param_env={:?}, \ - existing_predicates={:?})", - item_def_id, param_env, existing_predicates - ); - - let tcx = self.cx.tcx; - - // The `Sized` trait must be handled specially, since we only display it when - // it is *not* required (i.e., '?Sized') - let sized_trait = tcx.require_lang_item(LangItem::Sized, None); - - let mut replacer = RegionReplacer { vid_to_region: &vid_to_region, tcx }; - - let orig_bounds: FxHashSet<_> = tcx.param_env(item_def_id).caller_bounds().iter().collect(); - let clean_where_predicates = param_env - .caller_bounds() - .iter() - .filter(|p| { - !orig_bounds.contains(p) - || match p.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => pred.def_id() == sized_trait, - _ => false, - } - }) - .map(|p| p.fold_with(&mut replacer)); - - let raw_generics = clean_ty_generics( - self.cx, - tcx.generics_of(item_def_id), - tcx.explicit_predicates_of(item_def_id), - ); - let mut generic_params = raw_generics.params; - - debug!("param_env_to_generics({item_def_id:?}): generic_params={generic_params:?}"); - - let mut has_sized = FxHashSet::default(); - let mut ty_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); - let mut lifetime_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default(); - let mut ty_to_traits: FxHashMap> = Default::default(); - - let mut ty_to_fn: FxHashMap)> = Default::default(); - - // FIXME: This code shares much of the logic found in `clean_ty_generics` and - // `simplify::where_clause`. Consider deduplicating it to avoid diverging - // implementations. - // Further, the code below does not merge (partially re-sugared) bounds like - // `Tr` & `Tr` and it does not render higher-ranked parameters - // originating from equality predicates. - for p in clean_where_predicates { - let (orig_p, p) = (p, clean_predicate(p, self.cx)); - if p.is_none() { - continue; - } - let p = p.unwrap(); - match p { - WherePredicate::BoundPredicate { ty, mut bounds, .. } => { - // Writing a projection trait bound of the form - // ::Name : ?Sized - // is illegal, because ?Sized bounds can only - // be written in the (here, nonexistent) definition - // of the type. - // Therefore, we make sure that we never add a ?Sized - // bound for projections - if let Type::QPath { .. } = ty { - has_sized.insert(ty.clone()); } - - if bounds.is_empty() { - continue; - } - - let mut for_generics = self.extract_for_generics(orig_p); - - assert!(bounds.len() == 1); - let mut b = bounds.pop().expect("bounds were empty"); - - if b.is_sized_bound(self.cx) { - has_sized.insert(ty.clone()); - } else if !b - .get_trait_path() - .and_then(|trait_| { - ty_to_traits - .get(&ty) - .map(|bounds| bounds.contains(&strip_path_generics(trait_))) - }) - .unwrap_or(false) - { - // If we've already added a projection bound for the same type, don't add - // this, as it would be a duplicate - - // Handle any 'Fn/FnOnce/FnMut' bounds specially, - // as we want to combine them with any 'Output' qpaths - // later - - let is_fn = match b { - GenericBound::TraitBound(ref mut p, _) => { - // Insert regions into the for_generics hash map first, to ensure - // that we don't end up with duplicate bounds (e.g., for<'b, 'b>) - for_generics.extend(p.generic_params.drain(..)); - p.generic_params.extend(for_generics); - self.is_fn_trait(&p.trait_) - } - _ => false, - }; - - let poly_trait = b.get_poly_trait().expect("Cannot get poly trait"); - - if is_fn { - ty_to_fn - .entry(ty.clone()) - .and_modify(|e| *e = (poly_trait.clone(), e.1.clone())) - .or_insert(((poly_trait.clone()), None)); - - ty_to_bounds.entry(ty.clone()).or_default(); - } else { - ty_to_bounds.entry(ty.clone()).or_default().insert(b.clone()); + (&RegionTarget::RegionVid(_), &RegionTarget::Region(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.swap_remove(&target); } } - } - WherePredicate::RegionPredicate { lifetime, bounds } => { - lifetime_to_bounds.entry(lifetime).or_default().extend(bounds); - } - WherePredicate::EqPredicate { lhs, rhs } => { - match lhs { - Type::QPath(box QPathData { - ref assoc, - ref self_type, - trait_: Some(ref trait_), - .. - }) => { - let ty = &*self_type; - let mut new_trait = trait_.clone(); - - if self.is_fn_trait(trait_) && assoc.name == sym::Output { - ty_to_fn - .entry(ty.clone()) - .and_modify(|e| { - *e = (e.0.clone(), Some(rhs.ty().unwrap().clone())) - }) - .or_insert(( - PolyTrait { - trait_: trait_.clone(), - generic_params: Vec::new(), - }, - Some(rhs.ty().unwrap().clone()), - )); - continue; - } - - let args = &mut new_trait - .segments - .last_mut() - .expect("segments were empty") - .args; - - match args { - // Convert something like ' = u8' - // to 'T: Iterator' - GenericArgs::AngleBracketed { ref mut bindings, .. } => { - bindings.push(TypeBinding { - assoc: assoc.clone(), - kind: TypeBindingKind::Equality { term: rhs }, - }); - } - GenericArgs::Parenthesized { .. } => { - existing_predicates.push(WherePredicate::EqPredicate { - lhs: lhs.clone(), - rhs, - }); - continue; // If something other than a Fn ends up - // with parentheses, leave it alone - } - } - - let bounds = ty_to_bounds.entry(ty.clone()).or_default(); - - bounds.insert(GenericBound::TraitBound( - PolyTrait { trait_: new_trait, generic_params: Vec::new() }, - hir::TraitBoundModifier::None, - )); - - // Remove any existing 'plain' bound (e.g., 'T: Iterator`) so - // that we don't see a - // duplicate bound like `T: Iterator + Iterator` - // on the docs page. - bounds.remove(&GenericBound::TraitBound( - PolyTrait { trait_: trait_.clone(), generic_params: Vec::new() }, - hir::TraitBoundModifier::None, - )); - // Avoid creating any new duplicate bounds later in the outer - // loop - ty_to_traits.entry(ty.clone()).or_default().insert(trait_.clone()); + (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*larger) { + let deps = v.into_mut(); + deps.smaller.insert(*smaller); + deps.smaller.swap_remove(&target); } - _ => panic!("Unexpected LHS {lhs:?} for {item_def_id:?}"), } - } - }; - } - - let final_bounds = self.make_final_bounds(ty_to_bounds, ty_to_fn, lifetime_to_bounds); - - existing_predicates.extend(final_bounds); - - for param in generic_params.iter_mut() { - match param.kind { - GenericParamDefKind::Type { ref mut default, ref mut bounds, .. } => { - // We never want something like `impl`. - default.take(); - let generic_ty = Type::Generic(param.name); - if !has_sized.contains(&generic_ty) { - bounds.insert(0, GenericBound::maybe_sized(self.cx)); + (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { + if let IndexEntry::Occupied(v) = map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.swap_remove(&target); + } + if let IndexEntry::Occupied(v) = map.entry(*larger) { + let larger_deps = v.into_mut(); + larger_deps.smaller.insert(*smaller); + larger_deps.smaller.swap_remove(&target); + } } } - GenericParamDefKind::Lifetime { .. } => {} - GenericParamDefKind::Const { ref mut default, .. } => { - // We never want something like `impl` - default.take(); - } } } - - self.sort_where_predicates(&mut existing_predicates); - - Generics { params: generic_params, where_predicates: existing_predicates } - } - - /// Ensure that the predicates are in a consistent order. The precise - /// ordering doesn't actually matter, but it's important that - /// a given set of predicates always appears in the same order - - /// both for visual consistency between 'rustdoc' runs, and to - /// make writing tests much easier - #[inline] - fn sort_where_predicates(&self, predicates: &mut [WherePredicate]) { - // We should never have identical bounds - and if we do, - // they're visually identical as well. Therefore, using - // an unstable sort is fine. - self.unstable_debug_sort(predicates); - } - - /// Ensure that the bounds are in a consistent order. The precise - /// ordering doesn't actually matter, but it's important that - /// a given set of bounds always appears in the same order - - /// both for visual consistency between 'rustdoc' runs, and to - /// make writing tests much easier - #[inline] - fn sort_where_bounds(&self, bounds: &mut Vec) { - // We should never have identical bounds - and if we do, - // they're visually identical as well. Therefore, using - // an unstable sort is fine. - self.unstable_debug_sort(bounds); } - /// This might look horrendously hacky, but it's actually not that bad. - /// - /// For performance reasons, we use several different FxHashMaps - /// in the process of computing the final set of where predicates. - /// However, the iteration order of a HashMap is completely unspecified. - /// In fact, the iteration of an FxHashMap can even vary between platforms, - /// since FxHasher has different behavior for 32-bit and 64-bit platforms. - /// - /// Obviously, it's extremely undesirable for documentation rendering - /// to be dependent on the platform it's run on. Apart from being confusing - /// to end users, it makes writing tests much more difficult, as predicates - /// can appear in any order in the final result. - /// - /// To solve this problem, we sort WherePredicates and GenericBounds - /// by their Debug string. The thing to keep in mind is that we don't really - /// care what the final order is - we're synthesizing an impl or bound - /// ourselves, so any order can be considered equally valid. By sorting the - /// predicates and bounds, however, we ensure that for a given codebase, all - /// auto-trait impls always render in exactly the same way. - /// - /// Using the Debug implementation for sorting prevents us from needing to - /// write quite a bit of almost entirely useless code (e.g., how should two - /// Types be sorted relative to each other). It also allows us to solve the - /// problem for both WherePredicates and GenericBounds at the same time. This - /// approach is probably somewhat slower, but the small number of items - /// involved (impls rarely have more than a few bounds) means that it - /// shouldn't matter in practice. - fn unstable_debug_sort(&self, vec: &mut [T]) { - vec.sort_by_cached_key(|x| format!("{x:?}")) - } + let region_params: FxIndexSet<_> = generics + .params + .iter() + .filter_map(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => Some(param.name), + _ => None, + }) + .collect(); - fn is_fn_trait(&self, path: &Path) -> bool { - let tcx = self.cx.tcx; - let did = path.def_id(); - did == tcx.require_lang_item(LangItem::Fn, None) - || did == tcx.require_lang_item(LangItem::FnMut, None) - || did == tcx.require_lang_item(LangItem::FnOnce, None) - } + region_params + .iter() + .filter_map(|&name| { + let bounds: FxIndexSet<_> = outlives_predicates + .get(&name)? + .iter() + .map(|®ion| { + let lifetime = early_bound_region_name(region) + .inspect(|name| assert!(region_params.contains(name))) + .map(|name| Lifetime(name)) + .unwrap_or(Lifetime::statik()); + clean::GenericBound::Outlives(lifetime) + }) + .collect(); + if bounds.is_empty() { + return None; + } + Some(clean::WherePredicate::RegionPredicate { + lifetime: Lifetime(name), + bounds: bounds.into_iter().collect(), + }) + }) + .collect() } -fn region_name(region: Region<'_>) -> Option { +fn early_bound_region_name(region: Region<'_>) -> Option { match *region { ty::ReEarlyParam(r) => Some(r.name), _ => None, } } - -/// Replaces all [`ty::RegionVid`]s in a type with [`ty::Region`]s, using the provided map. -struct RegionReplacer<'a, 'tcx> { - vid_to_region: &'a FxHashMap>, - tcx: TyCtxt<'tcx>, -} - -impl<'a, 'tcx> TypeFolder> for RegionReplacer<'a, 'tcx> { - fn interner(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - // These are the regions that can be seen in the AST. - ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned().unwrap_or(r), - ty::ReEarlyParam(_) | ty::ReStatic | ty::ReBound(..) | ty::ReError(_) => r, - r => bug!("unexpected region: {r:?}"), - } - } -} diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 47cfe651e319d..8ed6ee014f3fa 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -1,141 +1,130 @@ -use crate::rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_hir as hir; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits; -use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::{self, ToPredicate}; +use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use super::*; +use thin_vec::ThinVec; -pub(crate) struct BlanketImplFinder<'a, 'tcx> { - pub(crate) cx: &'a mut core::DocContext<'tcx>, -} +use crate::clean; +use crate::clean::{ + clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty_generics, +}; +use crate::core::DocContext; + +#[instrument(level = "debug", skip(cx))] +pub(crate) fn synthesize_blanket_impls( + cx: &mut DocContext<'_>, + item_def_id: DefId, +) -> Vec { + let tcx = cx.tcx; + let ty = tcx.type_of(item_def_id); -impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { - pub(crate) fn get_blanket_impls(&mut self, item_def_id: DefId) -> Vec { - let cx = &mut self.cx; - let ty = cx.tcx.type_of(item_def_id); + let mut blanket_impls = Vec::new(); + for trait_def_id in tcx.all_traits() { + if !cx.cache.effective_visibilities.is_reachable(tcx, trait_def_id) + || cx.generated_synthetics.get(&(ty.skip_binder(), trait_def_id)).is_some() + { + continue; + } + // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls + let trait_impls = tcx.trait_impls_of(trait_def_id); + 'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() { + trace!("considering impl `{impl_def_id:?}` for trait `{trait_def_id:?}`"); - trace!("get_blanket_impls({ty:?})"); - let mut impls = Vec::new(); - for trait_def_id in cx.tcx.all_traits() { - if !cx.cache.effective_visibilities.is_reachable(cx.tcx, trait_def_id) - || cx.generated_synthetics.get(&(ty.skip_binder(), trait_def_id)).is_some() - { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { continue; } - // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls - let trait_impls = cx.tcx.trait_impls_of(trait_def_id); - 'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() { - trace!( - "get_blanket_impls: Considering impl for trait '{:?}' {:?}", - trait_def_id, - impl_def_id - ); - let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap(); - if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { - continue; - } - let infcx = cx.tcx.infer_ctxt().build(); - let args = infcx.fresh_args_for_item(DUMMY_SP, item_def_id); - let impl_ty = ty.instantiate(infcx.tcx, args); - let param_env = ty::ParamEnv::empty(); + let infcx = tcx.infer_ctxt().build(); + let args = infcx.fresh_args_for_item(DUMMY_SP, item_def_id); + let impl_ty = ty.instantiate(tcx, args); + let param_env = ty::ParamEnv::empty(); - let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); - let impl_trait_ref = trait_ref.instantiate(infcx.tcx, impl_args); + let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = trait_ref.instantiate(tcx, impl_args); - // Require the type the impl is implemented on to match - // our type, and ignore the impl if there was a mismatch. - let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq( - DefineOpaqueTypes::No, - impl_trait_ref.self_ty(), - impl_ty, - ) else { - continue; - }; - let InferOk { value: (), obligations } = eq_result; - // FIXME(eddyb) ignoring `obligations` might cause false positives. - drop(obligations); + // Require the type the impl is implemented on to match + // our type, and ignore the impl if there was a mismatch. + let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq( + DefineOpaqueTypes::Yes, + impl_trait_ref.self_ty(), + impl_ty, + ) else { + continue; + }; + let InferOk { value: (), obligations } = eq_result; + // FIXME(eddyb) ignoring `obligations` might cause false positives. + drop(obligations); - trace!( - "invoking predicate_may_hold: param_env={:?}, impl_trait_ref={:?}, impl_ty={:?}", + let predicates = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .predicates + .into_iter() + .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(tcx))); + for predicate in predicates { + let obligation = traits::Obligation::new( + tcx, + traits::ObligationCause::dummy(), param_env, - impl_trait_ref, - impl_ty + predicate, ); - let predicates = cx - .tcx - .predicates_of(impl_def_id) - .instantiate(cx.tcx, impl_args) - .predicates - .into_iter() - .chain(Some(ty::Binder::dummy(impl_trait_ref).to_predicate(infcx.tcx))); - for predicate in predicates { - debug!("testing predicate {predicate:?}"); - let obligation = traits::Obligation::new( - infcx.tcx, - traits::ObligationCause::dummy(), - param_env, - predicate, - ); - match infcx.evaluate_obligation(&obligation) { - Ok(eval_result) if eval_result.may_apply() => {} - Err(traits::OverflowError::Canonical) => {} - _ => continue 'blanket_impls, - } + match infcx.evaluate_obligation(&obligation) { + Ok(eval_result) if eval_result.may_apply() => {} + Err(traits::OverflowError::Canonical) => {} + _ => continue 'blanket_impls, } - debug!( - "get_blanket_impls: found applicable impl for trait_ref={:?}, ty={:?}", - trait_ref, ty - ); + } + debug!("found applicable impl for trait ref {trait_ref:?}"); - cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); + cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); - impls.push(Item { - name: None, - attrs: Default::default(), - item_id: ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, - kind: Box::new(ImplItem(Box::new(Impl { - unsafety: hir::Unsafety::Normal, - generics: clean_ty_generics( - cx, - cx.tcx.generics_of(impl_def_id), - cx.tcx.explicit_predicates_of(impl_def_id), - ), - // FIXME(eddyb) compute both `trait_` and `for_` from - // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(clean_trait_ref_with_bindings( - cx, - ty::Binder::dummy(trait_ref.instantiate_identity()), - ThinVec::new(), - )), - for_: clean_middle_ty( - ty::Binder::dummy(ty.instantiate_identity()), - cx, - None, - None, - ), - items: cx - .tcx - .associated_items(impl_def_id) - .in_definition_order() - .filter(|item| !item.is_impl_trait_in_trait()) - .map(|item| clean_middle_assoc_item(item, cx)) - .collect::>(), - polarity: ty::ImplPolarity::Positive, - kind: ImplKind::Blanket(Box::new(clean_middle_ty( - ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()), - cx, - None, - None, - ))), - }))), - cfg: None, - inline_stmt_id: None, - }); - } + blanket_impls.push(clean::Item { + name: None, + attrs: Default::default(), + item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id }, + kind: Box::new(clean::ImplItem(Box::new(clean::Impl { + unsafety: hir::Unsafety::Normal, + generics: clean_ty_generics( + cx, + tcx.generics_of(impl_def_id), + tcx.explicit_predicates_of(impl_def_id), + ), + // FIXME(eddyb) compute both `trait_` and `for_` from + // the post-inference `trait_ref`, as it's more accurate. + trait_: Some(clean_trait_ref_with_bindings( + cx, + ty::Binder::dummy(trait_ref.instantiate_identity()), + ThinVec::new(), + )), + for_: clean_middle_ty( + ty::Binder::dummy(ty.instantiate_identity()), + cx, + None, + None, + ), + items: tcx + .associated_items(impl_def_id) + .in_definition_order() + .filter(|item| !item.is_impl_trait_in_trait()) + .map(|item| clean_middle_assoc_item(item, cx)) + .collect(), + polarity: ty::ImplPolarity::Positive, + kind: clean::ImplKind::Blanket(Box::new(clean_middle_ty( + ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()), + cx, + None, + None, + ))), + }))), + cfg: None, + inline_stmt_id: None, + }); } - - impls } + + blanket_impls } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 277db90df9aaa..2814e83dcd757 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -511,6 +511,7 @@ impl<'a> fmt::Display for Display<'a> { "wasi" => "WASI", "watchos" => "watchOS", "windows" => "Windows", + "visionos" => "visionOS", _ => "", }, (sym::target_arch, Some(arch)) => match arch.as_str() { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 6f86c6450d969..4d506edc47bd4 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 dfc026fe50bb4..fc4f48262e5dc 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1,5 +1,25 @@ -//! This module contains the "cleaned" pieces of the AST, and the functions -//! that clean them. +//! This module defines the primary IR[^1] used in rustdoc together with the procedures that +//! transform rustc data types into it. +//! +//! This IR — commonly referred to as the *cleaned AST* — is modeled after the [AST][ast]. +//! +//! There are two kinds of transformation — *cleaning* — procedures: +//! +//! 1. Cleans [HIR][hir] types. Used for user-written code and inlined local re-exports +//! both found in the local crate. +//! 2. Cleans [`rustc_middle::ty`] types. Used for inlined cross-crate re-exports and anything +//! output by the trait solver (e.g., when synthesizing blanket and auto-trait impls). +//! They usually have `ty` or `middle` in their name. +//! +//! Their name is prefixed by `clean_`. +//! +//! Both the HIR and the `rustc_middle::ty` IR are quite removed from the source code. +//! The cleaned AST on the other hand is closer to it which simplifies the rendering process. +//! Furthermore, operating on a single IR instead of two avoids duplicating efforts down the line. +//! +//! This IR is consumed by both the HTML and the JSON backend. +//! +//! [^1]: Intermediate representation. mod auto_trait; mod blanket_impl; @@ -20,35 +40,31 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; -use rustc_hir_analysis::hir_ty_to_ty; -use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; +use rustc_hir_analysis::lower_ty; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; -use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, ExpnKind}; +use rustc_span::ExpnKind; use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; -use std::collections::hash_map::Entry; use std::collections::BTreeMap; -use std::hash::Hash; use std::mem; use thin_vec::ThinVec; -use crate::core::{self, DocContext, ImplTraitParam}; +use crate::core::DocContext; use crate::formats::item_type::ItemType; use crate::visit_ast::Module as DocModule; use utils::*; pub(crate) use self::types::*; -pub(crate) use self::utils::{get_auto_trait_and_blanket_impls, krate, register_res}; +pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls}; pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let mut items: Vec = vec![]; @@ -502,6 +518,7 @@ fn projection_to_path_segment<'tcx>( fn clean_generic_param_def<'tcx>( def: &ty::GenericParamDef, + defaults: ParamDefaults, cx: &mut DocContext<'tcx>, ) -> GenericParamDef { let (name, kind) = match def.kind { @@ -509,7 +526,9 @@ fn clean_generic_param_def<'tcx>( (def.name, GenericParamDefKind::Lifetime { outlives: ThinVec::new() }) } ty::GenericParamDefKind::Type { has_default, synthetic, .. } => { - let default = if has_default { + let default = if let ParamDefaults::Yes = defaults + && has_default + { Some(clean_middle_ty( ty::Binder::dummy(cx.tcx.type_of(def.def_id).instantiate_identity()), cx, @@ -542,11 +561,14 @@ fn clean_generic_param_def<'tcx>( Some(def.def_id), None, )), - default: match has_default { - true => Some(Box::new( + default: if let ParamDefaults::Yes = defaults + && has_default + { + Some(Box::new( cx.tcx.const_param_default(def.def_id).instantiate_identity().to_string(), - )), - false => None, + )) + } else { + None }, is_host_effect, }, @@ -556,6 +578,12 @@ fn clean_generic_param_def<'tcx>( GenericParamDef { name, def_id: def.def_id, kind } } +/// Whether to clean generic parameter defaults or not. +enum ParamDefaults { + Yes, + No, +} + fn clean_generic_param<'tcx>( cx: &mut DocContext<'tcx>, generics: Option<&hir::Generics<'tcx>>, @@ -626,7 +654,10 @@ fn is_impl_trait(param: &hir::GenericParam<'_>) -> bool { /// /// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information. fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { - matches!(param.kind, hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided }) + matches!( + param.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided(_) } + ) } pub(crate) fn clean_generics<'tcx>( @@ -756,34 +787,30 @@ fn clean_ty_generics<'tcx>( gens: &ty::Generics, preds: ty::GenericPredicates<'tcx>, ) -> Generics { - // Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses, - // since `Clean for ty::Predicate` would consume them. - let mut impl_trait = BTreeMap::>::default(); - - // Bounds in the type_params and lifetimes fields are repeated in the - // predicates field (see rustc_hir_analysis::collect::ty_generics), so remove - // them. - let stripped_params = gens + // Don't populate `cx.impl_trait_bounds` before cleaning where clauses, + // since `clean_predicate` would consume them. + let mut impl_trait = BTreeMap::>::default(); + + let params: ThinVec<_> = gens .params .iter() - .filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime if param.is_anonymous_lifetime() => None, - ty::GenericParamDefKind::Lifetime => Some(clean_generic_param_def(param, cx)), + .filter(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => !param.is_anonymous_lifetime(), ty::GenericParamDefKind::Type { synthetic, .. } => { if param.name == kw::SelfUpper { - assert_eq!(param.index, 0); - return None; + debug_assert_eq!(param.index, 0); + return false; } if synthetic { - impl_trait.insert(param.index.into(), vec![]); - return None; + impl_trait.insert(param.index, vec![]); + return false; } - Some(clean_generic_param_def(param, cx)) + true } - ty::GenericParamDefKind::Const { is_host_effect: true, .. } => None, - ty::GenericParamDefKind::Const { .. } => Some(clean_generic_param_def(param, cx)), + ty::GenericParamDefKind::Const { is_host_effect, .. } => !is_host_effect, }) - .collect::>(); + .map(|param| clean_generic_param_def(param, ParamDefaults::Yes, cx)) + .collect(); // param index -> [(trait DefId, associated type name & generics, term)] let mut impl_trait_proj = @@ -820,7 +847,7 @@ fn clean_ty_generics<'tcx>( })(); if let Some(param_idx) = param_idx - && let Some(bounds) = impl_trait.get_mut(¶m_idx.into()) + && let Some(bounds) = impl_trait.get_mut(¶m_idx) { let pred = clean_predicate(*pred, cx)?; @@ -844,7 +871,7 @@ fn clean_ty_generics<'tcx>( }) .collect::>(); - for (param, mut bounds) in impl_trait { + for (idx, mut bounds) in impl_trait { let mut has_sized = false; bounds.retain(|b| { if b.is_sized_bound(cx) { @@ -867,7 +894,6 @@ fn clean_ty_generics<'tcx>( bounds.insert(0, GenericBound::sized(cx)); } - let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() }; if let Some(proj) = impl_trait_proj.remove(&idx) { for (trait_did, name, rhs) in proj { let rhs = clean_middle_term(rhs, cx); @@ -875,61 +901,18 @@ fn clean_ty_generics<'tcx>( } } - cx.impl_trait_bounds.insert(param, bounds); + cx.impl_trait_bounds.insert(idx.into(), bounds); } // Now that `cx.impl_trait_bounds` is populated, we can process // remaining predicates which could contain `impl Trait`. - let mut where_predicates = - where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect::>(); - - // In the surface language, all type parameters except `Self` have an - // implicit `Sized` bound unless removed with `?Sized`. - // However, in the list of where-predicates below, `Sized` appears like a - // normal bound: It's either present (the type is sized) or - // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`). - // - // This is unsuitable for rendering. - // Thus, as a first step remove all `Sized` bounds that should be implicit. - // - // Note that associated types also have an implicit `Sized` bound but we - // don't actually know the set of associated types right here so that's - // handled when cleaning associated types. - let mut sized_params = FxHashSet::default(); - where_predicates.retain(|pred| { - if let WherePredicate::BoundPredicate { ty: Generic(g), bounds, .. } = pred - && *g != kw::SelfUpper - && bounds.iter().any(|b| b.is_sized_bound(cx)) - { - sized_params.insert(*g); - false - } else { - true - } - }); - - // As a final step, go through the type parameters again and insert a - // `?Sized` bound for each one we didn't find to be `Sized`. - for tp in &stripped_params { - if let types::GenericParamDefKind::Type { .. } = tp.kind - && !sized_params.contains(&tp.name) - { - where_predicates.push(WherePredicate::BoundPredicate { - ty: Type::Generic(tp.name), - bounds: vec![GenericBound::maybe_sized(cx)], - bound_params: Vec::new(), - }) - } - } - - // It would be nice to collect all of the bounds on a type and recombine - // them if possible, to avoid e.g., `where T: Foo, T: Bar, T: Sized, T: 'a` - // and instead see `where T: Foo + Bar + Sized + 'a` + let where_predicates = + where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect(); - Generics { - params: stripped_params, - where_predicates: simplify::where_clauses(cx, where_predicates), - } + let mut generics = Generics { params, where_predicates }; + simplify::sized_bounds(cx, &mut generics); + generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + generics } fn clean_ty_alias_inner_type<'tcx>( @@ -1257,12 +1240,8 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); - let item_type = clean_middle_ty( - ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), - cx, - None, - None, - ); + let item_type = + clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, default)), cx, None, None); AssocTypeItem( Box::new(TypeAlias { type_: clean_ty(default, cx), @@ -1303,12 +1282,8 @@ pub(crate) fn clean_impl_item<'tcx>( hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); let generics = clean_generics(impl_.generics, cx); - let item_type = clean_middle_ty( - ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), - cx, - None, - None, - ); + let item_type = + clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); AssocTypeItem( Box::new(TypeAlias { type_, @@ -1572,7 +1547,7 @@ fn first_non_private<'tcx>( path: &hir::Path<'tcx>, ) -> Option { let target_def_id = path.res.opt_def_id()?; - let (parent_def_id, ident) = match &path.segments[..] { + let (parent_def_id, ident) = match &path.segments { [] => return None, // Relative paths are available in the same scope as the owner. [leaf] => (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident), @@ -1687,7 +1662,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type } hir::QPath::Resolved(Some(qself), p) => { // Try to normalize `::T` to a type - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = lower_ty(cx.tcx, hir_ty); // `hir_to_ty` can return projection types with escaping vars for GATs, e.g. `<() as Trait>::Gat<'_>` if !ty.has_escaping_bound_vars() && let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) @@ -1713,7 +1688,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type })) } hir::QPath::TypeRelative(qself, segment) => { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = lower_ty(cx.tcx, hir_ty); let self_type = clean_ty(qself, cx); let (trait_, should_show_cast) = match ty.kind() { @@ -1827,6 +1802,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T BorrowedRef { lifetime, mutability: m.mutbl, type_: Box::new(clean_ty(m.ty, cx)) } } TyKind::Slice(ty) => Slice(Box::new(clean_ty(ty, cx))), + TyKind::Pat(ty, pat) => Type::Pat(Box::new(clean_ty(ty, cx)), format!("{pat:?}").into()), TyKind::Array(ty, ref length) => { let length = match length { hir::ArrayLen::Infer(..) => "_".to_string(), @@ -2053,13 +2029,17 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))), + ty::Pat(ty, pat) => Type::Pat( + Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), + format!("{pat:?}").into_boxed_str(), + ), ty::Array(ty, mut n) => { n = n.normalize(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) } - ty::RawPtr(mt) => { - RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None, None))) + ty::RawPtr(ty, mutbl) => { + RawPointer(mutbl, Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))) } ty::Ref(r, ty, mutbl) => BorrowedRef { lifetime: clean_middle_region(r), @@ -2739,12 +2719,8 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::TyAlias(hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); - let type_ = clean_middle_ty( - ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), - cx, - None, - None, - ); + let type_ = + clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); let generics = clean_generics(generics, cx); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; @@ -3051,22 +3027,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/simplify.rs b/src/librustdoc/clean/simplify.rs index c35fb9ec78875..5a3ccb6239a06 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -12,6 +12,7 @@ //! bounds by special casing scenarios such as these. Fun! use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::DefId; use rustc_middle::ty; use thin_vec::ThinVec; @@ -21,7 +22,7 @@ use crate::clean::GenericArgs as PP; use crate::clean::WherePredicate as WP; use crate::core::DocContext; -pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> ThinVec { +pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: ThinVec) -> ThinVec { // First, partition the where clause into its separate components. // // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to @@ -128,6 +129,48 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) .any(|did| trait_is_same_or_supertrait(cx, did, trait_)) } +pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generics) { + let mut sized_params = UnordSet::new(); + + // In the surface language, all type parameters except `Self` have an + // implicit `Sized` bound unless removed with `?Sized`. + // However, in the list of where-predicates below, `Sized` appears like a + // normal bound: It's either present (the type is sized) or + // absent (the type might be unsized) but never *maybe* (i.e. `?Sized`). + // + // This is unsuitable for rendering. + // Thus, as a first step remove all `Sized` bounds that should be implicit. + // + // Note that associated types also have an implicit `Sized` bound but we + // don't actually know the set of associated types right here so that + // should be handled when cleaning associated types. + generics.where_predicates.retain(|pred| { + if let WP::BoundPredicate { ty: clean::Generic(param), bounds, .. } = pred + && *param != rustc_span::symbol::kw::SelfUpper + && bounds.iter().any(|b| b.is_sized_bound(cx)) + { + sized_params.insert(*param); + false + } else { + true + } + }); + + // As a final step, go through the type parameters again and insert a + // `?Sized` bound for each one we didn't find to be `Sized`. + for param in &generics.params { + if let clean::GenericParamDefKind::Type { .. } = param.kind + && !sized_params.contains(¶m.name) + { + generics.where_predicates.push(WP::BoundPredicate { + ty: clean::Type::Generic(param.name), + bounds: vec![clean::GenericBound::maybe_sized(cx)], + bound_params: Vec::new(), + }) + } + } +} + /// Move bounds that are (likely) directly attached to generic parameters from the where-clause to /// the respective parameter. /// diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index a51f6360df2a4..af81cc6878e63 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -23,6 +23,7 @@ use rustc_hir::{BodyId, Mutability}; use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety; use rustc_index::IndexVec; use rustc_metadata::rendered_const; +use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{ @@ -266,8 +267,15 @@ impl ExternalCrate { let as_primitive = |res: Res| { let Res::Def(DefKind::Mod, def_id) = res else { return None }; tcx.get_attrs(def_id, sym::rustc_doc_primitive).find_map(|attr| { - // FIXME: should warn on unknown primitives? - Some((def_id, PrimitiveType::from_symbol(attr.value_str()?)?)) + let attr_value = attr.value_str().expect("syntax should already be validated"); + let Some(prim) = PrimitiveType::from_symbol(attr_value) else { + span_bug!( + attr.span, + "primitive `{attr_value}` is not a member of `PrimitiveType`" + ); + }; + + Some((def_id, prim)) }) }; @@ -400,7 +408,7 @@ impl Item { pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span { span_of_fragments(&self.attrs.doc_strings) - .unwrap_or_else(|| self.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner())) + .unwrap_or_else(|| self.span(tcx).map_or(DUMMY_SP, |span| span.inner())) } /// Combine all doc strings into a single value handling indentation and newlines as needed. @@ -592,13 +600,6 @@ impl Item { } } - pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option { - match self.const_stability(tcx)?.level { - StabilityLevel::Stable { since, .. } => Some(since), - StabilityLevel::Unstable { .. } => None, - } - } - pub(crate) fn is_non_exhaustive(&self) -> bool { self.attrs.other_attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) } @@ -1277,13 +1278,6 @@ impl GenericBound { false } - pub(crate) fn get_poly_trait(&self) -> Option { - if let GenericBound::TraitBound(ref p, _) = *self { - return Some(p.clone()); - } - None - } - pub(crate) fn get_trait_path(&self) -> Option { if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self { Some(trait_.clone()) @@ -1482,7 +1476,9 @@ pub(crate) enum Type { /// /// This is mostly Rustdoc's version of [`hir::Path`]. /// It has to be different because Rustdoc's [`PathSegment`] can contain cleaned generics. - Path { path: Path }, + Path { + path: Path, + }, /// A `dyn Trait` object: `dyn for<'a> Trait<'a> + Send + 'static` DynTrait(Vec, Option), /// A type parameter. @@ -1499,10 +1495,15 @@ pub(crate) enum Type { /// /// The `String` field is a stringified version of the array's length parameter. Array(Box, Box), + Pat(Box, Box), /// A raw pointer type: `*const i32`, `*mut i32` RawPointer(Mutability, Box), /// A reference type: `&i32`, `&'a mut Foo` - BorrowedRef { lifetime: Option, mutability: Mutability, type_: Box }, + BorrowedRef { + lifetime: Option, + mutability: Mutability, + type_: Box, + }, /// A qualified path to an associated item: `::Name` QPath(Box), @@ -1699,6 +1700,7 @@ impl Type { BareFunction(..) => PrimitiveType::Fn, Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, + Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { ref self_type, .. }) => return self_type.inner_def_id(cache), Generic(_) | Infer | ImplTrait(_) => return None, @@ -1752,6 +1754,7 @@ pub(crate) enum PrimitiveType { Str, Slice, Array, + Pat, Tuple, Unit, RawPointer, @@ -1804,8 +1807,10 @@ impl PrimitiveType { sym::bool => Some(PrimitiveType::Bool), sym::char => Some(PrimitiveType::Char), sym::str => Some(PrimitiveType::Str), + sym::f16 => Some(PrimitiveType::F16), sym::f32 => Some(PrimitiveType::F32), sym::f64 => Some(PrimitiveType::F64), + sym::f128 => Some(PrimitiveType::F128), sym::array => Some(PrimitiveType::Array), sym::slice => Some(PrimitiveType::Slice), sym::tuple => Some(PrimitiveType::Tuple), @@ -1838,8 +1843,10 @@ impl PrimitiveType { U32 => single(SimplifiedType::Uint(UintTy::U32)), U64 => single(SimplifiedType::Uint(UintTy::U64)), U128 => single(SimplifiedType::Uint(UintTy::U128)), + F16 => single(SimplifiedType::Float(FloatTy::F16)), F32 => single(SimplifiedType::Float(FloatTy::F32)), F64 => single(SimplifiedType::Float(FloatTy::F64)), + F128 => single(SimplifiedType::Float(FloatTy::F128)), Str => single(SimplifiedType::Str), Bool => single(SimplifiedType::Bool), Char => single(SimplifiedType::Char), @@ -1902,6 +1909,7 @@ impl PrimitiveType { Bool => sym::bool, Char => sym::char, Array => sym::array, + Pat => sym::pat, Slice => sym::slice, Tuple => sym::tuple, Unit => sym::unit, @@ -2552,7 +2560,7 @@ pub(crate) enum TypeBindingKind { } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 0b20cca3bcab2..aa923cc611734 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -1,5 +1,5 @@ -use crate::clean::auto_trait::AutoTraitFinder; -use crate::clean::blanket_impl::BlanketImplFinder; +use crate::clean::auto_trait::synthesize_auto_trait_impls; +use crate::clean::blanket_impl::synthesize_blanket_impls; use crate::clean::render_macro_matchers::render_macro_matcher; use crate::clean::{ clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, Crate, @@ -251,15 +251,6 @@ pub(super) fn clean_middle_path<'tcx>( } } -/// Remove the generic arguments from a path. -pub(crate) fn strip_path_generics(mut path: Path) -> Path { - for ps in path.segments.iter_mut() { - ps.args = GenericArgs::AngleBracketed { args: Default::default(), bindings: ThinVec::new() } - } - - path -} - pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String { let segments = match *p { hir::QPath::Resolved(_, path) => &path.segments, @@ -329,6 +320,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { elts.iter().map(|p| name_from_pat(p).to_string()).collect::>().join(", ") ), PatKind::Box(p) => return name_from_pat(&*p), + PatKind::Deref(p) => format!("deref!({})", name_from_pat(&*p)), PatKind::Ref(p, _) => return name_from_pat(&*p), PatKind::Lit(..) => { warn!( @@ -485,20 +477,20 @@ pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type { } } -pub(crate) fn get_auto_trait_and_blanket_impls( +pub(crate) fn synthesize_auto_trait_and_blanket_impls( cx: &mut DocContext<'_>, item_def_id: DefId, ) -> impl Iterator { let auto_impls = cx .sess() .prof - .generic_activity("get_auto_trait_impls") - .run(|| AutoTraitFinder::new(cx).get_auto_trait_impls(item_def_id)); + .generic_activity("synthesize_auto_trait_impls") + .run(|| synthesize_auto_trait_impls(cx, item_def_id)); let blanket_impls = cx .sess() .prof - .generic_activity("get_blanket_impls") - .run(|| BlanketImplFinder { cx }.get_blanket_impls(item_def_id)); + .generic_activity("synthesize_blanket_impls") + .run(|| synthesize_blanket_impls(cx, item_def_id)); auto_impls.into_iter().chain(blanket_impls) } @@ -588,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, + flag: Symbol, +) -> bool { + attrs .any(|attr| attr.meta_item_list().is_some_and(|l| rustc_attr::list_contains_name(&l, flag))) } @@ -598,7 +597,7 @@ pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool { /// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable. pub(crate) const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL"); pub(crate) static DOC_CHANNEL: Lazy<&'static str> = - Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').filter(|c| !c.is_empty()).next().unwrap()); + Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').find(|c| !c.is_empty()).unwrap()); /// Render a sequence of macro arms in a format suitable for displaying to the user /// as part of an item declaration. diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index be7e319bc79f4..465f969435d1b 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -460,8 +460,6 @@ impl Options { &matches.free[0] }); - let libs = - matches.opt_strs("L").iter().map(|s| SearchPath::from_cli_opt(early_dcx, s)).collect(); let externs = parse_externs(early_dcx, matches, &unstable_opts); let extern_html_root_urls = match parse_extern_html_roots(matches) { Ok(ex) => ex, @@ -625,6 +623,29 @@ impl Options { } let target = parse_target_triple(early_dcx, matches); + let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from); + + let sysroot = match &maybe_sysroot { + Some(s) => s.clone(), + None => { + rustc_session::filesearch::get_or_default_sysroot().expect("Failed finding sysroot") + } + }; + + let libs = matches + .opt_strs("L") + .iter() + .map(|s| { + SearchPath::from_cli_opt( + &sysroot, + &target, + early_dcx, + s, + #[allow(rustc::bad_opt_access)] // we have no `Session` here + unstable_opts.unstable_options, + ) + }) + .collect(); let show_coverage = matches.opt_present("show-coverage"); @@ -653,7 +674,6 @@ impl Options { let bin_crate = crate_types.contains(&CrateType::Executable); let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); let playground_url = matches.opt_str("playground-url"); - let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from); let module_sorting = if matches.opt_present("sort-modules-by-appearance") { ModuleSorting::DeclarationOrder } else { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 80a30ac272771..25b78d9598d88 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -509,7 +509,7 @@ impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> { /// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter /// for `impl Trait` in argument position. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub(crate) enum ImplTraitParam { DefId(DefId), ParamIndex(u32), diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c6eb7be08cd81..0ad4c9c234647 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -20,9 +20,9 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::{Target, TargetTriple}; -use tempfile::Builder as TempFileBuilder; use std::env; +use std::fs::File; use std::io::{self, Write}; use std::panic; use std::path::{Path, PathBuf}; @@ -31,6 +31,8 @@ use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use tempfile::{Builder as TempFileBuilder, TempDir}; + use crate::clean::{types::AttributesExt, Attributes}; use crate::config::Options as RustdocOptions; use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; @@ -48,7 +50,49 @@ pub(crate) struct GlobalTestOptions { pub(crate) attrs: Vec, } -pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { +pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> { + let mut file = File::create(file_path) + .map_err(|error| format!("failed to create args file: {error:?}"))?; + + // We now put the common arguments into the file we created. + let mut content = vec!["--crate-type=bin".to_string()]; + + for cfg in &options.cfgs { + content.push(format!("--cfg={cfg}")); + } + for check_cfg in &options.check_cfgs { + content.push(format!("--check-cfg={check_cfg}")); + } + + for lib_str in &options.lib_strs { + content.push(format!("-L{lib_str}")); + } + for extern_str in &options.extern_strs { + content.push(format!("--extern={extern_str}")); + } + content.push("-Ccodegen-units=1".to_string()); + for codegen_options_str in &options.codegen_options_strs { + content.push(format!("-C{codegen_options_str}")); + } + for unstable_option_str in &options.unstable_opts_strs { + content.push(format!("-Z{unstable_option_str}")); + } + + let content = content.join("\n"); + + file.write(content.as_bytes()) + .map_err(|error| format!("failed to write arguments to temporary file: {error:?}"))?; + Ok(()) +} + +fn get_doctest_dir() -> io::Result { + TempFileBuilder::new().prefix("rustdoctest").tempdir() +} + +pub(crate) fn run( + dcx: &rustc_errors::DiagCtxt, + options: RustdocOptions, +) -> Result<(), ErrorGuaranteed> { let input = config::Input::File(options.input.clone()); let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; @@ -118,6 +162,15 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let externs = options.externs.clone(); let json_unused_externs = options.json_unused_externs; + let temp_dir = match get_doctest_dir() + .map_err(|error| format!("failed to create temporary directory: {error:?}")) + { + Ok(temp_dir) => temp_dir, + Err(error) => return crate::wrap_return(dcx, Err(error)), + }; + let file_path = temp_dir.path().join("rustdoc-cfgs"); + crate::wrap_return(dcx, generate_args_file(&file_path, &options))?; + let (tests, unused_extern_reports, compiling_test_count) = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { @@ -134,6 +187,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { Some(compiler.sess.psess.clone_source_map()), None, enable_per_target_ignores, + file_path, ); let mut hir_collector = HirCollector { @@ -322,44 +376,39 @@ fn run_test( test: &str, crate_name: &str, line: usize, - rustdoc_options: RustdocOptions, + rustdoc_options: IndividualTestOptions, mut lang_string: LangString, no_run: bool, - runtool: Option, - runtool_args: Vec, - target: TargetTriple, opts: &GlobalTestOptions, edition: Edition, - outdir: DirState, path: PathBuf, - test_id: &str, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { - let (test, line_offset, supports_color) = - make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id)); + let (test, line_offset, supports_color) = make_test( + test, + Some(crate_name), + lang_string.test_harness, + opts, + edition, + Some(&rustdoc_options.test_id), + ); // Make sure we emit well-formed executable names for our target. - let rust_out = add_exe_suffix("rust_out".to_owned(), &target); - let output_file = outdir.path().join(rust_out); + let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); + let output_file = rustdoc_options.outdir.path().join(rust_out); let rustc_binary = rustdoc_options .test_builder .as_deref() .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg("--crate-type").arg("bin"); - for cfg in &rustdoc_options.cfgs { - compiler.arg("--cfg").arg(&cfg); - } - if !rustdoc_options.check_cfgs.is_empty() { - compiler.arg("-Z").arg("unstable-options"); - for check_cfg in &rustdoc_options.check_cfgs { - compiler.arg("--check-cfg").arg(&check_cfg); - } - } - if let Some(sysroot) = rustdoc_options.maybe_sysroot { - compiler.arg("--sysroot").arg(sysroot); + + compiler.arg(&format!("@{}", rustdoc_options.arg_file.display())); + + if let Some(sysroot) = &rustdoc_options.maybe_sysroot { + compiler.arg(format!("--sysroot={}", sysroot.display())); } + compiler.arg("--edition").arg(&edition.to_string()); compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); @@ -367,29 +416,17 @@ fn run_test( if lang_string.test_harness { compiler.arg("--test"); } - if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail { + if rustdoc_options.is_json_unused_externs_enabled && !lang_string.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); - compiler.arg("-Z").arg("unstable-options"); compiler.arg("-W").arg("unused_crate_dependencies"); + compiler.arg("-Z").arg("unstable-options"); } - for lib_str in &rustdoc_options.lib_strs { - compiler.arg("-L").arg(&lib_str); - } - for extern_str in &rustdoc_options.extern_strs { - compiler.arg("--extern").arg(&extern_str); - } - compiler.arg("-Ccodegen-units=1"); - for codegen_options_str in &rustdoc_options.codegen_options_strs { - compiler.arg("-C").arg(&codegen_options_str); - } - for unstable_option_str in &rustdoc_options.unstable_opts_strs { - compiler.arg("-Z").arg(&unstable_option_str); - } - if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() { + + if no_run && !lang_string.compile_fail && rustdoc_options.should_persist_doctests { compiler.arg("--emit=metadata"); } - compiler.arg("--target").arg(match target { + compiler.arg("--target").arg(match rustdoc_options.target { TargetTriple::TargetTriple(s) => s, TargetTriple::TargetJson { path_for_rustdoc, .. } => { path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string() @@ -485,10 +522,10 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = runtool { + if let Some(tool) = rustdoc_options.runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(runtool_args); + cmd.args(rustdoc_options.runtool_args); cmd.arg(output_file); } else { cmd = Command::new(output_file); @@ -897,6 +934,58 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { (before, after, crates) } +pub(crate) struct IndividualTestOptions { + test_builder: Option, + test_builder_wrappers: Vec, + is_json_unused_externs_enabled: bool, + should_persist_doctests: bool, + error_format: ErrorOutputType, + test_run_directory: Option, + nocapture: bool, + arg_file: PathBuf, + outdir: DirState, + runtool: Option, + runtool_args: Vec, + target: TargetTriple, + test_id: String, + maybe_sysroot: Option, +} + +impl IndividualTestOptions { + fn new(options: &RustdocOptions, arg_file: &Path, test_id: String) -> Self { + let outdir = if let Some(ref path) = options.persist_doctests { + let mut path = path.clone(); + path.push(&test_id); + + if let Err(err) = std::fs::create_dir_all(&path) { + eprintln!("Couldn't create directory for doctest executables: {err}"); + panic::resume_unwind(Box::new(())); + } + + DirState::Perm(path) + } else { + DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir")) + }; + + Self { + test_builder: options.test_builder.clone(), + test_builder_wrappers: options.test_builder_wrappers.clone(), + is_json_unused_externs_enabled: options.json_unused_externs.is_enabled(), + should_persist_doctests: options.persist_doctests.is_none(), + error_format: options.error_format, + test_run_directory: options.test_run_directory.clone(), + nocapture: options.nocapture, + arg_file: arg_file.into(), + outdir, + runtool: options.runtool.clone(), + runtool_args: options.runtool_args.clone(), + target: options.target.clone(), + test_id, + maybe_sysroot: options.maybe_sysroot.clone(), + } + } +} + pub(crate) trait Tester { fn add_test(&mut self, test: String, config: LangString, line: usize); fn get_line(&self) -> usize { @@ -941,6 +1030,7 @@ pub(crate) struct Collector { visited_tests: FxHashMap<(String, usize), usize>, unused_extern_reports: Arc>>, compiling_test_count: AtomicUsize, + arg_file: PathBuf, } impl Collector { @@ -952,6 +1042,7 @@ impl Collector { source_map: Option>, filename: Option, enable_per_target_ignores: bool, + arg_file: PathBuf, ) -> Collector { Collector { tests: Vec::new(), @@ -967,6 +1058,7 @@ impl Collector { visited_tests: FxHashMap::default(), unused_extern_reports: Default::default(), compiling_test_count: AtomicUsize::new(0), + arg_file, } } @@ -1009,13 +1101,9 @@ impl Tester for Collector { let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); let edition = config.edition.unwrap_or(self.rustdoc_options.edition); - let rustdoc_options = self.rustdoc_options.clone(); - let runtool = self.rustdoc_options.runtool.clone(); - let runtool_args = self.rustdoc_options.runtool_args.clone(); - let target = self.rustdoc_options.target.clone(); - let target_str = target.to_string(); + let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = config.no_run || rustdoc_options.no_run; + let no_run = config.no_run || self.rustdoc_options.no_run; if !config.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } @@ -1049,23 +1137,9 @@ impl Tester for Collector { self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0) }, ); - let outdir = if let Some(mut path) = rustdoc_options.persist_doctests.clone() { - path.push(&test_id); - if let Err(err) = std::fs::create_dir_all(&path) { - eprintln!("Couldn't create directory for doctest executables: {err}"); - panic::resume_unwind(Box::new(())); - } - - DirState::Perm(path) - } else { - DirState::Temp( - TempFileBuilder::new() - .prefix("rustdoctest") - .tempdir() - .expect("rustdoc needs a tempdir"), - ) - }; + let rustdoc_test_options = + IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); debug!("creating test {name}: {test}"); self.tests.push(test::TestDescAndFn { @@ -1096,17 +1170,12 @@ impl Tester for Collector { &test, &crate_name, line, - rustdoc_options, + rustdoc_test_options, config, no_run, - runtool, - runtool_args, - target, &opts, edition, - outdir, path, - &test_id, report_unused_externs, ); diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 9802097ea290c..a3b88a880f2af 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 { 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()) @@ -348,16 +346,28 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { { let desc = short_markdown_summary(&item.doc_value(), &item.link_names(self.cache)); + // For searching purposes, a re-export is a duplicate if: + // + // - It's either an inline, or a true re-export + // - It's got the same name + // - Both of them have the same exact path + let defid = (match &*item.kind { + &clean::ItemKind::ImportItem(ref import) => import.source.did, + _ => None, + }) + .or_else(|| item.item_id.as_def_id()); // In case this is a field from a tuple struct, we don't add it into // the search index because its name is something like "0", which is // not useful for rustdoc search. self.cache.search_index.push(IndexItem { ty, + defid, name: s, path: join_with_double_colon(path), desc, parent, parent_idx: None, + exact_path: None, impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = self.cache.parent_stack.last() { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 312765d3e6d03..f82b89fdd5f98 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1071,6 +1071,10 @@ fn fmt_type<'cx>( write!(f, "]") } }, + clean::Type::Pat(ref t, ref pat) => { + fmt::Display::fmt(&t.print(cx), f)?; + write!(f, " is {pat}") + } clean::Array(ref t, ref n) => match **t { clean::Generic(name) if !f.alternate() => primitive_link( f, diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index aa5998876d9ab..336d18a1df1c6 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -876,9 +876,10 @@ impl<'src> Classifier<'src> { }, Some(c) => c, }, - TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => { - Class::Ident(self.new_span(before, text)) - } + TokenKind::RawIdent + | TokenKind::UnknownPrefix + | TokenKind::InvalidPrefix + | TokenKind::InvalidIdent => Class::Ident(self.new_span(before, text)), TokenKind::Lifetime { .. } => Class::Lifetime, TokenKind::Eof => panic!("Eof in advance"), }; diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 64f0e096cd02e..5c5651f3ef0e6 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -35,13 +35,13 @@ use rustc_resolve::rustdoc::may_be_doc_link; use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; -use once_cell::sync::Lazy; use std::borrow::Cow; use std::collections::VecDeque; use std::fmt::Write; use std::iter::Peekable; use std::ops::{ControlFlow, Range}; use std::str::{self, CharIndices}; +use std::sync::OnceLock; use crate::clean::RenderedLink; use crate::doctest; @@ -1994,7 +1994,7 @@ pub struct IdMap { } // The map is pre-initialized and cloned each time to avoid reinitializing it repeatedly. -static DEFAULT_ID_MAP: Lazy, usize>> = Lazy::new(|| init_id_map()); +static DEFAULT_ID_MAP: OnceLock, usize>> = OnceLock::new(); fn init_id_map() -> FxHashMap, usize> { let mut map = FxHashMap::default(); @@ -2051,7 +2051,7 @@ fn init_id_map() -> FxHashMap, usize> { impl IdMap { pub fn new() -> Self { - IdMap { map: DEFAULT_ID_MAP.clone() } + IdMap { map: DEFAULT_ID_MAP.get_or_init(init_id_map).clone() } } pub(crate) fn derive + ToString>(&mut self, candidate: S) -> String { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index fc6f51e8272ae..db1119eca1d3d 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -78,8 +78,10 @@ pub(crate) struct Context<'tcx> { } // `Context` is cloned a lot, so we don't want the size to grow unexpectedly. -#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(not(windows), target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Context<'_>, 160); +#[cfg(all(windows, target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(Context<'_>, 168); /// Shared mutable state used in [`Context`] and elsewhere. pub(crate) struct SharedContext<'tcx> { @@ -231,7 +233,7 @@ impl<'tcx> Context<'tcx> { rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo), }; let mut page_buffer = Buffer::html(); - print_item(self, it, &mut page_buffer, &page); + print_item(self, it, &mut page_buffer); layout::render( &clone_shared.layout, &page, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 6c5040414bced..09a53affb14c0 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -56,7 +56,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_session::RustcVersion; use rustc_span::{ symbol::{sym, Symbol}, - BytePos, FileName, RealFileName, + BytePos, FileName, RealFileName, DUMMY_SP, }; use serde::ser::SerializeMap; use serde::{Serialize, Serializer}; @@ -111,11 +111,13 @@ pub(crate) enum RenderMode { #[derive(Debug)] pub(crate) struct IndexItem { pub(crate) ty: ItemType, + pub(crate) defid: Option, pub(crate) name: Symbol, pub(crate) path: String, pub(crate) desc: String, pub(crate) parent: Option, pub(crate) parent_idx: Option, + pub(crate) exact_path: Option, pub(crate) impl_id: Option, pub(crate) search_type: Option, pub(crate) aliases: Box<[Symbol]>, @@ -150,13 +152,13 @@ impl RenderType { string.push('{'); write_optional_id(self.id, string); string.push('{'); - for generic in &self.generics.as_ref().map(Vec::as_slice).unwrap_or_default()[..] { + for generic in &self.generics.as_deref().unwrap_or_default()[..] { generic.write_to_string(string); } string.push('}'); if self.bindings.is_some() { string.push('{'); - for binding in &self.bindings.as_ref().map(Vec::as_slice).unwrap_or_default()[..] { + for binding in &self.bindings.as_deref().unwrap_or_default()[..] { string.push('{'); binding.0.write_to_string(string); string.push('{'); @@ -184,40 +186,15 @@ pub(crate) enum RenderTypeId { impl RenderTypeId { pub fn write_to_string(&self, string: &mut String) { - // (sign, value) - let (sign, id): (bool, u32) = match &self { + let id: i32 = match &self { // 0 is a sentinel, everything else is one-indexed // concrete type - RenderTypeId::Index(idx) if *idx >= 0 => (false, (idx + 1isize).try_into().unwrap()), + RenderTypeId::Index(idx) if *idx >= 0 => (idx + 1isize).try_into().unwrap(), // generic type parameter - RenderTypeId::Index(idx) => (true, (-*idx).try_into().unwrap()), + RenderTypeId::Index(idx) => (*idx).try_into().unwrap(), _ => panic!("must convert render types to indexes before serializing"), }; - // zig-zag encoding - let value: u32 = (id << 1) | (if sign { 1 } else { 0 }); - // Self-terminating hex use capital letters for everything but the - // least significant digit, which is lowercase. For example, decimal 17 - // would be `` Aa `` if zig-zag encoding weren't used. - // - // Zig-zag encoding, however, stores the sign bit as the last bit. - // This means, in the last hexit, 1 is actually `c`, -1 is `b` - // (`a` is the imaginary -0), and, because all the bits are shifted - // by one, `` A` `` is actually 8 and `` Aa `` is -8. - // - // https://rust-lang.github.io/rustc-dev-guide/rustdoc-internals/search.html - // describes the encoding in more detail. - let mut shift: u32 = 28; - let mut mask: u32 = 0xF0_00_00_00; - while shift < 32 { - let hexit = (value & mask) >> shift; - if hexit != 0 || shift == 0 { - let hex = - char::try_from(if shift == 0 { '`' } else { '@' } as u32 + hexit).unwrap(); - string.push(hex); - } - shift = shift.wrapping_sub(4); - mask = mask >> 4; - } + search_index::encode::write_vlqhex_to_string(id, string); } } @@ -1019,32 +996,20 @@ fn assoc_method( /// consequence of the above rules. fn render_stability_since_raw_with_extra( w: &mut Buffer, - ver: Option, + stable_version: Option, const_stability: Option, - containing_ver: Option, - containing_const_ver: Option, extra_class: &str, ) -> bool { - let stable_version = if ver != containing_ver - && let Some(ver) = &ver - { - since_to_string(ver) - } else { - None - }; - let mut title = String::new(); let mut stability = String::new(); - if let Some(ver) = stable_version { - stability.push_str(ver.as_str()); - title.push_str(&format!("Stable since Rust version {ver}")); + if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) { + stability.push_str(&version); + title.push_str(&format!("Stable since Rust version {version}")); } let const_title_and_stability = match const_stability { - Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) - if Some(since) != containing_const_ver => - { + Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => { since_to_string(&since) .map(|since| (format!("const since {since}"), format!("const: {since}"))) } @@ -1099,17 +1064,8 @@ fn render_stability_since_raw( w: &mut Buffer, ver: Option, const_stability: Option, - containing_ver: Option, - containing_const_ver: Option, ) -> bool { - render_stability_since_raw_with_extra( - w, - ver, - const_stability, - containing_ver, - containing_const_ver, - "", - ) + render_stability_since_raw_with_extra(w, ver, const_stability, "") } fn render_assoc_item( @@ -1608,7 +1564,6 @@ fn render_impl( cx: &mut Context<'_>, item: &clean::Item, parent: &clean::Item, - containing_item: &clean::Item, link: AssocItemLink<'_>, render_mode: RenderMode, is_default_item: bool, @@ -1704,7 +1659,7 @@ fn render_impl( }) .map(|item| format!("{}.{name}", item.type_())); write!(w, "
"); - render_rightside(w, cx, item, containing_item, render_mode); + render_rightside(w, cx, item, render_mode); if trait_.is_some() { // Anchors are only used on trait impls. write!(w, "§"); @@ -1726,7 +1681,7 @@ fn render_impl( let source_id = format!("{item_type}.{name}"); let id = cx.derive_id(&source_id); write!(w, "
"); - render_rightside(w, cx, item, containing_item, render_mode); + render_rightside(w, cx, item, render_mode); if trait_.is_some() { // Anchors are only used on trait impls. write!(w, "§"); @@ -1812,7 +1767,6 @@ fn render_impl( cx, trait_item, if trait_.is_some() { &i.impl_item } else { parent }, - parent, link, render_mode, false, @@ -1828,7 +1782,6 @@ fn render_impl( t: &clean::Trait, i: &clean::Impl, parent: &clean::Item, - containing_item: &clean::Item, render_mode: RenderMode, rendering_params: ImplRenderingParameters, ) { @@ -1856,7 +1809,6 @@ fn render_impl( cx, trait_item, parent, - containing_item, assoc_link, render_mode, true, @@ -1879,7 +1831,6 @@ fn render_impl( t, i.inner_impl(), &i.impl_item, - parent, render_mode, rendering_params, ); @@ -1901,7 +1852,6 @@ fn render_impl( cx, i, parent, - parent, rendering_params.show_def_docs, use_absolute, aliases, @@ -1949,20 +1899,14 @@ fn render_impl( // Render the items that appear on the right side of methods, impls, and // associated types. For example "1.0.0 (const: 1.39.0) · source". -fn render_rightside( - w: &mut Buffer, - cx: &Context<'_>, - item: &clean::Item, - containing_item: &clean::Item, - render_mode: RenderMode, -) { +fn render_rightside(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, render_mode: RenderMode) { let tcx = cx.tcx(); // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove // this condition. - let (const_stability, const_stable_since) = match render_mode { - RenderMode::Normal => (item.const_stability(tcx), containing_item.const_stable_since(tcx)), - RenderMode::ForDeref { .. } => (None, None), + let const_stability = match render_mode { + RenderMode::Normal => item.const_stability(tcx), + RenderMode::ForDeref { .. } => None, }; let src_href = cx.src_href(item); let has_src_ref = src_href.is_some(); @@ -1972,8 +1916,6 @@ fn render_rightside( &mut rightside, item.stable_since(tcx), const_stability, - containing_item.stable_since(tcx), - const_stable_since, if has_src_ref { "" } else { " rightside" }, ); if let Some(link) = src_href { @@ -1995,7 +1937,6 @@ pub(crate) fn render_impl_summary( cx: &mut Context<'_>, i: &Impl, parent: &clean::Item, - containing_item: &clean::Item, show_def_docs: bool, use_absolute: Option, // This argument is used to reference same type with different paths to avoid duplication @@ -2010,7 +1951,7 @@ pub(crate) fn render_impl_summary( format!(" data-aliases=\"{}\"", aliases.join(",")) }; write!(w, "
"); - render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal); + render_rightside(w, cx, &i.impl_item, RenderMode::Normal); write!( w, "§\ @@ -2439,7 +2380,7 @@ fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &c let contents = match fs::read_to_string(&path) { Ok(contents) => contents, Err(err) => { - let span = item.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()); + let span = item.span(tcx).map_or(DUMMY_SP, |span| span.inner()); tcx.dcx().span_err(span, format!("failed to read file {}: {err}", path.display())); return false; } @@ -2506,7 +2447,7 @@ fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &c // Look for the example file in the source map if it exists, otherwise return a dummy span let file_span = (|| { let source_map = tcx.sess.source_map(); - let crate_src = tcx.sess.local_crate_source_file()?; + let crate_src = tcx.sess.local_crate_source_file()?.into_local_path()?; let abs_crate_src = crate_src.canonicalize().ok()?; let crate_root = abs_crate_src.parent()?.parent()?; let rel_path = path.strip_prefix(crate_root).ok()?; @@ -2520,7 +2461,7 @@ fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &c file.start_pos + BytePos(byte_max), )) })() - .unwrap_or(rustc_span::DUMMY_SP); + .unwrap_or(DUMMY_SP); let mut decoration_info = FxHashMap::default(); decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 5d4f1acc4b188..7de2aea1c04b3 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -31,11 +31,10 @@ use crate::html::format::{ display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause, visibility_print_with_space, Buffer, Ending, PrintWithSpace, }; -use crate::html::layout::Page; +use crate::html::highlight; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::{document_full, document_item_info}; use crate::html::url_parts_builder::UrlPartsBuilder; -use crate::html::{highlight, static_files}; use askama::Template; use itertools::Itertools; @@ -157,8 +156,6 @@ struct PathComponent { #[derive(Template)] #[template(path = "print_item.html")] struct ItemVars<'a> { - static_root_path: &'a str, - clipboard_svg: &'static static_files::StaticFile, typ: &'a str, name: &'a str, item_type: &'a str, @@ -178,12 +175,7 @@ fn print_where_clause_and_check<'a, 'tcx: 'a>( len_before != buffer.len() } -pub(super) fn print_item( - cx: &mut Context<'_>, - item: &clean::Item, - buf: &mut Buffer, - page: &Page<'_>, -) { +pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buffer) { debug_assert!(!item.is_stripped()); let typ = match *item.kind { clean::ModuleItem(_) => { @@ -222,8 +214,6 @@ pub(super) fn print_item( &mut stability_since_raw, item.stable_since(cx.tcx()), item.const_stability(cx.tcx()), - None, - None, ); let stability_since_raw: String = stability_since_raw.into_inner(); @@ -252,8 +242,6 @@ pub(super) fn print_item( }; let item_vars = ItemVars { - static_root_path: &page.get_static_root_path(), - clipboard_svg: &static_files::STATIC_FILES.clipboard_svg, typ, name: item.name.as_ref().unwrap().as_str(), item_type: &item.type_().to_string(), @@ -835,7 +823,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: write!(w, "
"); } write!(w, "
"); - render_rightside(w, cx, m, t, RenderMode::Normal); + render_rightside(w, cx, m, RenderMode::Normal); write!(w, "

"); render_assoc_item( w, @@ -1237,22 +1225,18 @@ fn item_opaque_ty( } fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - wrap_item(w, |w| { - write!( - w, - "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", - attrs = render_attributes_in_pre(it, "", cx), - vis = visibility_print_with_space(it, cx), - name = it.name.unwrap(), - generics = t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), - type_ = t.type_.print(cx), - ); - }); - } - - write_content(w, cx, it, t); + wrap_item(w, |w| { + write!( + w, + "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", + attrs = render_attributes_in_pre(it, "", cx), + vis = visibility_print_with_space(it, cx), + name = it.name.unwrap(), + generics = t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), + type_ = t.type_.print(cx), + ); + }); write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); @@ -1700,8 +1684,6 @@ fn item_variants( w, variant.stable_since(tcx), variant.const_stability(tcx), - it.stable_since(tcx), - it.const_stable_since(tcx), " rightside", ); w.write_str("

"); @@ -1728,6 +1710,8 @@ fn item_variants( } w.write_str("

"); + write!(w, "{}", document(cx, variant, Some(it), HeadingOffset::H4)); + let heading_and_fields = match &variant_data.kind { clean::VariantKind::Struct(s) => { // If there is no field to display, no need to add the heading. @@ -1789,8 +1773,6 @@ fn item_variants( } w.write_str("

, diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index e935dc7f5eb54..30b1ca6780062 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -121,7 +121,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { fn is_fn_ref(ty: Ty<'tcx>) -> Option<(DefId, GenericArgsRef<'tcx>)> { let referent_ty = match ty.kind() { ty::Ref(_, referent_ty, _) => Some(referent_ty), - ty::RawPtr(ty_and_mut) => Some(&ty_and_mut.ty), + ty::RawPtr(referent_ty, _) => Some(referent_ty), _ => None, }; referent_ty diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index a3a2108787a73..24832086b167b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -82,6 +82,7 @@ //! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const` //! that contain `AllocId`s. +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{intern_const_alloc_for_constprop, MemoryKind}; use rustc_const_eval::interpret::{ImmTy, InterpCx, OpTy, Projectable, Scalar}; use rustc_data_structures::fx::FxIndexSet; @@ -94,14 +95,13 @@ use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; use rustc_target::abi::{self, Abi, Size, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; use std::borrow::Cow; -use crate::dataflow_const_prop::DummyMachine; use crate::ssa::{AssignedValue, SsaLocals}; use either::Either; @@ -131,7 +131,7 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |local, value, location| { let value = match value { // We do not know anything of this assigned value. - AssignedValue::Arg | AssignedValue::Terminator(_) => None, + AssignedValue::Arg | AssignedValue::Terminator => None, // Try to get some insight. AssignedValue::Rvalue(rvalue) => { let value = state.simplify_rvalue(rvalue, location); @@ -355,7 +355,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } fn insert_tuple(&mut self, values: Vec) -> VnIndex { - self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::from_u32(0), values)) + self.insert(Value::Aggregate(AggregateTy::Tuple, VariantIdx::ZERO, values)) } #[instrument(level = "trace", skip(self), ret)] @@ -367,7 +367,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Repeat(..) => return None, Constant { ref value, disambiguator: _ } => { - self.ecx.eval_mir_constant(value, None, None).ok()? + self.ecx.eval_mir_constant(value, DUMMY_SP, None).ok()? } Aggregate(kind, variant, ref fields) => { let fields = fields @@ -451,11 +451,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AddressKind::Ref(bk) => Ty::new_ref( self.tcx, self.tcx.lifetimes.re_erased, - ty::TypeAndMut { ty: mplace.layout.ty, mutbl: bk.to_mutbl_lossy() }, + mplace.layout.ty, + bk.to_mutbl_lossy(), ), - AddressKind::Address(mutbl) => { - Ty::new_ptr(self.tcx, TypeAndMut { ty: mplace.layout.ty, mutbl }) - } + AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), }; let layout = self.ecx.layout_of(ty).ok()?; ImmTy::from_immediate(pointer, layout).into() @@ -488,7 +487,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { NullOp::OffsetOf(fields) => { layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() } - NullOp::UbCheck(_) => return None, + NullOp::UbChecks => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::try_from_uint(val, usize_layout)?; @@ -886,6 +885,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AggregateKind::Adt(did, ..) => tcx.def_kind(did) != DefKind::Enum, // Coroutines are never ZST, as they at least contain the implicit states. AggregateKind::Coroutine(..) => false, + AggregateKind::RawPtr(..) => bug!("MIR for RawPtr aggregate must have 2 fields"), }; if is_zst { @@ -911,6 +911,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Do not track unions. AggregateKind::Adt(_, _, _, _, Some(_)) => return None, + // FIXME: Do the extra work to GVN `from_raw_parts` + AggregateKind::RawPtr(..) => return None, }; let fields: Option> = fields @@ -1203,7 +1205,7 @@ impl<'tcx> VnState<'_, 'tcx> { // not give the same value as the former mention. && value.is_deterministic() { - return Some(ConstOperand { span: rustc_span::DUMMY_SP, user_ty: None, const_: value }); + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } let op = self.evaluated[index].as_ref()?; @@ -1220,7 +1222,7 @@ impl<'tcx> VnState<'_, 'tcx> { assert!(!value.may_have_provenance(self.tcx, op.layout.size)); let const_ = Const::Val(value, op.layout.ty); - Some(ConstOperand { span: rustc_span::DUMMY_SP, user_ty: None, const_ }) + Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ }) } /// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`, diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f6a0945c22297..7a3b08f82f619 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; -use rustc_session::config::OptLevel; +use rustc_session::config::{DebugInfo, OptLevel}; use rustc_span::source_map::Spanned; use rustc_span::sym; use rustc_target::abi::FieldIdx; @@ -165,7 +165,7 @@ impl<'tcx> Inliner<'tcx> { caller_body: &mut Body<'tcx>, callsite: &CallSite<'tcx>, ) -> Result, &'static str> { - self.check_mir_is_available(caller_body, &callsite.callee)?; + self.check_mir_is_available(caller_body, callsite.callee)?; let callee_attrs = self.tcx.codegen_fn_attrs(callsite.callee.def_id()); let cross_crate_inlinable = self.tcx.cross_crate_inlinable(callsite.callee.def_id()); @@ -213,6 +213,7 @@ impl<'tcx> Inliner<'tcx> { MirPhase::Runtime(RuntimePhase::Optimized), self.param_env, &callee_body, + &caller_body, ) .is_empty() { @@ -297,7 +298,7 @@ impl<'tcx> Inliner<'tcx> { fn check_mir_is_available( &self, caller_body: &Body<'tcx>, - callee: &Instance<'tcx>, + callee: Instance<'tcx>, ) -> Result<(), &'static str> { let caller_def_id = caller_body.source.def_id(); let callee_def_id = callee.def_id(); @@ -323,7 +324,7 @@ impl<'tcx> Inliner<'tcx> { // do not need to catch this here, we can wait until the inliner decides to continue // inlining a second time. InstanceDef::VTableShim(_) - | InstanceDef::ReifyShim(_) + | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::ConstructCoroutineInClosureShim { .. } @@ -331,7 +332,8 @@ impl<'tcx> Inliner<'tcx> { | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) | InstanceDef::ThreadLocalShim(..) - | InstanceDef::FnPtrAddrShim(..) => return Ok(()), + | InstanceDef::FnPtrAddrShim(..) + | InstanceDef::AsyncDropGlueCtorShim(..) => return Ok(()), } if self.tcx.is_constructor(callee_def_id) { @@ -353,7 +355,7 @@ impl<'tcx> Inliner<'tcx> { // If we know for sure that the function we're calling will itself try to // call us, then we avoid inlining that function. - if self.tcx.mir_callgraph_reachable((*callee, caller_def_id.expect_local())) { + if self.tcx.mir_callgraph_reachable((callee, caller_def_id.expect_local())) { return Err("caller might be reachable from callee (query cycle avoidance)"); } @@ -565,7 +567,8 @@ impl<'tcx> Inliner<'tcx> { mut callee_body: Body<'tcx>, ) { let terminator = caller_body[callsite.block].terminator.take().unwrap(); - let TerminatorKind::Call { args, destination, unwind, target, .. } = terminator.kind else { + let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind + else { bug!("unexpected terminator kind {:?}", terminator.kind); }; @@ -697,7 +700,19 @@ impl<'tcx> Inliner<'tcx> { // Insert all of the (mapped) parts of the callee body into the caller. caller_body.local_decls.extend(callee_body.drain_vars_and_temps()); caller_body.source_scopes.extend(&mut callee_body.source_scopes.drain(..)); - caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + if self + .tcx + .sess + .opts + .unstable_opts + .inline_mir_preserve_debug + .unwrap_or(self.tcx.sess.opts.debuginfo != DebugInfo::None) + { + // Note that we need to preserve these in the standard library so that + // people working on rust can build with or without debuginfo while + // still getting consistent results from the mir-opt tests. + caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + } caller_body.basic_blocks_mut().extend(callee_body.basic_blocks_mut().drain(..)); caller_body[callsite.block].terminator = Some(Terminator { @@ -705,18 +720,31 @@ impl<'tcx> Inliner<'tcx> { kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, }); - // Copy only unevaluated constants from the callee_body into the caller_body. - // Although we are only pushing `ConstKind::Unevaluated` consts to - // `required_consts`, here we may not only have `ConstKind::Unevaluated` - // because we are calling `instantiate_and_normalize_erasing_regions`. - caller_body.required_consts.extend(callee_body.required_consts.iter().copied().filter( - |&ct| match ct.const_ { - Const::Ty(_) => { - bug!("should never encounter ty::UnevaluatedConst in `required_consts`") - } - Const::Val(..) | Const::Unevaluated(..) => true, - }, - )); + // Copy required constants from the callee_body into the caller_body. Although we are only + // pushing unevaluated consts to `required_consts`, here they may have been evaluated + // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. + caller_body.required_consts.extend( + callee_body.required_consts.into_iter().filter(|ct| ct.const_.is_required_const()), + ); + // Now that we incorporated the callee's `required_consts`, we can remove the callee from + // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does + // some extra work here to save the monomorphization collector work later. It helps a lot, + // since monomorphization can avoid a lot of work when the "mentioned items" are similar to + // the actually used items. By doing this we can entirely avoid visiting the callee! + // We need to reconstruct the `required_item` for the callee so that we can find and + // remove it. + let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx)); + if let Some(idx) = + caller_body.mentioned_items.iter().position(|item| item.node == callee_item) + { + // We found the callee, so remove it and add its items instead. + caller_body.mentioned_items.remove(idx); + caller_body.mentioned_items.extend(callee_body.mentioned_items); + } else { + // If we can't find the callee, there's no point in adding its items. Probably it + // already got removed by being inlined elsewhere in the same function, so we already + // took its items. + } } fn make_call_args( @@ -1051,13 +1079,14 @@ fn try_instance_mir<'tcx>( tcx: TyCtxt<'tcx>, instance: InstanceDef<'tcx>, ) -> Result<&'tcx Body<'tcx>, &'static str> { - if let ty::InstanceDef::DropGlue(_, Some(ty)) = instance + if let ty::InstanceDef::DropGlue(_, Some(ty)) + | ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) = instance && let ty::Adt(def, args) = ty.kind() { let fields = def.all_fields(); for field in fields { let field_ty = field.ty(tcx, args); - if field_ty.has_param() && field_ty.has_projections() { + if field_ty.has_param() && field_ty.has_aliases() { return Err("cannot build drop shim for polymorphic type"); } } diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index f2b6dcac58632..8c5f965108bdc 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -5,6 +5,7 @@ use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, GenericArgsRef, InstanceDef, TyCtxt}; use rustc_session::Limit; +use rustc_span::sym; // FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking // this query ridiculously often. @@ -84,7 +85,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( // again, a function item can end up getting inlined. Thus we'll be able to cause // a cycle that way InstanceDef::VTableShim(_) - | InstanceDef::ReifyShim(_) + | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::ConstructCoroutineInClosureShim { .. } @@ -93,8 +94,10 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( | InstanceDef::CloneShim(..) => {} // This shim does not call any other functions, thus there can be no recursion. - InstanceDef::FnPtrAddrShim(..) => continue, - InstanceDef::DropGlue(..) => { + InstanceDef::FnPtrAddrShim(..) => { + continue; + } + InstanceDef::DropGlue(..) | InstanceDef::AsyncDropGlueCtorShim(..) => { // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this // needs some more analysis. @@ -164,11 +167,20 @@ pub(crate) fn mir_inliner_callees<'tcx>( let mut calls = FxIndexSet::default(); for bb_data in body.basic_blocks.iter() { let terminator = bb_data.terminator(); - if let TerminatorKind::Call { func, .. } = &terminator.kind { + if let TerminatorKind::Call { func, args: call_args, .. } = &terminator.kind { let ty = func.ty(&body.local_decls, tcx); - let call = match ty.kind() { - ty::FnDef(def_id, args) => (*def_id, *args), - _ => continue, + let ty::FnDef(def_id, generic_args) = ty.kind() else { + continue; + }; + let call = if tcx.is_intrinsic(*def_id, sym::const_eval_select) { + let func = &call_args[2].node; + let ty = func.ty(&body.local_decls, tcx); + let ty::FnDef(def_id, generic_args) = ty.kind() else { + continue; + }; + (*def_id, *generic_args) + } else { + (*def_id, *generic_args) }; calls.insert(call); } diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 6b33d81c1c412..fd768cc96ae3a 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -1,10 +1,12 @@ //! Performs various peephole optimizations. use crate::simplify::simplify_duplicate_switch_targets; +use rustc_ast::attr; use rustc_middle::mir::*; use rustc_middle::ty::layout; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt}; +use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; @@ -22,13 +24,19 @@ impl<'tcx> MirPass<'tcx> for InstSimplify { local_decls: &body.local_decls, param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()), }; + let preserve_ub_checks = + attr::contains_name(tcx.hir().krate_attrs(), sym::rustc_preserve_ub_checks); for block in body.basic_blocks.as_mut() { for statement in block.statements.iter_mut() { match statement.kind { StatementKind::Assign(box (_place, ref mut rvalue)) => { + if !preserve_ub_checks { + ctx.simplify_ub_check(&statement.source_info, rvalue); + } ctx.simplify_bool_cmp(&statement.source_info, rvalue); ctx.simplify_ref_deref(&statement.source_info, rvalue); ctx.simplify_len(&statement.source_info, rvalue); + ctx.simplify_ptr_aggregate(&statement.source_info, rvalue); ctx.simplify_cast(rvalue); } _ => {} @@ -51,8 +59,17 @@ struct InstSimplifyContext<'tcx, 'a> { impl<'tcx> InstSimplifyContext<'tcx, '_> { fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool { + self.should_simplify_custom(source_info, "Rvalue", rvalue) + } + + fn should_simplify_custom( + &self, + source_info: &SourceInfo, + label: &str, + value: impl std::fmt::Debug, + ) -> bool { self.tcx.consider_optimizing(|| { - format!("InstSimplify - Rvalue: {rvalue:?} SourceInfo: {source_info:?}") + format!("InstSimplify - {label}: {value:?} SourceInfo: {source_info:?}") }) } @@ -104,7 +121,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { if a.const_.ty().is_bool() { a.const_.try_to_bool() } else { None } } - /// Transform "&(*a)" ==> "a". + /// Transform `&(*a)` ==> `a`. fn simplify_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Ref(_, _, place) = rvalue { if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() { @@ -124,7 +141,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } - /// Transform "Len([_; N])" ==> "N". + /// Transform `Len([_; N])` ==> `N`. fn simplify_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Len(ref place) = *rvalue { let place_ty = place.ty(self.local_decls, self.tcx).ty; @@ -140,6 +157,38 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } + /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. + fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue + { + let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx); + if meta_ty.is_unit() { + // The mutable borrows we're holding prevent printing `rvalue` here + if !self.should_simplify_custom( + source_info, + "Aggregate::RawPtr", + (&pointee_ty, *mutability, &fields), + ) { + return; + } + + let mut fields = std::mem::take(fields); + let _meta = fields.pop().unwrap(); + let data = fields.pop().unwrap(); + let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability); + *rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty); + } + } + } + + fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue { + let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks()); + let constant = ConstOperand { span: source_info.span, const_, user_ty: None }; + *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); + } + } + fn simplify_cast(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Cast(kind, operand, cast_ty) = rvalue { let operand_ty = operand.ty(self.local_decls, self.tcx); diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 6629face94041..a458297210db8 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -36,6 +36,7 @@ //! cost by `MAX_COST`. use rustc_arena::DroplessArena; +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::BitSet; @@ -50,7 +51,6 @@ use rustc_span::DUMMY_SP; use rustc_target::abi::{TagEncoding, Variants}; use crate::cost_checker::CostChecker; -use crate::dataflow_const_prop::DummyMachine; pub struct JumpThreading; @@ -417,7 +417,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Operand::Constant(constant) => { let constant = - self.ecx.eval_mir_constant(&constant.const_, Some(constant.span), None).ok()?; + self.ecx.eval_mir_constant(&constant.const_, constant.span, None).ok()?; self.process_constant(bb, lhs, constant, state); } // Transfer the conditions on the copied rhs. diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 4bca437ea6f27..90c1c7ba63b45 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -6,13 +6,14 @@ use std::fmt::Debug; +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ format_interp_error, ImmTy, InterpCx, InterpResult, Projectable, Scalar, }; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::HirId; -use rustc_index::{bit_set::BitSet, Idx, IndexVec}; +use rustc_index::{bit_set::BitSet, IndexVec}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; @@ -20,7 +21,6 @@ use rustc_middle::ty::{self, ConstInt, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisi use rustc_span::Span; use rustc_target::abi::{Abi, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx}; -use crate::dataflow_const_prop::DummyMachine; use crate::errors::{AssertLint, AssertLintKind}; use crate::MirLint; @@ -124,10 +124,8 @@ impl<'tcx> Value<'tcx> { fields.ensure_contains_elem(*idx, || Value::Uninit) } (PlaceElem::Field(..), val @ Value::Uninit) => { - *val = Value::Aggregate { - variant: VariantIdx::new(0), - fields: Default::default(), - }; + *val = + Value::Aggregate { variant: VariantIdx::ZERO, fields: Default::default() }; val.project_mut(&[*proj])? } _ => return None, @@ -261,7 +259,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // manually normalized. let val = self.tcx.try_normalize_erasing_regions(self.param_env, c.const_).ok()?; - self.use_ecx(|this| this.ecx.eval_mir_constant(&val, Some(c.span), None))? + self.use_ecx(|this| this.ecx.eval_mir_constant(&val, c.span, None))? .as_mplace_or_imm() .right() } @@ -572,7 +570,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { self.use_ecx(|this| this.ecx.overflowing_binary_op(bin_op, &left, &right))?; let overflowed = ImmTy::from_bool(overflowed, self.tcx); Value::Aggregate { - variant: VariantIdx::new(0), + variant: VariantIdx::ZERO, fields: [Value::from(val), overflowed.into()].into_iter().collect(), } } @@ -585,32 +583,21 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { val.into() } - Aggregate(ref kind, ref fields) => { - // Do not const prop union fields as they can be - // made to produce values that don't match their - // underlying layout's type (see ICE #121534). - // If the last element of the `Adt` tuple - // is `Some` it indicates the ADT is a union - if let AggregateKind::Adt(_, _, _, _, Some(_)) = **kind { - return None; - }; - Value::Aggregate { - fields: fields - .iter() - .map(|field| { - self.eval_operand(field).map_or(Value::Uninit, Value::Immediate) - }) - .collect(), - variant: match **kind { - AggregateKind::Adt(_, variant, _, _, _) => variant, - AggregateKind::Array(_) - | AggregateKind::Tuple - | AggregateKind::Closure(_, _) - | AggregateKind::Coroutine(_, _) - | AggregateKind::CoroutineClosure(_, _) => VariantIdx::new(0), - }, - } - } + Aggregate(ref kind, ref fields) => Value::Aggregate { + fields: fields + .iter() + .map(|field| self.eval_operand(field).map_or(Value::Uninit, Value::Immediate)) + .collect(), + variant: match **kind { + AggregateKind::Adt(_, variant, _, _, _) => variant, + AggregateKind::Array(_) + | AggregateKind::Tuple + | AggregateKind::RawPtr(_, _) + | AggregateKind::Closure(_, _) + | AggregateKind::Coroutine(_, _) + | AggregateKind::CoroutineClosure(_, _) => VariantIdx::ZERO, + }, + }, Repeat(ref op, n) => { trace!(?op, ?n); @@ -639,7 +626,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { NullOp::OffsetOf(fields) => { op_layout.offset_of_subfield(self, fields.iter()).bytes() } - NullOp::UbCheck(_) => return None, + NullOp::UbChecks => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() } @@ -798,7 +785,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { if let Some(ref value) = self.eval_operand(discr) && let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value)) && let Ok(constant) = value_const.try_to_int() - && let Ok(constant) = constant.to_bits(constant.size()) + && let Ok(constant) = constant.try_to_bits(constant.size()) { // We managed to evaluate the discriminant, so we know we only need to visit // one target. @@ -897,13 +884,20 @@ impl CanConstProp { }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { let ty = body.local_decls[local].ty; - match tcx.layout_of(param_env.and(ty)) { - Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {} - // Either the layout fails to compute, then we can't use this local anyway - // or the local is too large, then we don't want to. - _ => { - *val = ConstPropMode::NoPropagation; - continue; + if ty.is_union() { + // Unions are incompatible with the current implementation of + // const prop because Rust has no concept of an active + // variant of a union + *val = ConstPropMode::NoPropagation; + } else { + match tcx.layout_of(param_env.and(ty)) { + Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {} + // Either the layout fails to compute, then we can't use this local anyway + // or the local is too large, then we don't want to. + _ => { + *val = ConstPropMode::NoPropagation; + continue; + } } } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 4551380152217..e69c5da757ed4 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -4,11 +4,9 @@ #![feature(cow_is_borrowed)] #![feature(decl_macro)] #![feature(impl_trait_in_assoc_type)] -#![feature(inline_const)] #![feature(is_sorted)] #![feature(let_chains)] #![feature(map_try_insert)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![feature(never_type)] #![feature(option_get_or_insert_default)] #![feature(round_char_boundary)] @@ -54,7 +52,6 @@ mod add_moves_for_packed_drops; mod add_retag; mod check_const_item_mutation; mod check_packed_ref; -pub mod check_unsafety; mod remove_place_mention; // This pass is public to allow external drivers to perform MIR cleanup mod add_subtyping_projections; @@ -89,6 +86,7 @@ mod lint; mod lower_intrinsics; mod lower_slice_len; mod match_branches; +mod mentioned_items; mod multiple_return_terminators; mod normalize_array_len; mod nrvo; @@ -110,7 +108,7 @@ pub mod simplify; mod simplify_branches; mod simplify_comparison_integral; mod sroa; -mod uninhabited_enum_branching; +mod unreachable_enum_branching; mod unreachable_prop; use rustc_const_eval::transform::check_consts::{self, ConstCx}; @@ -120,14 +118,13 @@ use rustc_mir_dataflow::rustc_peek; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { - check_unsafety::provide(providers); coverage::query::provide(providers); ffi_unwind_calls::provide(providers); shim::provide(providers); cross_crate_inline::provide(providers); providers.queries = query::Providers { mir_keys, - mir_const, + mir_built, mir_const_qualif, mir_promoted, mir_drops_elaborated_and_const_checked, @@ -259,9 +256,9 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { // N.B., this `borrow()` is guaranteed to be valid (i.e., the value // cannot yet be stolen), because `mir_promoted()`, which steals - // from `mir_const()`, forces this query to execute before + // from `mir_built()`, forces this query to execute before // performing the steal. - let body = &tcx.mir_const(def).borrow(); + let body = &tcx.mir_built(def).borrow(); if body.return_ty().references_error() { // It's possible to reach here without an error being emitted (#121103). @@ -279,19 +276,8 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { validator.qualifs_in_return_place() } -/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts! -/// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query). -/// We used to have this for pre-miri MIR based const eval. -fn mir_const(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { - // MIR unsafety check uses the raw mir, so make sure it is run. - if !tcx.sess.opts.unstable_opts.thir_unsafeck { - tcx.ensure_with_value().mir_unsafety_check_result(def); - } - - // has_ffi_unwind_calls query uses the raw mir, so make sure it is run. - tcx.ensure_with_value().has_ffi_unwind_calls(def); - - let mut body = tcx.mir_built(def).steal(); +fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { + let mut body = tcx.build_mir(def); pass_manager::dump_mir_for_phase_change(tcx, &body); @@ -339,11 +325,15 @@ fn mir_promoted( | DefKind::AnonConst => tcx.mir_const_qualif(def), _ => ConstQualifs::default(), }; - let mut body = tcx.mir_const(def).steal(); + // has_ffi_unwind_calls query uses the raw mir, so make sure it is run. + tcx.ensure_with_value().has_ffi_unwind_calls(def); + let mut body = tcx.mir_built(def).steal(); if let Some(error_reported) = const_qualifs.tainted_by_errors { body.tainted_by_errors = Some(error_reported); } + // Collect `required_consts` *before* promotion, so if there are any consts being promoted + // we still add them to the list in the outer MIR body. let mut required_consts = Vec::new(); let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts); for (bb, bb_data) in traversal::reverse_postorder(&body) { @@ -507,7 +497,7 @@ fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let passes: &[&dyn MirPass<'tcx>] = &[ &cleanup_post_borrowck::CleanupPostBorrowck, &remove_noop_landing_pads::RemoveNoopLandingPads, - &simplify::SimplifyCfg::EarlyOpt, + &simplify::SimplifyCfg::PostAnalysis, &deref_separator::Derefer, ]; @@ -529,11 +519,11 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // AddMovesForPackedDrops needs to run after drop // elaboration. &add_moves_for_packed_drops::AddMovesForPackedDrops, - // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, + // `AddRetag` needs to run after `ElaborateDrops` but before `ElaborateBoxDerefs`. Otherwise it should run fairly late, // but before optimizations begin. + &add_retag::AddRetag, &elaborate_box_derefs::ElaborateBoxDerefs, &coroutine::StateTransform, - &add_retag::AddRetag, &Lint(known_panics_lint::KnownPanicsLint), ]; pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial))); @@ -544,7 +534,7 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let passes: &[&dyn MirPass<'tcx>] = &[ &lower_intrinsics::LowerIntrinsics, &remove_place_mention::RemovePlaceMention, - &simplify::SimplifyCfg::ElaborateDrops, + &simplify::SimplifyCfg::PreOptimizations, ]; pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup))); @@ -566,6 +556,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { tcx, body, &[ + // Before doing anything, remember which items are being mentioned so that the set of items + // visited does not depend on the optimization level. + &mentioned_items::MentionedItems, + // Add some UB checks before any UB gets optimized away. &check_alignment::CheckAlignment, // Before inlining: trim down MIR with passes to reduce inlining work. @@ -580,9 +574,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &remove_zsts::RemoveZsts, &remove_unneeded_drops::RemoveUnneededDrops, // Type instantiation may create uninhabited enums. - &uninhabited_enum_branching::UninhabitedEnumBranching, + // Also eliminates some unreachable branches based on variants of enums. + &unreachable_enum_branching::UnreachableEnumBranching, &unreachable_prop::UnreachablePropagation, - &o1(simplify::SimplifyCfg::AfterUninhabitedEnumBranching), + &o1(simplify::SimplifyCfg::AfterUnreachableEnumBranching), // Inlining may have introduced a lot of redundant code and a large move pattern. // Now, we need to shrink the generated MIR. @@ -633,12 +628,6 @@ fn optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> &Body<'_> { } fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { - if tcx.intrinsic(did).is_some_and(|i| i.must_be_overridden) { - span_bug!( - tcx.def_span(did), - "this intrinsic must be overridden by the codegen backend, it has no meaningful body", - ) - } if tcx.is_constructor(did.to_def_id()) { // There's no reason to run all of the MIR passes on constructors when // we can just output the MIR we want directly. This also saves const diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 1bab240ef50f2..da63fcf23d9ee 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -20,30 +20,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::unreachable => { terminator.kind = TerminatorKind::Unreachable; } - sym::check_language_ub => { + sym::ub_checks => { let target = target.unwrap(); block.statements.push(Statement { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::NullaryOp( - NullOp::UbCheck(UbKind::LanguageUb), - tcx.types.bool, - ), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; - } - sym::check_library_ub => { - let target = target.unwrap(); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::NullaryOp( - NullOp::UbCheck(UbKind::LibraryUb), - tcx.types.bool, - ), + Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool), ))), }); terminator.kind = TerminatorKind::Goto { target }; @@ -107,6 +90,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul + | sym::three_way_compare | sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul @@ -126,6 +110,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::wrapping_add => BinOp::Add, sym::wrapping_sub => BinOp::Sub, sym::wrapping_mul => BinOp::Mul, + sym::three_way_compare => BinOp::Cmp, sym::unchecked_add => BinOp::AddUnchecked, sym::unchecked_sub => BinOp::SubUnchecked, sym::unchecked_mul => BinOp::MulUnchecked, @@ -302,6 +287,34 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Unreachable; } } + sym::aggregate_raw_ptr => { + let Ok([data, meta]) = <[_; 2]>::try_from(std::mem::take(args)) else { + span_bug!( + terminator.source_info.span, + "Wrong number of arguments for aggregate_raw_ptr intrinsic", + ); + }; + let target = target.unwrap(); + let pointer_ty = generic_args.type_at(0); + let kind = if let ty::RawPtr(pointee_ty, mutability) = pointer_ty.kind() { + AggregateKind::RawPtr(*pointee_ty, *mutability) + } else { + span_bug!( + terminator.source_info.span, + "Return type of aggregate_raw_ptr intrinsic must be a raw pointer", + ); + }; + let fields = [data.node, meta.node]; + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::Aggregate(Box::new(kind), fields.into()), + ))), + }); + + terminator.kind = TerminatorKind::Goto { target }; + } _ => {} } } diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 8137525a33243..2267a621a834a 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -21,7 +21,7 @@ impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls { pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let language_items = tcx.lang_items(); let Some(slice_len_fn_item_def_id) = language_items.slice_len_fn() else { - // there is no language item to compare to :) + // there is no lang item to compare to :) return; }; diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 6d4332793af31..1411d9be22365 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -1,11 +1,128 @@ +use rustc_index::IndexSlice; +use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{ParamEnv, ScalarInt, Ty, TyCtxt}; +use rustc_target::abi::Size; use std::iter; use super::simplify::simplify_cfg; pub struct MatchBranchSimplification; +impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + sess.mir_opt_level() >= 1 + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let def_id = body.source.def_id(); + let param_env = tcx.param_env_reveal_all_normalized(def_id); + + let mut should_cleanup = false; + for i in 0..body.basic_blocks.len() { + let bbs = &*body.basic_blocks; + let bb_idx = BasicBlock::from_usize(i); + if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {def_id:?} ")) { + continue; + } + + match bbs[bb_idx].terminator().kind { + TerminatorKind::SwitchInt { + discr: ref _discr @ (Operand::Copy(_) | Operand::Move(_)), + ref targets, + .. + // We require that the possible target blocks don't contain this block. + } if !targets.all_targets().contains(&bb_idx) => {} + // Only optimize switch int statements + _ => continue, + }; + + if SimplifyToIf.simplify(tcx, body, bb_idx, param_env).is_some() { + should_cleanup = true; + continue; + } + // unsound: https://github.com/rust-lang/rust/issues/124150 + if tcx.sess.opts.unstable_opts.unsound_mir_opts + && SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() + { + should_cleanup = true; + continue; + } + } + + if should_cleanup { + simplify_cfg(body); + } + } +} + +trait SimplifyMatch<'tcx> { + /// Simplifies a match statement, returning true if the simplification succeeds, false otherwise. + /// Generic code is written here, and we generally don't need a custom implementation. + fn simplify( + &mut self, + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + switch_bb_idx: BasicBlock, + param_env: ParamEnv<'tcx>, + ) -> Option<()> { + let bbs = &body.basic_blocks; + let (discr, targets) = match bbs[switch_bb_idx].terminator().kind { + TerminatorKind::SwitchInt { ref discr, ref targets, .. } => (discr, targets), + _ => unreachable!(), + }; + + let discr_ty = discr.ty(body.local_decls(), tcx); + self.can_simplify(tcx, targets, param_env, bbs, discr_ty)?; + + let mut patch = MirPatch::new(body); + + // Take ownership of items now that we know we can optimize. + let discr = discr.clone(); + + // Introduce a temporary for the discriminant value. + let source_info = bbs[switch_bb_idx].terminator().source_info; + let discr_local = patch.new_temp(discr_ty, source_info.span); + + let (_, first) = targets.iter().next().unwrap(); + let statement_index = bbs[switch_bb_idx].statements.len(); + let parent_end = Location { block: switch_bb_idx, statement_index }; + patch.add_statement(parent_end, StatementKind::StorageLive(discr_local)); + patch.add_assign(parent_end, Place::from(discr_local), Rvalue::Use(discr)); + self.new_stmts(tcx, targets, param_env, &mut patch, parent_end, bbs, discr_local, discr_ty); + patch.add_statement(parent_end, StatementKind::StorageDead(discr_local)); + patch.patch_terminator(switch_bb_idx, bbs[first].terminator().kind.clone()); + patch.apply(body); + Some(()) + } + + /// Check that the BBs to be simplified satisfies all distinct and + /// that the terminator are the same. + /// There are also conditions for different ways of simplification. + fn can_simplify( + &mut self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + bbs: &IndexSlice>, + discr_ty: Ty<'tcx>, + ) -> Option<()>; + + fn new_stmts( + &self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + patch: &mut MirPatch<'tcx>, + parent_end: Location, + bbs: &IndexSlice>, + discr_local: Local, + discr_ty: Ty<'tcx>, + ); +} + +struct SimplifyToIf; + /// If a source block is found that switches between two blocks that are exactly /// the same modulo const bool assignments (e.g., one assigns true another false /// to the same place), merge a target block statements into the source block, @@ -37,144 +154,349 @@ pub struct MatchBranchSimplification; /// goto -> bb3; /// } /// ``` +impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf { + fn can_simplify( + &mut self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + bbs: &IndexSlice>, + _discr_ty: Ty<'tcx>, + ) -> Option<()> { + if targets.iter().len() != 1 { + return None; + } + // We require that the possible target blocks all be distinct. + let (_, first) = targets.iter().next().unwrap(); + let second = targets.otherwise(); + if first == second { + return None; + } + // Check that destinations are identical, and if not, then don't optimize this block + if bbs[first].terminator().kind != bbs[second].terminator().kind { + return None; + } -impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() >= 1 - } + // Check that blocks are assignments of consts to the same place or same statement, + // and match up 1-1, if not don't optimize this block. + let first_stmts = &bbs[first].statements; + let second_stmts = &bbs[second].statements; + if first_stmts.len() != second_stmts.len() { + return None; + } + for (f, s) in iter::zip(first_stmts, second_stmts) { + match (&f.kind, &s.kind) { + // If two statements are exactly the same, we can optimize. + (f_s, s_s) if f_s == s_s => {} - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let def_id = body.source.def_id(); - let param_env = tcx.param_env_reveal_all_normalized(def_id); + // If two statements are const bool assignments to the same place, we can optimize. + ( + StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s + && f_c.const_.ty().is_bool() + && s_c.const_.ty().is_bool() + && f_c.const_.try_eval_bool(tcx, param_env).is_some() + && s_c.const_.try_eval_bool(tcx, param_env).is_some() => {} - let bbs = body.basic_blocks.as_mut(); - let mut should_cleanup = false; - 'outer: for bb_idx in bbs.indices() { - if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {def_id:?} ")) { - continue; + // Otherwise we cannot optimize. Try another block. + _ => return None, } + } + Some(()) + } - let (discr, val, first, second) = match bbs[bb_idx].terminator().kind { - TerminatorKind::SwitchInt { - discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)), - ref targets, - .. - } if targets.iter().len() == 1 => { - let (value, target) = targets.iter().next().unwrap(); - // We require that this block and the two possible target blocks all be - // distinct. - if target == targets.otherwise() - || bb_idx == target - || bb_idx == targets.otherwise() - { - continue; + fn new_stmts( + &self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + patch: &mut MirPatch<'tcx>, + parent_end: Location, + bbs: &IndexSlice>, + discr_local: Local, + discr_ty: Ty<'tcx>, + ) { + let (val, first) = targets.iter().next().unwrap(); + let second = targets.otherwise(); + // We already checked that first and second are different blocks, + // and bb_idx has a different terminator from both of them. + let first = &bbs[first]; + let second = &bbs[second]; + for (f, s) in iter::zip(&first.statements, &second.statements) { + match (&f.kind, &s.kind) { + (f_s, s_s) if f_s == s_s => { + patch.add_statement(parent_end, f.kind.clone()); + } + + ( + StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), + ) => { + // From earlier loop we know that we are dealing with bool constants only: + let f_b = f_c.const_.try_eval_bool(tcx, param_env).unwrap(); + let s_b = s_c.const_.try_eval_bool(tcx, param_env).unwrap(); + if f_b == s_b { + // Same value in both blocks. Use statement as is. + patch.add_statement(parent_end, f.kind.clone()); + } else { + // Different value between blocks. Make value conditional on switch condition. + let size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size; + let const_cmp = Operand::const_from_scalar( + tcx, + discr_ty, + rustc_const_eval::interpret::Scalar::from_uint(val, size), + rustc_span::DUMMY_SP, + ); + let op = if f_b { BinOp::Eq } else { BinOp::Ne }; + let rhs = Rvalue::BinaryOp( + op, + Box::new((Operand::Copy(Place::from(discr_local)), const_cmp)), + ); + patch.add_assign(parent_end, *lhs, rhs); } - (discr, value, target, targets.otherwise()) } - // Only optimize switch int statements - _ => continue, - }; - // Check that destinations are identical, and if not, then don't optimize this block - if bbs[first].terminator().kind != bbs[second].terminator().kind { - continue; + _ => unreachable!(), } + } + } +} - // Check that blocks are assignments of consts to the same place or same statement, - // and match up 1-1, if not don't optimize this block. - let first_stmts = &bbs[first].statements; - let scnd_stmts = &bbs[second].statements; - if first_stmts.len() != scnd_stmts.len() { - continue; - } - for (f, s) in iter::zip(first_stmts, scnd_stmts) { - match (&f.kind, &s.kind) { - // If two statements are exactly the same, we can optimize. - (f_s, s_s) if f_s == s_s => {} +#[derive(Default)] +struct SimplifyToExp { + transfrom_types: Vec, +} - // If two statements are const bool assignments to the same place, we can optimize. - ( - StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), - StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), - ) if lhs_f == lhs_s - && f_c.const_.ty().is_bool() - && s_c.const_.ty().is_bool() - && f_c.const_.try_eval_bool(tcx, param_env).is_some() - && s_c.const_.try_eval_bool(tcx, param_env).is_some() => {} +#[derive(Clone, Copy)] +enum CompareType<'tcx, 'a> { + /// Identical statements. + Same(&'a StatementKind<'tcx>), + /// Assignment statements have the same value. + Eq(&'a Place<'tcx>, Ty<'tcx>, ScalarInt), + /// Enum variant comparison type. + Discr { place: &'a Place<'tcx>, ty: Ty<'tcx>, is_signed: bool }, +} - // Otherwise we cannot optimize. Try another block. - _ => continue 'outer, - } - } - // Take ownership of items now that we know we can optimize. - let discr = discr.clone(); - let discr_ty = discr.ty(&body.local_decls, tcx); +enum TransfromType { + Same, + Eq, + Discr, +} + +impl From> for TransfromType { + fn from(compare_type: CompareType<'_, '_>) -> Self { + match compare_type { + CompareType::Same(_) => TransfromType::Same, + CompareType::Eq(_, _, _) => TransfromType::Eq, + CompareType::Discr { .. } => TransfromType::Discr, + } + } +} + +/// If we find that the value of match is the same as the assignment, +/// merge a target block statements into the source block, +/// using cast to transform different integer types. +/// +/// For example: +/// +/// ```ignore (MIR) +/// bb0: { +/// switchInt(_1) -> [1: bb2, 2: bb3, 3: bb4, otherwise: bb1]; +/// } +/// +/// bb1: { +/// unreachable; +/// } +/// +/// bb2: { +/// _0 = const 1_i16; +/// goto -> bb5; +/// } +/// +/// bb3: { +/// _0 = const 2_i16; +/// goto -> bb5; +/// } +/// +/// bb4: { +/// _0 = const 3_i16; +/// goto -> bb5; +/// } +/// ``` +/// +/// into: +/// +/// ```ignore (MIR) +/// bb0: { +/// _0 = _3 as i16 (IntToInt); +/// goto -> bb5; +/// } +/// ``` +impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { + fn can_simplify( + &mut self, + tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + param_env: ParamEnv<'tcx>, + bbs: &IndexSlice>, + discr_ty: Ty<'tcx>, + ) -> Option<()> { + if targets.iter().len() < 2 || targets.iter().len() > 64 { + return None; + } + // We require that the possible target blocks all be distinct. + if !targets.is_distinct() { + return None; + } + if !bbs[targets.otherwise()].is_empty_unreachable() { + return None; + } + let mut target_iter = targets.iter(); + let (first_val, first_target) = target_iter.next().unwrap(); + let first_terminator_kind = &bbs[first_target].terminator().kind; + // Check that destinations are identical, and if not, then don't optimize this block + if !targets + .iter() + .all(|(_, other_target)| first_terminator_kind == &bbs[other_target].terminator().kind) + { + return None; + } - // Introduce a temporary for the discriminant value. - let source_info = bbs[bb_idx].terminator().source_info; - let discr_local = body.local_decls.push(LocalDecl::new(discr_ty, source_info.span)); + let discr_size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size; + let first_stmts = &bbs[first_target].statements; + let (second_val, second_target) = target_iter.next().unwrap(); + let second_stmts = &bbs[second_target].statements; + if first_stmts.len() != second_stmts.len() { + return None; + } - // We already checked that first and second are different blocks, - // and bb_idx has a different terminator from both of them. - let (from, first, second) = bbs.pick3_mut(bb_idx, first, second); + fn int_equal(l: ScalarInt, r: impl Into, size: Size) -> bool { + l.assert_int(l.size()) == ScalarInt::try_from_uint(r, size).unwrap().assert_int(size) + } - let new_stmts = iter::zip(&first.statements, &second.statements).map(|(f, s)| { - match (&f.kind, &s.kind) { - (f_s, s_s) if f_s == s_s => (*f).clone(), + // We first compare the two branches, and then the other branches need to fulfill the same conditions. + let mut compare_types = Vec::new(); + for (f, s) in iter::zip(first_stmts, second_stmts) { + let compare_type = match (&f.kind, &s.kind) { + // If two statements are exactly the same, we can optimize. + (f_s, s_s) if f_s == s_s => CompareType::Same(f_s), - ( - StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), - StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(s_c)))), - ) => { - // From earlier loop we know that we are dealing with bool constants only: - let f_b = f_c.const_.try_eval_bool(tcx, param_env).unwrap(); - let s_b = s_c.const_.try_eval_bool(tcx, param_env).unwrap(); - if f_b == s_b { - // Same value in both blocks. Use statement as is. - (*f).clone() - } else { - // Different value between blocks. Make value conditional on switch condition. - let size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size; - let const_cmp = Operand::const_from_scalar( - tcx, - discr_ty, - rustc_const_eval::interpret::Scalar::from_uint(val, size), - rustc_span::DUMMY_SP, - ); - let op = if f_b { BinOp::Eq } else { BinOp::Ne }; - let rhs = Rvalue::BinaryOp( - op, - Box::new((Operand::Copy(Place::from(discr_local)), const_cmp)), - ); - Statement { - source_info: f.source_info, - kind: StatementKind::Assign(Box::new((*lhs, rhs))), + // If two statements are assignments with the match values to the same place, we can optimize. + ( + StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))), + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s + && f_c.const_.ty() == s_c.const_.ty() + && f_c.const_.ty().is_integral() => + { + match ( + f_c.const_.try_eval_scalar_int(tcx, param_env), + s_c.const_.try_eval_scalar_int(tcx, param_env), + ) { + (Some(f), Some(s)) if f == s => CompareType::Eq(lhs_f, f_c.const_.ty(), f), + // Enum variants can also be simplified to an assignment statement if their values are equal. + // We need to consider both unsigned and signed scenarios here. + (Some(f), Some(s)) + if ((f_c.const_.ty().is_signed() || discr_ty.is_signed()) + && int_equal(f, first_val, discr_size) + && int_equal(s, second_val, discr_size)) + || (Some(f) == ScalarInt::try_from_uint(first_val, f.size()) + && Some(s) + == ScalarInt::try_from_uint(second_val, s.size())) => + { + CompareType::Discr { + place: lhs_f, + ty: f_c.const_.ty(), + is_signed: f_c.const_.ty().is_signed() || discr_ty.is_signed(), } } + _ => { + return None; + } } + } + + // Otherwise we cannot optimize. Try another block. + _ => return None, + }; + compare_types.push(compare_type); + } - _ => unreachable!(), + // All remaining BBs need to fulfill the same pattern as the two BBs from the previous step. + for (other_val, other_target) in target_iter { + let other_stmts = &bbs[other_target].statements; + if compare_types.len() != other_stmts.len() { + return None; + } + for (f, s) in iter::zip(&compare_types, other_stmts) { + match (*f, &s.kind) { + (CompareType::Same(f_s), s_s) if f_s == s_s => {} + ( + CompareType::Eq(lhs_f, f_ty, val), + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s + && s_c.const_.ty() == f_ty + && s_c.const_.try_eval_scalar_int(tcx, param_env) == Some(val) => {} + ( + CompareType::Discr { place: lhs_f, ty: f_ty, is_signed }, + StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))), + ) if lhs_f == lhs_s && s_c.const_.ty() == f_ty => { + let Some(f) = s_c.const_.try_eval_scalar_int(tcx, param_env) else { + return None; + }; + if is_signed + && s_c.const_.ty().is_signed() + && int_equal(f, other_val, discr_size) + { + continue; + } + if Some(f) == ScalarInt::try_from_uint(other_val, f.size()) { + continue; + } + return None; + } + _ => return None, } - }); - - from.statements - .push(Statement { source_info, kind: StatementKind::StorageLive(discr_local) }); - from.statements.push(Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - Place::from(discr_local), - Rvalue::Use(discr), - ))), - }); - from.statements.extend(new_stmts); - from.statements - .push(Statement { source_info, kind: StatementKind::StorageDead(discr_local) }); - from.terminator_mut().kind = first.terminator().kind.clone(); - should_cleanup = true; + } } + self.transfrom_types = compare_types.into_iter().map(|c| c.into()).collect(); + Some(()) + } - if should_cleanup { - simplify_cfg(body); + fn new_stmts( + &self, + _tcx: TyCtxt<'tcx>, + targets: &SwitchTargets, + _param_env: ParamEnv<'tcx>, + patch: &mut MirPatch<'tcx>, + parent_end: Location, + bbs: &IndexSlice>, + discr_local: Local, + discr_ty: Ty<'tcx>, + ) { + let (_, first) = targets.iter().next().unwrap(); + let first = &bbs[first]; + + for (t, s) in iter::zip(&self.transfrom_types, &first.statements) { + match (t, &s.kind) { + (TransfromType::Same, _) | (TransfromType::Eq, _) => { + patch.add_statement(parent_end, s.kind.clone()); + } + ( + TransfromType::Discr, + StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))), + ) => { + let operand = Operand::Copy(Place::from(discr_local)); + let r_val = if f_c.const_.ty() == discr_ty { + Rvalue::Use(operand) + } else { + Rvalue::Cast(CastKind::IntToInt, operand, f_c.const_.ty()) + }; + patch.add_assign(parent_end, *lhs, r_val); + } + _ => unreachable!(), + } } } } diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs new file mode 100644 index 0000000000000..57b6126dece93 --- /dev/null +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -0,0 +1,117 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{self, Location, MentionedItem, MirPass}; +use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt}; +use rustc_session::Session; +use rustc_span::source_map::Spanned; + +pub struct MentionedItems; + +struct MentionedItemsVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + mentioned_items: &'a mut Vec>>, +} + +impl<'tcx> MirPass<'tcx> for MentionedItems { + fn is_enabled(&self, _sess: &Session) -> bool { + // If this pass is skipped the collector assume that nothing got mentioned! We could + // potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses + // of anything, but that still seems fragile. Furthermore, even debug builds use level 1, so + // special-casing level 0 is just not worth it. + true + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { + debug_assert!(body.mentioned_items.is_empty()); + let mut mentioned_items = Vec::new(); + MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body); + body.mentioned_items = mentioned_items; + } +} + +// This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are +// visiting the exact same places but then instead of monomorphizing and creating `MonoItems`, we +// have to remain generic and just recording the relevant information in `mentioned_items`, where it +// will then be monomorphized later during "mentioned items" collection. +impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + self.super_terminator(terminator, location); + let span = || self.body.source_info(location).span; + match &terminator.kind { + mir::TerminatorKind::Call { func, .. } => { + let callee_ty = func.ty(self.body, self.tcx); + self.mentioned_items + .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() }); + } + mir::TerminatorKind::Drop { place, .. } => { + let ty = place.ty(self.body, self.tcx).ty; + self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span: span() }); + } + mir::TerminatorKind::InlineAsm { ref operands, .. } => { + for op in operands { + match *op { + mir::InlineAsmOperand::SymFn { ref value } => { + self.mentioned_items.push(Spanned { + node: MentionedItem::Fn(value.const_.ty()), + span: span(), + }); + } + _ => {} + } + } + } + _ => {} + } + } + + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + let span = || self.body.source_info(location).span; + match *rvalue { + // We need to detect unsizing casts that required vtables. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::Unsize), + ref operand, + target_ty, + ) + | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { + // This isn't monomorphized yet so we can't tell what the actual types are -- just + // add everything that may involve a vtable. + let source_ty = operand.ty(self.body, self.tcx); + let may_involve_vtable = match ( + source_ty.builtin_deref(true).map(|t| t.ty.kind()), + target_ty.builtin_deref(true).map(|t| t.ty.kind()), + ) { + (Some(ty::Array(..)), Some(ty::Str | ty::Slice(..))) => false, // &str/&[T] unsizing + _ => true, + }; + if may_involve_vtable { + self.mentioned_items.push(Spanned { + node: MentionedItem::UnsizeCast { source_ty, target_ty }, + span: span(), + }); + } + } + // Similarly, record closures that are turned into function pointers. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), + ref operand, + _, + ) => { + let source_ty = operand.ty(self.body, self.tcx); + self.mentioned_items + .push(Spanned { node: MentionedItem::Closure(source_ty), span: span() }); + } + // And finally, function pointer reification casts. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), + ref operand, + _, + ) => { + let fn_ty = operand.ty(self.body, self.tcx); + self.mentioned_items.push(Spanned { node: MentionedItem::Fn(fn_ty), span: span() }); + } + _ => {} + } + } +} diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index c3a92911bbf0f..232c290e0fb21 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -84,7 +84,7 @@ impl<'tcx> MirPass<'tcx> for RenameReturnPlace { /// /// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned /// to the return place along all possible paths through the control-flow graph. -fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option { +fn local_eligible_for_nrvo(body: &mir::Body<'_>) -> Option { if IsReturnPlaceRead::run(body) { return None; } @@ -118,10 +118,7 @@ fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option { copied_to_return_place } -fn find_local_assigned_to_return_place( - start: BasicBlock, - body: &mut mir::Body<'_>, -) -> Option { +fn find_local_assigned_to_return_place(start: BasicBlock, body: &mir::Body<'_>) -> Option { let mut block = start; let mut seen = BitSet::new_empty(body.basic_blocks.len()); diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 77478cc741d39..17a1c3c715797 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -186,9 +186,6 @@ fn run_passes_inner<'tcx>( if let Some(by_move_body) = coroutine.by_move_body.as_mut() { run_passes_inner(tcx, by_move_body, passes, phase_change, validate_each); } - if let Some(by_mut_body) = coroutine.by_mut_body.as_mut() { - run_passes_inner(tcx, by_mut_body, passes, phase_change, validate_each); - } } } diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9fe8c34a8bf5d..689a547689a2d 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -13,6 +13,7 @@ //! move analysis runs after promotion on broken MIR. use either::{Left, Right}; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -175,6 +176,12 @@ fn collect_temps_and_candidates<'tcx>( struct Validator<'a, 'tcx> { ccx: &'a ConstCx<'a, 'tcx>, temps: &'a mut IndexSlice, + /// For backwards compatibility, we are promoting function calls in `const`/`static` + /// initializers. But we want to avoid evaluating code that might panic and that otherwise would + /// not have been evaluated, so we only promote such calls in basic blocks that are guaranteed + /// to execute. In other words, we only promote such calls in basic blocks that are definitely + /// not dead code. Here we cache the result of computing that set of basic blocks. + promotion_safe_blocks: Option>, } impl<'a, 'tcx> std::ops::Deref for Validator<'a, 'tcx> { @@ -260,7 +267,9 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_rvalue(rhs) } Right(terminator) => match &terminator.kind { - TerminatorKind::Call { func, args, .. } => self.validate_call(func, args), + TerminatorKind::Call { func, args, .. } => { + self.validate_call(func, args, loc.block) + } TerminatorKind::Yield { .. } => Err(Unpromotable), kind => { span_bug!(terminator.source_info.span, "{:?} not promotable", kind); @@ -384,7 +393,7 @@ impl<'tcx> Validator<'_, 'tcx> { match kind { // Reject these borrow types just to be safe. // FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase. - BorrowKind::Fake | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => { + BorrowKind::Fake(_) | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => { return Err(Unpromotable); } @@ -434,7 +443,7 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), // ptr-to-int casts are not possible in consts and thus not promotable - Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => return Err(Unpromotable), + Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => return Err(Unpromotable), // all other casts including int-to-ptr casts are fine, they just use the integer value // at pointer type. @@ -446,7 +455,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::SizeOf => {} NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} - NullOp::UbCheck(_) => {} + NullOp::UbChecks => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), @@ -464,7 +473,7 @@ impl<'tcx> Validator<'_, 'tcx> { let op = *op; let lhs_ty = lhs.ty(self.body, self.tcx); - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs_ty.kind() { + if let ty::RawPtr(_, _) | ty::FnPtr(..) = lhs_ty.kind() { // Raw and fn pointer operations are not allowed inside consts and thus not promotable. assert!(matches!( op, @@ -490,14 +499,14 @@ impl<'tcx> Validator<'_, 'tcx> { } _ => None, }; - match rhs_val.map(|x| x.try_to_uint(sz).unwrap()) { + match rhs_val.map(|x| x.assert_uint(sz)) { // for the zero test, int vs uint does not matter Some(x) if x != 0 => {} // okay _ => return Err(Unpromotable), // value not known or 0 -- not okay } // Furthermore, for signed divison, we also have to exclude `int::MIN / -1`. if lhs_ty.is_signed() { - match rhs_val.map(|x| x.try_to_int(sz).unwrap()) { + match rhs_val.map(|x| x.assert_int(sz)) { Some(-1) | None => { // The RHS is -1 or unknown, so we have to be careful. // But is the LHS int::MIN? @@ -508,7 +517,7 @@ impl<'tcx> Validator<'_, 'tcx> { _ => None, }; let lhs_min = sz.signed_int_min(); - match lhs_val.map(|x| x.try_to_int(sz).unwrap()) { + match lhs_val.map(|x| x.assert_int(sz)) { Some(x) if x != lhs_min => {} // okay _ => return Err(Unpromotable), // value not known or int::MIN -- not okay } @@ -525,6 +534,7 @@ impl<'tcx> Validator<'_, 'tcx> { | BinOp::Lt | BinOp::Ge | BinOp::Gt + | BinOp::Cmp | BinOp::Offset | BinOp::Add | BinOp::AddUnchecked @@ -587,29 +597,79 @@ impl<'tcx> Validator<'_, 'tcx> { Ok(()) } + /// Computes the sets of blocks of this MIR that are definitely going to be executed + /// if the function returns successfully. That makes it safe to promote calls in them + /// that might fail. + fn promotion_safe_blocks(body: &mir::Body<'tcx>) -> FxHashSet { + let mut safe_blocks = FxHashSet::default(); + let mut safe_block = START_BLOCK; + loop { + safe_blocks.insert(safe_block); + // Let's see if we can find another safe block. + safe_block = match body.basic_blocks[safe_block].terminator().kind { + TerminatorKind::Goto { target } => target, + TerminatorKind::Call { target: Some(target), .. } + | TerminatorKind::Drop { target, .. } => { + // This calls a function or the destructor. `target` does not get executed if + // the callee loops or panics. But in both cases the const already fails to + // evaluate, so we are fine considering `target` a safe block for promotion. + target + } + TerminatorKind::Assert { target, .. } => { + // Similar to above, we only consider successful execution. + target + } + _ => { + // No next safe block. + break; + } + }; + } + safe_blocks + } + + /// Returns whether the block is "safe" for promotion, which means it cannot be dead code. + /// We use this to avoid promoting operations that can fail in dead code. + fn is_promotion_safe_block(&mut self, block: BasicBlock) -> bool { + let body = self.body; + let safe_blocks = + self.promotion_safe_blocks.get_or_insert_with(|| Self::promotion_safe_blocks(body)); + safe_blocks.contains(&block) + } + fn validate_call( &mut self, callee: &Operand<'tcx>, args: &[Spanned>], + block: BasicBlock, ) -> Result<(), Unpromotable> { + // Validate the operands. If they fail, there's no question -- we cannot promote. + self.validate_operand(callee)?; + for arg in args { + self.validate_operand(&arg.node)?; + } + + // Functions marked `#[rustc_promotable]` are explicitly allowed to be promoted, so we can + // accept them at this point. let fn_ty = callee.ty(self.body, self.tcx); + if let ty::FnDef(def_id, _) = *fn_ty.kind() { + if self.tcx.is_promotable_const_fn(def_id) { + return Ok(()); + } + } - // Inside const/static items, we promote all (eligible) function calls. - // Everywhere else, we require `#[rustc_promotable]` on the callee. - let promote_all_const_fn = matches!( + // Ideally, we'd stop here and reject the rest. + // But for backward compatibility, we have to accept some promotion in const/static + // initializers. Inline consts are explicitly excluded, they are more recent so we have no + // backwards compatibility reason to allow more promotion inside of them. + let promote_all_fn = matches!( self.const_kind, Some(hir::ConstContext::Static(_) | hir::ConstContext::Const { inline: false }) ); - if !promote_all_const_fn { - if let ty::FnDef(def_id, _) = *fn_ty.kind() { - // Never promote runtime `const fn` calls of - // functions without `#[rustc_promotable]`. - if !self.tcx.is_promotable_const_fn(def_id) { - return Err(Unpromotable); - } - } + if !promote_all_fn { + return Err(Unpromotable); } - + // Make sure the callee is a `const fn`. let is_const_fn = match *fn_ty.kind() { ty::FnDef(def_id, _) => self.tcx.is_const_fn_raw(def_id), _ => false, @@ -617,23 +677,23 @@ impl<'tcx> Validator<'_, 'tcx> { if !is_const_fn { return Err(Unpromotable); } - - self.validate_operand(callee)?; - for arg in args { - self.validate_operand(&arg.node)?; + // The problem is, this may promote calls to functions that panic. + // We don't want to introduce compilation errors if there's a panic in a call in dead code. + // So we ensure that this is not dead code. + if !self.is_promotion_safe_block(block) { + return Err(Unpromotable); } - + // This passed all checks, so let's accept. Ok(()) } } -// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`. fn validate_candidates( ccx: &ConstCx<'_, '_>, temps: &mut IndexSlice, candidates: &[Candidate], ) -> Vec { - let mut validator = Validator { ccx, temps }; + let mut validator = Validator { ccx, temps, promotion_safe_blocks: None }; candidates .iter() @@ -652,6 +712,10 @@ struct Promoter<'a, 'tcx> { /// If true, all nested temps are also kept in the /// source MIR, not moved to the promoted MIR. keep_original: bool, + + /// If true, add the new const (the promoted) to the required_consts of the parent MIR. + /// This is initially false and then set by the visitor when it encounters a `Call` terminator. + add_to_required: bool, } impl<'a, 'tcx> Promoter<'a, 'tcx> { @@ -754,6 +818,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { TerminatorKind::Call { mut func, mut args, call_source: desugar, fn_span, .. } => { + // This promoted involves a function call, so it may fail to evaluate. + // Let's make sure it is added to `required_consts` so that that failure cannot get lost. + self.add_to_required = true; + self.visit_operand(&mut func, loc); for arg in &mut args { self.visit_operand(&mut arg.node, loc); @@ -788,7 +856,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { fn promote_candidate(mut self, candidate: Candidate, next_promoted_id: usize) -> Body<'tcx> { let def = self.source.source.def_id(); - let mut rvalue = { + let (mut rvalue, promoted_op) = { let promoted = &mut self.promoted; let promoted_id = Promoted::new(next_promoted_id); let tcx = self.tcx; @@ -798,11 +866,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let args = tcx.erase_regions(GenericArgs::identity_for_item(tcx, def)); let uneval = mir::UnevaluatedConst { def, args, promoted: Some(promoted_id) }; - Operand::Constant(Box::new(ConstOperand { - span, - user_ty: None, - const_: Const::Unevaluated(uneval, ty), - })) + ConstOperand { span, user_ty: None, const_: Const::Unevaluated(uneval, ty) } }; let blocks = self.source.basic_blocks.as_mut(); @@ -820,11 +884,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let ty = local_decls[place.local].ty; let span = statement.source_info.span; - let ref_ty = Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() }, - ); + let ref_ty = + Ty::new_ref(tcx, tcx.lifetimes.re_erased, ty, borrow_kind.to_mutbl_lossy()); let mut projection = vec![PlaceElem::Deref]; projection.extend(place.projection); @@ -838,22 +899,26 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let promoted_ref = local_decls.push(promoted_ref); assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref); + let promoted_operand = promoted_operand(ref_ty, span); let promoted_ref_statement = Statement { source_info: statement.source_info, kind: StatementKind::Assign(Box::new(( Place::from(promoted_ref), - Rvalue::Use(promoted_operand(ref_ty, span)), + Rvalue::Use(Operand::Constant(Box::new(promoted_operand))), ))), }; self.extra_statements.push((loc, promoted_ref_statement)); - Rvalue::Ref( - tcx.lifetimes.re_erased, - *borrow_kind, - Place { - local: mem::replace(&mut place.local, promoted_ref), - projection: List::empty(), - }, + ( + Rvalue::Ref( + tcx.lifetimes.re_erased, + *borrow_kind, + Place { + local: mem::replace(&mut place.local, promoted_ref), + projection: List::empty(), + }, + ), + promoted_operand, ) }; @@ -865,6 +930,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let span = self.promoted.span; self.assign(RETURN_PLACE, rvalue, span); + + // Now that we did promotion, we know whether we'll want to add this to `required_consts`. + if self.add_to_required { + self.source.required_consts.push(promoted_op); + } + self.promoted } } @@ -880,6 +951,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> { *local = self.promote_temp(*local); } } + + fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) { + if constant.const_.is_required_const() { + self.promoted.required_consts.push(*constant); + } + + // Skipping `super_constant` as the visitor is otherwise only looking for locals. + } } fn promote_candidates<'tcx>( @@ -933,8 +1012,10 @@ fn promote_candidates<'tcx>( temps: &mut temps, extra_statements: &mut extra_statements, keep_original: false, + add_to_required: false, }; + // `required_consts` of the promoted itself gets filled while building the MIR body. let mut promoted = promoter.promote_candidate(candidate, promotions.len()); promoted.source.promoted = Some(promotions.next_index()); promotions.push(promoted); diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index abde6a47e83aa..71ac929d35ebd 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -1,6 +1,5 @@ use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{Const, ConstOperand, Location}; -use rustc_middle::ty::ConstKind; +use rustc_middle::mir::{ConstOperand, Location}; pub struct RequiredConstsVisitor<'a, 'tcx> { required_consts: &'a mut Vec>, @@ -14,14 +13,8 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) { - let const_ = constant.const_; - match const_ { - Const::Ty(c) => match c.kind() { - ConstKind::Param(_) | ConstKind::Error(_) | ConstKind::Value(_) => {} - _ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c), - }, - Const::Unevaluated(..) => self.required_consts.push(*constant), - Const::Val(..) => {} + if constant.const_.is_required_const() { + self.required_consts.push(*constant); } } } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 733e2f93b2535..1c85a604053c7 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -3,8 +3,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_middle::mir::*; use rustc_middle::query::Providers; +use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, CoroutineArgs, EarlyBinder, Ty, TyCtxt}; -use rustc_middle::ty::{GenericArgs, CAPTURE_STRUCT_LOCAL}; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_index::{Idx, IndexVec}; @@ -17,11 +17,13 @@ use std::iter; use crate::{ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, - pass_manager as pm, remove_noop_landing_pads, simplify, + mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, }; use rustc_middle::mir::patch::MirPatch; use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle}; +mod async_destructor_ctor; + pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } @@ -55,7 +57,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' // a virtual call, or a direct call to a function for which // indirect calls must be codegen'd differently than direct ones // (such as `#[track_caller]`). - ty::InstanceDef::ReifyShim(def_id) => { + ty::InstanceDef::ReifyShim(def_id, _) => { build_call_shim(tcx, instance, None, CallKind::Direct(def_id)) } ty::InstanceDef::ClosureOnceShim { call_once: _, track_caller: _ } => { @@ -72,37 +74,12 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::ConstructCoroutineInClosureShim { coroutine_closure_def_id, - target_kind, - } => match target_kind { - ty::ClosureKind::Fn => unreachable!("shouldn't be building shim for Fn"), - ty::ClosureKind::FnMut => { - // No need to optimize the body, it has already been optimized - // since we steal it from the `AsyncFn::call` body and just fix - // the return type. - return build_construct_coroutine_by_mut_shim(tcx, coroutine_closure_def_id); - } - ty::ClosureKind::FnOnce => { - build_construct_coroutine_by_move_shim(tcx, coroutine_closure_def_id) - } - }, + receiver_by_ref, + } => build_construct_coroutine_by_move_shim(tcx, coroutine_closure_def_id, receiver_by_ref), - ty::InstanceDef::CoroutineKindShim { coroutine_def_id, target_kind } => match target_kind { - ty::ClosureKind::Fn => unreachable!(), - ty::ClosureKind::FnMut => { - return tcx - .optimized_mir(coroutine_def_id) - .coroutine_by_mut_body() - .unwrap() - .clone(); - } - ty::ClosureKind::FnOnce => { - return tcx - .optimized_mir(coroutine_def_id) - .coroutine_by_move_body() - .unwrap() - .clone(); - } - }, + ty::InstanceDef::CoroutineKindShim { coroutine_def_id } => { + return tcx.optimized_mir(coroutine_def_id).coroutine_by_move_body().unwrap().clone(); + } ty::InstanceDef::DropGlue(def_id, ty) => { // FIXME(#91576): Drop shims for coroutines aren't subject to the MIR passes at the end @@ -123,21 +100,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' let body = if id_args.as_coroutine().kind_ty() == args.as_coroutine().kind_ty() { coroutine_body.coroutine_drop().unwrap() } else { - match args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap() { - ty::ClosureKind::Fn => { - unreachable!() - } - ty::ClosureKind::FnMut => coroutine_body - .coroutine_by_mut_body() - .unwrap() - .coroutine_drop() - .unwrap(), - ty::ClosureKind::FnOnce => coroutine_body - .coroutine_by_move_body() - .unwrap() - .coroutine_drop() - .unwrap(), - } + assert_eq!( + args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap(), + ty::ClosureKind::FnOnce + ); + coroutine_body.coroutine_by_move_body().unwrap().coroutine_drop().unwrap() }; let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args); @@ -147,6 +114,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' tcx, &mut body, &[ + &mentioned_items::MentionedItems, &abort_unwinding_calls::AbortUnwindingCalls, &add_call_guards::CriticalCallEdges, ], @@ -161,6 +129,9 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance), ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), + ty::InstanceDef::AsyncDropGlueCtorShim(def_id, ty) => { + async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty) + } ty::InstanceDef::Virtual(..) => { bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance) } @@ -178,6 +149,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' tcx, &mut result, &[ + &mentioned_items::MentionedItems, &add_moves_for_packed_drops::AddMovesForPackedDrops, &deref_separator::Derefer, &remove_noop_landing_pads::RemoveNoopLandingPads, @@ -572,14 +544,8 @@ impl<'tcx> CloneShimBuilder<'tcx> { const_: Const::zero_sized(func_ty), })); - let ref_loc = self.make_place( - Mutability::Not, - Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, - ), - ); + let ref_loc = + self.make_place(Mutability::Not, Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty)); // `let ref_loc: &ty = &src;` let statement = self.make_statement(StatementKind::Assign(Box::new(( @@ -804,11 +770,7 @@ fn build_call_shim<'tcx>( // let rcvr = &mut rcvr; let ref_rcvr = local_decls.push( LocalDecl::new( - Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty: sig.inputs()[0], mutbl: hir::Mutability::Mut }, - ), + Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, sig.inputs()[0]), span, ) .immutable(), @@ -1028,7 +990,7 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t let locals = local_decls_for_sig(&sig, span); let source_info = SourceInfo::outermost(span); - // FIXME: use `expose_addr` once we figure out whether function pointers have meaningful provenance. + // FIXME: use `expose_provenance` once we figure out whether function pointers have meaningful provenance. let rvalue = Rvalue::Cast( CastKind::FnPtrToPtr, Operand::Move(Place::from(Local::new(1))), @@ -1051,12 +1013,26 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t fn build_construct_coroutine_by_move_shim<'tcx>( tcx: TyCtxt<'tcx>, coroutine_closure_def_id: DefId, + receiver_by_ref: bool, ) -> Body<'tcx> { - let self_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity(); + let mut self_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity(); let ty::CoroutineClosure(_, args) = *self_ty.kind() else { bug!(); }; + // We use `&mut Self` here because we only need to emit an ABI-compatible shim body, + // rather than match the signature exactly (which might take `&self` instead). + // + // The self type here is a coroutine-closure, not a coroutine, and we never read from + // it because it never has any captures, because this is only true in the Fn/FnMut + // implementation, not the AsyncFn/AsyncFnMut implementation, which is implemented only + // if the coroutine-closure has no captures. + if receiver_by_ref { + // Triple-check that there's no captures here. + assert_eq!(args.as_coroutine_closure().tupled_upvars_ty(), tcx.types.unit); + self_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, self_ty); + } + let poly_sig = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { tcx.mk_fn_sig( [self_ty].into_iter().chain(sig.tupled_inputs_ty.tuple_fields()), @@ -1112,49 +1088,19 @@ fn build_construct_coroutine_by_move_shim<'tcx>( let source = MirSource::from_instance(ty::InstanceDef::ConstructCoroutineInClosureShim { coroutine_closure_def_id, - target_kind: ty::ClosureKind::FnOnce, + receiver_by_ref, }); let body = new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span); - dump_mir(tcx, false, "coroutine_closure_by_move", &0, &body, |_, _| Ok(())); - - body -} - -fn build_construct_coroutine_by_mut_shim<'tcx>( - tcx: TyCtxt<'tcx>, - coroutine_closure_def_id: DefId, -) -> Body<'tcx> { - let mut body = tcx.optimized_mir(coroutine_closure_def_id).clone(); - let coroutine_closure_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity(); - let ty::CoroutineClosure(_, args) = *coroutine_closure_ty.kind() else { - bug!(); - }; - let args = args.as_coroutine_closure(); - - body.local_decls[RETURN_PLACE].ty = - tcx.instantiate_bound_regions_with_erased(args.coroutine_closure_sig().map_bound(|sig| { - sig.to_coroutine_given_kind_and_upvars( - tcx, - args.parent_args(), - tcx.coroutine_for_closure(coroutine_closure_def_id), - ty::ClosureKind::FnMut, - tcx.lifetimes.re_erased, - args.tupled_upvars_ty(), - args.coroutine_captures_by_ref_ty(), - ) - })); - body.local_decls[CAPTURE_STRUCT_LOCAL].ty = - Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_closure_ty); - - body.source = MirSource::from_instance(ty::InstanceDef::ConstructCoroutineInClosureShim { - coroutine_closure_def_id, - target_kind: ty::ClosureKind::FnMut, - }); - - body.pass_count = 0; - dump_mir(tcx, false, "coroutine_closure_by_mut", &0, &body, |_, _| Ok(())); + dump_mir( + tcx, + false, + if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" }, + &0, + &body, + |_, _| Ok(()), + ); body } diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs new file mode 100644 index 0000000000000..80eadb9abdcf4 --- /dev/null +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -0,0 +1,618 @@ +use std::iter; + +use itertools::Itertools; +use rustc_ast::Mutability; +use rustc_const_eval::interpret; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_index::{Idx, IndexVec}; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Body, CallSource, CastKind, Const, ConstOperand, ConstValue, Local, + LocalDecl, MirSource, Operand, Place, PlaceElem, Rvalue, SourceInfo, Statement, StatementKind, + Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, RETURN_PLACE, +}; +use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::util::Discr; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::source_map::respan; +use rustc_span::{Span, Symbol}; +use rustc_target::abi::{FieldIdx, VariantIdx}; +use rustc_target::spec::PanicStrategy; + +use super::{local_decls_for_sig, new_body}; + +pub fn build_async_destructor_ctor_shim<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + ty: Option>, +) -> Body<'tcx> { + debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); + + AsyncDestructorCtorShimBuilder::new(tcx, def_id, ty).build() +} + +/// Builder for async_drop_in_place shim. Functions as a stack machine +/// to build up an expression using combinators. Stack contains pairs +/// of locals and types. Combinator is a not yet instantiated pair of a +/// function and a type, is considered to be an operator which consumes +/// operands from the stack by instantiating its function and its type +/// with operand types and moving locals into the function call. Top +/// pair is considered to be the last operand. +// FIXME: add mir-opt tests +struct AsyncDestructorCtorShimBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + def_id: DefId, + self_ty: Option>, + span: Span, + source_info: SourceInfo, + param_env: ty::ParamEnv<'tcx>, + + stack: Vec>, + last_bb: BasicBlock, + top_cleanup_bb: Option, + + locals: IndexVec>, + bbs: IndexVec>, +} + +#[derive(Clone, Copy)] +enum SurfaceDropKind { + Async, + Sync, +} + +impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { + const SELF_PTR: Local = Local::from_u32(1); + const INPUT_COUNT: usize = 1; + const MAX_STACK_LEN: usize = 2; + + fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Option>) -> Self { + let args = if let Some(ty) = self_ty { + tcx.mk_args(&[ty.into()]) + } else { + ty::GenericArgs::identity_for_item(tcx, def_id) + }; + let sig = tcx.fn_sig(def_id).instantiate(tcx, args); + let sig = tcx.instantiate_bound_regions_with_erased(sig); + let span = tcx.def_span(def_id); + + let source_info = SourceInfo::outermost(span); + + debug_assert_eq!(sig.inputs().len(), Self::INPUT_COUNT); + let locals = local_decls_for_sig(&sig, span); + + // Usual case: noop() + unwind resume + return + let mut bbs = IndexVec::with_capacity(3); + let param_env = tcx.param_env_reveal_all_normalized(def_id); + AsyncDestructorCtorShimBuilder { + tcx, + def_id, + self_ty, + span, + source_info, + param_env, + + stack: Vec::with_capacity(Self::MAX_STACK_LEN), + last_bb: bbs.push(BasicBlockData::new(None)), + top_cleanup_bb: match tcx.sess.panic_strategy() { + PanicStrategy::Unwind => { + // Don't drop input arg because it's just a pointer + Some(bbs.push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::UnwindResume, + }), + is_cleanup: true, + })) + } + PanicStrategy::Abort => None, + }, + + locals, + bbs, + } + } + + fn build(self) -> Body<'tcx> { + let (tcx, def_id, Some(self_ty)) = (self.tcx, self.def_id, self.self_ty) else { + return self.build_zst_output(); + }; + + let surface_drop_kind = || { + let param_env = tcx.param_env_reveal_all_normalized(def_id); + if self_ty.has_surface_async_drop(tcx, param_env) { + Some(SurfaceDropKind::Async) + } else if self_ty.has_surface_drop(tcx, param_env) { + Some(SurfaceDropKind::Sync) + } else { + None + } + }; + + match self_ty.kind() { + ty::Array(elem_ty, _) => self.build_slice(true, *elem_ty), + ty::Slice(elem_ty) => self.build_slice(false, *elem_ty), + + ty::Tuple(elem_tys) => self.build_chain(None, elem_tys.iter()), + ty::Adt(adt_def, args) if adt_def.is_struct() => { + let field_tys = adt_def.non_enum_variant().fields.iter().map(|f| f.ty(tcx, args)); + self.build_chain(surface_drop_kind(), field_tys) + } + ty::Closure(_, args) => self.build_chain(None, args.as_closure().upvar_tys().iter()), + ty::CoroutineClosure(_, args) => { + self.build_chain(None, args.as_coroutine_closure().upvar_tys().iter()) + } + + ty::Adt(adt_def, args) if adt_def.is_enum() => { + self.build_enum(*adt_def, *args, surface_drop_kind()) + } + + ty::Adt(adt_def, _) => { + assert!(adt_def.is_union()); + match surface_drop_kind().unwrap() { + SurfaceDropKind::Async => self.build_fused_async_surface(), + SurfaceDropKind::Sync => self.build_fused_sync_surface(), + } + } + + ty::Bound(..) + | ty::Foreign(_) + | ty::Placeholder(_) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) | ty::TyVar(_)) + | ty::Param(_) + | ty::Alias(..) => { + bug!("Building async destructor for unexpected type: {self_ty:?}") + } + + _ => { + bug!( + "Building async destructor constructor shim is not yet implemented for type: {self_ty:?}" + ) + } + } + } + + fn build_enum( + mut self, + adt_def: ty::AdtDef<'tcx>, + args: ty::GenericArgsRef<'tcx>, + surface_drop: Option, + ) -> Body<'tcx> { + let tcx = self.tcx; + + let surface = match surface_drop { + None => None, + Some(kind) => { + self.put_self(); + Some(match kind { + SurfaceDropKind::Async => self.combine_async_surface(), + SurfaceDropKind::Sync => self.combine_sync_surface(), + }) + } + }; + + let mut other = None; + for (variant_idx, discr) in adt_def.discriminants(tcx) { + let variant = adt_def.variant(variant_idx); + + let mut chain = None; + for (field_idx, field) in variant.fields.iter_enumerated() { + let field_ty = field.ty(tcx, args); + self.put_variant_field(variant.name, variant_idx, field_idx, field_ty); + let defer = self.combine_defer(field_ty); + chain = Some(match chain { + None => defer, + Some(chain) => self.combine_chain(chain, defer), + }) + } + let variant_dtor = chain.unwrap_or_else(|| self.put_noop()); + + other = Some(match other { + None => variant_dtor, + Some(other) => { + self.put_self(); + self.put_discr(discr); + self.combine_either(other, variant_dtor) + } + }); + } + let variants_dtor = other.unwrap_or_else(|| self.put_noop()); + + let dtor = match surface { + None => variants_dtor, + Some(surface) => self.combine_chain(surface, variants_dtor), + }; + self.combine_fuse(dtor); + self.return_() + } + + fn build_chain(mut self, surface_drop: Option, elem_tys: I) -> Body<'tcx> + where + I: Iterator> + ExactSizeIterator, + { + let surface = match surface_drop { + None => None, + Some(kind) => { + self.put_self(); + Some(match kind { + SurfaceDropKind::Async => self.combine_async_surface(), + SurfaceDropKind::Sync => self.combine_sync_surface(), + }) + } + }; + + let mut chain = None; + for (field_idx, field_ty) in elem_tys.enumerate().map(|(i, ty)| (FieldIdx::new(i), ty)) { + self.put_field(field_idx, field_ty); + let defer = self.combine_defer(field_ty); + chain = Some(match chain { + None => defer, + Some(chain) => self.combine_chain(chain, defer), + }) + } + let chain = chain.unwrap_or_else(|| self.put_noop()); + + let dtor = match surface { + None => chain, + Some(surface) => self.combine_chain(surface, chain), + }; + self.combine_fuse(dtor); + self.return_() + } + + fn build_zst_output(mut self) -> Body<'tcx> { + self.put_zst_output(); + self.return_() + } + + fn build_fused_async_surface(mut self) -> Body<'tcx> { + self.put_self(); + let surface = self.combine_async_surface(); + self.combine_fuse(surface); + self.return_() + } + + fn build_fused_sync_surface(mut self) -> Body<'tcx> { + self.put_self(); + let surface = self.combine_sync_surface(); + self.combine_fuse(surface); + self.return_() + } + + fn build_slice(mut self, is_array: bool, elem_ty: Ty<'tcx>) -> Body<'tcx> { + if is_array { + self.put_array_as_slice(elem_ty) + } else { + self.put_self() + } + let dtor = self.combine_slice(elem_ty); + self.combine_fuse(dtor); + self.return_() + } + + fn put_zst_output(&mut self) { + let return_ty = self.locals[RETURN_PLACE].ty; + self.put_operand(Operand::Constant(Box::new(ConstOperand { + span: self.span, + user_ty: None, + const_: Const::zero_sized(return_ty), + }))); + } + + /// Puts `to_drop: *mut Self` on top of the stack. + fn put_self(&mut self) { + self.put_operand(Operand::Copy(Self::SELF_PTR.into())) + } + + /// Given that `Self is [ElemTy; N]` puts `to_drop: *mut [ElemTy]` + /// on top of the stack. + fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) { + let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty)); + self.put_temp_rvalue(Rvalue::Cast( + CastKind::PointerCoercion(PointerCoercion::Unsize), + Operand::Copy(Self::SELF_PTR.into()), + slice_ptr_ty, + )) + } + + /// If given Self is a struct puts `to_drop: *mut FieldTy` on top + /// of the stack. + fn put_field(&mut self, field: FieldIdx, field_ty: Ty<'tcx>) { + let place = Place { + local: Self::SELF_PTR, + projection: self + .tcx + .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]), + }; + self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place)) + } + + /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of + /// the stack. + fn put_variant_field( + &mut self, + variant_sym: Symbol, + variant: VariantIdx, + field: FieldIdx, + field_ty: Ty<'tcx>, + ) { + let place = Place { + local: Self::SELF_PTR, + projection: self.tcx.mk_place_elems(&[ + PlaceElem::Deref, + PlaceElem::Downcast(Some(variant_sym), variant), + PlaceElem::Field(field, field_ty), + ]), + }; + self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place)) + } + + /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of + /// the stack. + fn put_discr(&mut self, discr: Discr<'tcx>) { + let (size, _) = discr.ty.int_size_and_signed(self.tcx); + self.put_operand(Operand::const_from_scalar( + self.tcx, + discr.ty, + interpret::Scalar::from_uint(discr.val, size), + self.span, + )); + } + + /// Puts `x: RvalueType` on top of the stack. + fn put_temp_rvalue(&mut self, rvalue: Rvalue<'tcx>) { + let last_bb = &mut self.bbs[self.last_bb]; + debug_assert!(last_bb.terminator.is_none()); + let source_info = self.source_info; + + let local_ty = rvalue.ty(&self.locals, self.tcx); + // We need to create a new local to be able to "consume" it with + // a combinator + let local = self.locals.push(LocalDecl::with_source_info(local_ty, source_info)); + last_bb.statements.extend_from_slice(&[ + Statement { source_info, kind: StatementKind::StorageLive(local) }, + Statement { + source_info, + kind: StatementKind::Assign(Box::new((local.into(), rvalue))), + }, + ]); + + self.put_operand(Operand::Move(local.into())); + } + + /// Puts operand on top of the stack. + fn put_operand(&mut self, operand: Operand<'tcx>) { + if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb { + let source_info = self.source_info; + match &operand { + Operand::Copy(_) | Operand::Constant(_) => { + *top_cleanup_bb = self.bbs.push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Goto { target: *top_cleanup_bb }, + }), + is_cleanup: true, + }); + } + Operand::Move(place) => { + let local = place.as_local().unwrap(); + *top_cleanup_bb = self.bbs.push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { + source_info, + kind: if self.locals[local].ty.needs_drop(self.tcx, self.param_env) { + TerminatorKind::Drop { + place: local.into(), + target: *top_cleanup_bb, + unwind: UnwindAction::Terminate( + UnwindTerminateReason::InCleanup, + ), + replace: false, + } + } else { + TerminatorKind::Goto { target: *top_cleanup_bb } + }, + }), + is_cleanup: true, + }); + } + }; + } + self.stack.push(operand); + } + + /// Puts `noop: async_drop::Noop` on top of the stack + fn put_noop(&mut self) -> Ty<'tcx> { + self.apply_combinator(0, LangItem::AsyncDropNoop, &[]) + } + + fn combine_async_surface(&mut self) -> Ty<'tcx> { + self.apply_combinator(1, LangItem::SurfaceAsyncDropInPlace, &[self.self_ty.unwrap().into()]) + } + + fn combine_sync_surface(&mut self) -> Ty<'tcx> { + self.apply_combinator( + 1, + LangItem::AsyncDropSurfaceDropInPlace, + &[self.self_ty.unwrap().into()], + ) + } + + fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()]) + } + + fn combine_slice(&mut self, elem_ty: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator(1, LangItem::AsyncDropSlice, &[elem_ty.into()]) + } + + fn combine_defer(&mut self, to_drop_ty: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator(1, LangItem::AsyncDropDefer, &[to_drop_ty.into()]) + } + + fn combine_chain(&mut self, first: Ty<'tcx>, second: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator(2, LangItem::AsyncDropChain, &[first.into(), second.into()]) + } + + fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator( + 4, + LangItem::AsyncDropEither, + &[other.into(), matched.into(), self.self_ty.unwrap().into()], + ) + } + + fn return_(mut self) -> Body<'tcx> { + let last_bb = &mut self.bbs[self.last_bb]; + debug_assert!(last_bb.terminator.is_none()); + let source_info = self.source_info; + + let (1, Some(output)) = (self.stack.len(), self.stack.pop()) else { + span_bug!( + self.span, + "async destructor ctor shim builder finished with invalid number of stack items: expected 1 found {}", + self.stack.len(), + ) + }; + #[cfg(debug_assertions)] + if let Some(ty) = self.self_ty { + debug_assert_eq!( + output.ty(&self.locals, self.tcx), + ty.async_destructor_ty(self.tcx, self.param_env), + "output async destructor types did not match for type: {ty:?}", + ); + } + + let dead_storage = match &output { + Operand::Move(place) => Some(Statement { + source_info, + kind: StatementKind::StorageDead(place.as_local().unwrap()), + }), + _ => None, + }; + + last_bb.statements.extend( + iter::once(Statement { + source_info, + kind: StatementKind::Assign(Box::new((RETURN_PLACE.into(), Rvalue::Use(output)))), + }) + .chain(dead_storage), + ); + + last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return }); + + let source = MirSource::from_instance(ty::InstanceDef::AsyncDropGlueCtorShim( + self.def_id, + self.self_ty, + )); + new_body(source, self.bbs, self.locals, Self::INPUT_COUNT, self.span) + } + + fn apply_combinator( + &mut self, + arity: usize, + function: LangItem, + args: &[ty::GenericArg<'tcx>], + ) -> Ty<'tcx> { + let function = self.tcx.require_lang_item(function, Some(self.span)); + let operands_split = self + .stack + .len() + .checked_sub(arity) + .expect("async destructor ctor shim combinator tried to consume too many items"); + let operands = &self.stack[operands_split..]; + + let func_ty = Ty::new_fn_def(self.tcx, function, args.iter().copied()); + let func_sig = func_ty.fn_sig(self.tcx).no_bound_vars().unwrap(); + #[cfg(debug_assertions)] + operands.iter().zip(func_sig.inputs()).for_each(|(operand, expected_ty)| { + let operand_ty = operand.ty(&self.locals, self.tcx); + if operand_ty == *expected_ty { + return; + } + + // If projection of Discriminant then compare with `Ty::discriminant_ty` + if let ty::Alias(ty::AliasKind::Projection, ty::AliasTy { args, def_id, .. }) = + expected_ty.kind() + && Some(*def_id) == self.tcx.lang_items().discriminant_type() + && args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty + { + return; + } + + span_bug!( + self.span, + "Operand type and combinator argument type are not equal. + operand_ty: {:?} + argument_ty: {:?} +", + operand_ty, + expected_ty + ); + }); + + let target = self.bbs.push(BasicBlockData { + statements: operands + .iter() + .rev() + .filter_map(|o| { + if let Operand::Move(Place { local, projection }) = o { + assert!(projection.is_empty()); + Some(Statement { + source_info: self.source_info, + kind: StatementKind::StorageDead(*local), + }) + } else { + None + } + }) + .collect(), + terminator: None, + is_cleanup: false, + }); + + let dest_ty = func_sig.output(); + let dest = + self.locals.push(LocalDecl::with_source_info(dest_ty, self.source_info).immutable()); + + let unwind = if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb { + for _ in 0..arity { + *top_cleanup_bb = + self.bbs[*top_cleanup_bb].terminator().successors().exactly_one().ok().unwrap(); + } + UnwindAction::Cleanup(*top_cleanup_bb) + } else { + UnwindAction::Unreachable + }; + + let last_bb = &mut self.bbs[self.last_bb]; + debug_assert!(last_bb.terminator.is_none()); + last_bb.statements.push(Statement { + source_info: self.source_info, + kind: StatementKind::StorageLive(dest), + }); + last_bb.terminator = Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Call { + func: Operand::Constant(Box::new(ConstOperand { + span: self.span, + user_ty: None, + const_: Const::Val(ConstValue::ZeroSized, func_ty), + })), + destination: dest.into(), + target: Some(target), + unwind, + call_source: CallSource::Misc, + fn_span: self.span, + args: self.stack.drain(operands_split..).map(|o| respan(self.span, o)).collect(), + }, + }); + + self.put_operand(Operand::Move(dest.into())); + self.last_bb = target; + + dest_ty + } +} diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 8c8818bd68e6f..5bbe3bb747fd9 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -37,11 +37,14 @@ pub enum SimplifyCfg { Initial, PromoteConsts, RemoveFalseEdges, - EarlyOpt, - ElaborateDrops, + /// Runs at the beginning of "analysis to runtime" lowering, *before* drop elaboration. + PostAnalysis, + /// Runs at the end of "analysis to runtime" lowering, *after* drop elaboration. + /// This is before the main optimization passes on runtime MIR kick in. + PreOptimizations, Final, MakeShim, - AfterUninhabitedEnumBranching, + AfterUnreachableEnumBranching, } impl SimplifyCfg { @@ -50,12 +53,12 @@ impl SimplifyCfg { SimplifyCfg::Initial => "SimplifyCfg-initial", SimplifyCfg::PromoteConsts => "SimplifyCfg-promote-consts", SimplifyCfg::RemoveFalseEdges => "SimplifyCfg-remove-false-edges", - SimplifyCfg::EarlyOpt => "SimplifyCfg-early-opt", - SimplifyCfg::ElaborateDrops => "SimplifyCfg-elaborate-drops", + SimplifyCfg::PostAnalysis => "SimplifyCfg-post-analysis", + SimplifyCfg::PreOptimizations => "SimplifyCfg-pre-optimizations", SimplifyCfg::Final => "SimplifyCfg-final", SimplifyCfg::MakeShim => "SimplifyCfg-make_shim", - SimplifyCfg::AfterUninhabitedEnumBranching => { - "SimplifyCfg-after-uninhabited-enum-branching" + SimplifyCfg::AfterUnreachableEnumBranching => { + "SimplifyCfg-after-unreachable-enum-branching" } } } @@ -412,7 +415,7 @@ fn make_local_map( used_locals: &UsedLocals, ) -> IndexVec> { let mut map: IndexVec> = IndexVec::from_elem(None, local_decls); - let mut used = Local::new(0); + let mut used = Local::ZERO; for alive_index in local_decls.indices() { // `is_used` treats the `RETURN_PLACE` and arguments as used. diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index 35a052166bd93..c746041ebd8ac 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -19,6 +19,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyConstCondition { let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); 'blocks: for block in body.basic_blocks_mut() { for stmt in block.statements.iter_mut() { + // Simplify `assume` of a known value: either a NOP or unreachable. if let StatementKind::Intrinsic(box ref intrinsic) = stmt.kind && let NonDivergingIntrinsic::Assume(discr) = intrinsic && let Operand::Constant(ref c) = discr diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index e4fdbd6ae697d..fddc62e6652fa 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -29,7 +29,7 @@ pub struct SsaLocals { pub enum AssignedValue<'a, 'tcx> { Arg, Rvalue(&'a mut Rvalue<'tcx>), - Terminator(&'a mut TerminatorKind<'tcx>), + Terminator, } impl SsaLocals { @@ -149,8 +149,7 @@ impl SsaLocals { Set1::One(DefLocation::CallReturn { call, .. }) => { let bb = &mut basic_blocks[call]; let loc = Location { block: call, statement_index: bb.statements.len() }; - let term = bb.terminator_mut(); - f(local, AssignedValue::Terminator(&mut term.kind), loc) + f(local, AssignedValue::Terminator, loc) } _ => {} } diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs deleted file mode 100644 index 57fe46ad75aee..0000000000000 --- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs +++ /dev/null @@ -1,167 +0,0 @@ -//! A pass that eliminates branches on uninhabited enum variants. - -use crate::MirPass; -use rustc_data_structures::fx::FxHashSet; -use rustc_middle::mir::patch::MirPatch; -use rustc_middle::mir::{ - BasicBlock, BasicBlockData, BasicBlocks, Body, Local, Operand, Rvalue, StatementKind, - TerminatorKind, -}; -use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{Ty, TyCtxt}; -use rustc_target::abi::{Abi, Variants}; - -pub struct UninhabitedEnumBranching; - -fn get_discriminant_local(terminator: &TerminatorKind<'_>) -> Option { - if let TerminatorKind::SwitchInt { discr: Operand::Move(p), .. } = terminator { - p.as_local() - } else { - None - } -} - -/// If the basic block terminates by switching on a discriminant, this returns the `Ty` the -/// discriminant is read from. Otherwise, returns None. -fn get_switched_on_type<'tcx>( - block_data: &BasicBlockData<'tcx>, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, -) -> Option> { - let terminator = block_data.terminator(); - - // Only bother checking blocks which terminate by switching on a local. - let local = get_discriminant_local(&terminator.kind)?; - - let stmt_before_term = block_data.statements.last()?; - - if let StatementKind::Assign(box (l, Rvalue::Discriminant(place))) = stmt_before_term.kind - && l.as_local() == Some(local) - { - let ty = place.ty(body, tcx).ty; - if ty.is_enum() { - return Some(ty); - } - } - - None -} - -fn variant_discriminants<'tcx>( - layout: &TyAndLayout<'tcx>, - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, -) -> FxHashSet { - match &layout.variants { - Variants::Single { index } => { - let mut res = FxHashSet::default(); - res.insert( - ty.discriminant_for_variant(tcx, *index) - .map_or(index.as_u32() as u128, |discr| discr.val), - ); - res - } - Variants::Multiple { variants, .. } => variants - .iter_enumerated() - .filter_map(|(idx, layout)| { - (layout.abi != Abi::Uninhabited) - .then(|| ty.discriminant_for_variant(tcx, idx).unwrap().val) - }) - .collect(), - } -} - -impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() > 0 - } - - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - trace!("UninhabitedEnumBranching starting for {:?}", body.source); - - let mut unreachable_targets = Vec::new(); - let mut patch = MirPatch::new(body); - - for (bb, bb_data) in body.basic_blocks.iter_enumerated() { - trace!("processing block {:?}", bb); - - if bb_data.is_cleanup { - continue; - } - - let Some(discriminant_ty) = get_switched_on_type(bb_data, tcx, body) else { continue }; - - let layout = tcx.layout_of( - tcx.param_env_reveal_all_normalized(body.source.def_id()).and(discriminant_ty), - ); - - let mut allowed_variants = if let Ok(layout) = layout { - variant_discriminants(&layout, discriminant_ty, tcx) - } else if let Some(variant_range) = discriminant_ty.variant_range(tcx) { - variant_range - .map(|variant| { - discriminant_ty.discriminant_for_variant(tcx, variant).unwrap().val - }) - .collect() - } else { - continue; - }; - - trace!("allowed_variants = {:?}", allowed_variants); - - unreachable_targets.clear(); - let TerminatorKind::SwitchInt { targets, discr } = &bb_data.terminator().kind else { - bug!() - }; - - for (index, (val, _)) in targets.iter().enumerate() { - if !allowed_variants.remove(&val) { - unreachable_targets.push(index); - } - } - let otherwise_is_empty_unreachable = - body.basic_blocks[targets.otherwise()].is_empty_unreachable(); - // After resolving https://github.com/llvm/llvm-project/issues/78578, - // we can remove the limit on the number of successors. - fn check_successors(basic_blocks: &BasicBlocks<'_>, bb: BasicBlock) -> bool { - let mut successors = basic_blocks[bb].terminator().successors(); - let Some(first_successor) = successors.next() else { return true }; - if successors.next().is_some() { - return true; - } - if let TerminatorKind::SwitchInt { .. } = - &basic_blocks[first_successor].terminator().kind - { - return false; - }; - true - } - let otherwise_is_last_variant = !otherwise_is_empty_unreachable - && allowed_variants.len() == 1 - && check_successors(&body.basic_blocks, targets.otherwise()); - let replace_otherwise_to_unreachable = otherwise_is_last_variant - || !otherwise_is_empty_unreachable && allowed_variants.is_empty(); - - if unreachable_targets.is_empty() && !replace_otherwise_to_unreachable { - continue; - } - - let unreachable_block = patch.unreachable_no_cleanup_block(); - let mut targets = targets.clone(); - if replace_otherwise_to_unreachable { - if otherwise_is_last_variant { - #[allow(rustc::potential_query_instability)] - let last_variant = *allowed_variants.iter().next().unwrap(); - targets.add_target(last_variant, targets.otherwise()); - } - unreachable_targets.push(targets.iter().count()); - } - for index in unreachable_targets.iter() { - targets.all_targets_mut()[*index] = unreachable_block; - } - patch.patch_terminator(bb, TerminatorKind::SwitchInt { targets, discr: discr.clone() }); - } - - patch.apply(body); - } -} diff --git a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs new file mode 100644 index 0000000000000..66b6235eb9380 --- /dev/null +++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs @@ -0,0 +1,208 @@ +//! A pass that eliminates branches on uninhabited or unreachable enum variants. + +use crate::MirPass; +use rustc_data_structures::fx::FxHashSet; +use rustc_middle::mir::patch::MirPatch; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, BasicBlocks, Body, Local, Operand, Rvalue, StatementKind, + TerminatorKind, +}; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_target::abi::{Abi, Variants}; + +pub struct UnreachableEnumBranching; + +fn get_discriminant_local(terminator: &TerminatorKind<'_>) -> Option { + if let TerminatorKind::SwitchInt { discr: Operand::Move(p), .. } = terminator { + p.as_local() + } else { + None + } +} + +/// If the basic block terminates by switching on a discriminant, this returns the `Ty` the +/// discriminant is read from. Otherwise, returns None. +fn get_switched_on_type<'tcx>( + block_data: &BasicBlockData<'tcx>, + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, +) -> Option> { + let terminator = block_data.terminator(); + + // Only bother checking blocks which terminate by switching on a local. + let local = get_discriminant_local(&terminator.kind)?; + + let stmt_before_term = block_data.statements.last()?; + + if let StatementKind::Assign(box (l, Rvalue::Discriminant(place))) = stmt_before_term.kind + && l.as_local() == Some(local) + { + let ty = place.ty(body, tcx).ty; + if ty.is_enum() { + return Some(ty); + } + } + + None +} + +fn variant_discriminants<'tcx>( + layout: &TyAndLayout<'tcx>, + ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, +) -> FxHashSet { + match &layout.variants { + Variants::Single { index } => { + let mut res = FxHashSet::default(); + res.insert( + ty.discriminant_for_variant(tcx, *index) + .map_or(index.as_u32() as u128, |discr| discr.val), + ); + res + } + Variants::Multiple { variants, .. } => variants + .iter_enumerated() + .filter_map(|(idx, layout)| { + (layout.abi != Abi::Uninhabited) + .then(|| ty.discriminant_for_variant(tcx, idx).unwrap().val) + }) + .collect(), + } +} + +impl<'tcx> MirPass<'tcx> for UnreachableEnumBranching { + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + sess.mir_opt_level() > 0 + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + trace!("UnreachableEnumBranching starting for {:?}", body.source); + + let mut unreachable_targets = Vec::new(); + let mut patch = MirPatch::new(body); + + for (bb, bb_data) in body.basic_blocks.iter_enumerated() { + trace!("processing block {:?}", bb); + + if bb_data.is_cleanup { + continue; + } + + let Some(discriminant_ty) = get_switched_on_type(bb_data, tcx, body) else { continue }; + + let layout = tcx.layout_of( + tcx.param_env_reveal_all_normalized(body.source.def_id()).and(discriminant_ty), + ); + + let mut allowed_variants = if let Ok(layout) = layout { + // Find allowed variants based on uninhabited. + variant_discriminants(&layout, discriminant_ty, tcx) + } else if let Some(variant_range) = discriminant_ty.variant_range(tcx) { + // If there are some generics, we can still get the allowed variants. + variant_range + .map(|variant| { + discriminant_ty.discriminant_for_variant(tcx, variant).unwrap().val + }) + .collect() + } else { + continue; + }; + + trace!("allowed_variants = {:?}", allowed_variants); + + unreachable_targets.clear(); + let TerminatorKind::SwitchInt { targets, discr } = &bb_data.terminator().kind else { + bug!() + }; + + for (index, (val, _)) in targets.iter().enumerate() { + if !allowed_variants.remove(&val) { + unreachable_targets.push(index); + } + } + let otherwise_is_empty_unreachable = + body.basic_blocks[targets.otherwise()].is_empty_unreachable(); + fn check_successors(basic_blocks: &BasicBlocks<'_>, bb: BasicBlock) -> bool { + // After resolving https://github.com/llvm/llvm-project/issues/78578, + // We can remove this check. + // The main issue here is that `early-tailduplication` causes compile time overhead + // and potential performance problems. + // Simply put, when encounter a switch (indirect branch) statement, + // `early-tailduplication` tries to duplicate the switch branch statement with BB + // into (each) predecessors. This makes CFG very complex. + // We can understand it as it transforms the following code + // ```rust + // match a { ... many cases }; + // match b { ... many cases }; + // ``` + // into + // ```rust + // match a { ... many match b { goto BB cases } } + // ... BB cases + // ``` + // Abandon this transformation when it is possible (the best effort) + // to encounter the problem. + let mut successors = basic_blocks[bb].terminator().successors(); + let Some(first_successor) = successors.next() else { return true }; + if successors.next().is_some() { + return true; + } + if let TerminatorKind::SwitchInt { .. } = + &basic_blocks[first_successor].terminator().kind + { + return false; + }; + true + } + // If and only if there is a variant that does not have a branch set, + // change the current of otherwise as the variant branch and set otherwise to unreachable. + // It transforms following code + // ```rust + // match c { + // Ordering::Less => 1, + // Ordering::Equal => 2, + // _ => 3, + // } + // ``` + // to + // ```rust + // match c { + // Ordering::Less => 1, + // Ordering::Equal => 2, + // Ordering::Greater => 3, + // } + // ``` + let otherwise_is_last_variant = !otherwise_is_empty_unreachable + && allowed_variants.len() == 1 + // Despite the LLVM issue, we hope that small enum can still be transformed. + // This is valuable for both `a <= b` and `if let Some/Ok(v)`. + && (targets.all_targets().len() <= 3 + || check_successors(&body.basic_blocks, targets.otherwise())); + let replace_otherwise_to_unreachable = otherwise_is_last_variant + || (!otherwise_is_empty_unreachable && allowed_variants.is_empty()); + + if unreachable_targets.is_empty() && !replace_otherwise_to_unreachable { + continue; + } + + let unreachable_block = patch.unreachable_no_cleanup_block(); + let mut targets = targets.clone(); + if replace_otherwise_to_unreachable { + if otherwise_is_last_variant { + // We have checked that `allowed_variants` has only one element. + #[allow(rustc::potential_query_instability)] + let last_variant = *allowed_variants.iter().next().unwrap(); + targets.add_target(last_variant, targets.otherwise()); + } + unreachable_targets.push(targets.iter().count()); + } + for index in unreachable_targets.iter() { + targets.all_targets_mut()[*index] = unreachable_block; + } + patch.patch_terminator(bb, TerminatorKind::SwitchInt { targets, discr: discr.clone() }); + } + + patch.apply(body); + } +} diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index bff59aa602377..8ad7bc394c5ae 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -14,11 +14,7 @@ pub struct UnreachablePropagation; impl MirPass<'_> for UnreachablePropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { // Enable only under -Zmir-opt-level=2 as this can make programs less debuggable. - - // FIXME(#116171) Coverage gets confused by MIR passes that can remove all - // coverage statements from an instrumented function. This pass can be - // re-enabled when coverage codegen is robust against that happening. - sess.mir_opt_level() >= 2 && !sess.instrument_coverage() + sess.mir_opt_level() >= 2 } fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index cd9eb4916ce5d..dd1065590b306 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -28,6 +28,7 @@ //! - VTables //! - Object Shims //! +//! The main entry point is `collect_crate_mono_items`, at the bottom of this file. //! //! General Algorithm //! ----------------- @@ -137,21 +138,61 @@ //! just linked to and no node is created; which is exactly what we want, since //! no machine code should be generated in the current crate for such an item. //! -//! Eager and Lazy Collection Mode -//! ------------------------------ -//! Mono item collection can be performed in one of two modes: +//! Eager and Lazy Collection Strategy +//! ---------------------------------- +//! Mono item collection can be performed with one of two strategies: //! -//! - Lazy mode means that items will only be instantiated when actually +//! - Lazy strategy means that items will only be instantiated when actually //! used. The goal is to produce the least amount of machine code //! possible. //! -//! - Eager mode is meant to be used in conjunction with incremental compilation +//! - Eager strategy is meant to be used in conjunction with incremental compilation //! where a stable set of mono items is more important than a minimal -//! one. Thus, eager mode will instantiate drop-glue for every drop-able type +//! one. Thus, eager strategy will instantiate drop-glue for every drop-able type //! in the crate, even if no drop call for that type exists (yet). It will //! also instantiate default implementations of trait methods, something that //! otherwise is only done on demand. //! +//! Collection-time const evaluation and "mentioned" items +//! ------------------------------------------------------ +//! +//! One important role of collection is to evaluate all constants that are used by all the items +//! which are being collected. Codegen can then rely on only encountering constants that evaluate +//! successfully, and if a constant fails to evaluate, the collector has much better context to be +//! able to show where this constant comes up. +//! +//! However, the exact set of "used" items (collected as described above), and therefore the exact +//! set of used constants, can depend on optimizations. Optimizing away dead code may optimize away +//! a function call that uses a failing constant, so an unoptimized build may fail where an +//! optimized build succeeds. This is undesirable. +//! +//! To avoid this, the collector has the concept of "mentioned" items. Some time during the MIR +//! pipeline, before any optimization-level-dependent optimizations, we compute a list of all items +//! that syntactically appear in the code. These are considered "mentioned", and even if they are in +//! dead code and get optimized away (which makes them no longer "used"), they are still +//! "mentioned". For every used item, the collector ensures that all mentioned items, recursively, +//! do not use a failing constant. This is reflected via the [`CollectionMode`], which determines +//! whether we are visiting a used item or merely a mentioned item. +//! +//! The collector and "mentioned items" gathering (which lives in `rustc_mir_transform::mentioned_items`) +//! need to stay in sync in the following sense: +//! +//! - For every item that the collector gather that could eventually lead to build failure (most +//! likely due to containing a constant that fails to evaluate), a corresponding mentioned item +//! must be added. This should use the exact same strategy as the ecollector to make sure they are +//! in sync. However, while the collector works on monomorphized types, mentioned items are +//! collected on generic MIR -- so any time the collector checks for a particular type (such as +//! `ty::FnDef`), we have to just onconditionally add this as a mentioned item. +//! - In `visit_mentioned_item`, we then do with that mentioned item exactly what the collector +//! would have done during regular MIR visiting. Basically you can think of the collector having +//! two stages, a pre-monomorphization stage and a post-monomorphization stage (usually quite +//! literally separated by a call to `self.monomorphize`); the pre-monomorphizationn stage is +//! duplicated in mentioned items gathering and the post-monomorphization stage is duplicated in +//! `visit_mentioned_item`. +//! - Finally, as a performance optimization, the collector should fill `used_mentioned_item` during +//! its MIR traversal with exactly what mentioned item gathering would have added in the same +//! situation. This detects mentioned items that have *not* been optimized away and hence don't +//! need a dedicated traversal. //! //! Open Issues //! ----------- @@ -164,16 +205,20 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. +mod move_check; + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::{par_for_each_in, MTLock, MTLockRef}; +use rustc_data_structures::sync::{par_for_each_in, LRef, MTLock}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::lang_items::LangItem; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::mir::traversal; use rustc_middle::mir::visit::Visitor as MirVisitor; -use rustc_middle::mir::{self, Location}; +use rustc_middle::mir::{self, Location, MentionedItem}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; @@ -183,9 +228,8 @@ use rustc_middle::ty::{ TypeVisitableExt, VtblEntry, }; use rustc_middle::ty::{GenericArgKind, GenericArgs}; -use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext}; +use rustc_middle::{bug, span_bug}; use rustc_session::config::EntryFnType; -use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; use rustc_session::Limit; use rustc_span::source_map::{dummy_spanned, respan, Spanned}; use rustc_span::symbol::{sym, Ident}; @@ -194,12 +238,12 @@ use rustc_target::abi::Size; use std::path::PathBuf; use crate::errors::{ - self, EncounteredErrorWhileInstantiating, LargeAssignmentsLint, NoOptimizedMir, RecursionLimit, - TypeLengthLimit, + self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit, TypeLengthLimit, }; +use move_check::MoveCheckState; #[derive(PartialEq)] -pub enum MonoItemCollectionMode { +pub enum MonoItemCollectionStrategy { Eager, Lazy, } @@ -214,6 +258,35 @@ pub struct UsageMap<'tcx> { type MonoItems<'tcx> = Vec>>; +/// The state that is shared across the concurrent threads that are doing collection. +struct SharedState<'tcx> { + /// Items that have been or are currently being recursively collected. + visited: MTLock>>, + /// Items that have been or are currently being recursively treated as "mentioned", i.e., their + /// consts are evaluated but nothing is added to the collection. + mentioned: MTLock>>, + /// Which items are being used where, for better errors. + usage_map: MTLock>, +} + +/// See module-level docs on some contect for "mentioned" items. +#[derive(Copy, Clone, Debug, PartialEq)] +enum CollectionMode { + /// Collect items that are used, i.e., actually needed for codegen. + /// + /// Which items are used can depend on optimization levels, as MIR optimizations can remove + /// uses. + UsedItems, + /// Collect items that are mentioned. The goal of this mode is that it is independent of + /// optimizations: the set of "mentioned" items is computed before optimizations are run. + /// + /// The exact contents of this set are *not* a stable guarantee. (For instance, it is currently + /// computed after drop-elaboration. If we ever do some optimizations even in debug builds, we + /// might decide to run them before computing mentioned items.) The key property of this set is + /// that it is optimization-independent. + MentionedItems, +} + impl<'tcx> UsageMap<'tcx> { fn new() -> UsageMap<'tcx> { UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() } @@ -253,99 +326,40 @@ impl<'tcx> UsageMap<'tcx> { } } -#[instrument(skip(tcx, mode), level = "debug")] -pub fn collect_crate_mono_items( - tcx: TyCtxt<'_>, - mode: MonoItemCollectionMode, -) -> (FxHashSet>, UsageMap<'_>) { - let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); - - let roots = - tcx.sess.time("monomorphization_collector_root_collections", || collect_roots(tcx, mode)); - - debug!("building mono item graph, beginning at roots"); - - let mut visited = MTLock::new(FxHashSet::default()); - let mut usage_map = MTLock::new(UsageMap::new()); - let recursion_limit = tcx.recursion_limit(); - - { - let visited: MTLockRef<'_, _> = &mut visited; - let usage_map: MTLockRef<'_, _> = &mut usage_map; - - tcx.sess.time("monomorphization_collector_graph_walk", || { - par_for_each_in(roots, |root| { - let mut recursion_depths = DefIdMap::default(); - collect_items_rec( - tcx, - dummy_spanned(root), - visited, - &mut recursion_depths, - recursion_limit, - usage_map, - ); - }); - }); - } - - (visited.into_inner(), usage_map.into_inner()) -} - -// Find all non-generic items by walking the HIR. These items serve as roots to -// start monomorphizing from. -#[instrument(skip(tcx, mode), level = "debug")] -fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec> { - debug!("collecting roots"); - let mut roots = Vec::new(); - - { - let entry_fn = tcx.entry_fn(()); - - debug!("collect_roots: entry_fn = {:?}", entry_fn); - - let mut collector = RootCollector { tcx, mode, entry_fn, output: &mut roots }; - - let crate_items = tcx.hir_crate_items(()); - - for id in crate_items.items() { - collector.process_item(id); - } - - for id in crate_items.impl_items() { - collector.process_impl_item(id); - } - - collector.push_extra_entry_roots(); - } - - // We can only codegen items that are instantiable - items all of - // whose predicates hold. Luckily, items that aren't instantiable - // can't actually be used, so we can just skip codegenning them. - roots - .into_iter() - .filter_map(|Spanned { node: mono_item, .. }| { - mono_item.is_instantiable(tcx).then_some(mono_item) - }) - .collect() -} - /// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a /// post-monomorphization error is encountered during a collection step. -#[instrument(skip(tcx, visited, recursion_depths, recursion_limit, usage_map), level = "debug")] +/// +/// `mode` determined whether we are scanning for [used items][CollectionMode::UsedItems] +/// or [mentioned items][CollectionMode::MentionedItems]. +#[instrument(skip(tcx, state, recursion_depths, recursion_limit), level = "debug")] fn collect_items_rec<'tcx>( tcx: TyCtxt<'tcx>, starting_item: Spanned>, - visited: MTLockRef<'_, FxHashSet>>, + state: LRef<'_, SharedState<'tcx>>, recursion_depths: &mut DefIdMap, recursion_limit: Limit, - usage_map: MTLockRef<'_, UsageMap<'tcx>>, + mode: CollectionMode, ) { - if !visited.lock_mut().insert(starting_item.node) { - // We've been here already, no need to search again. - return; + if mode == CollectionMode::UsedItems { + if !state.visited.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + } else { + if state.visited.lock().contains(&starting_item.node) { + // We've already done a *full* visit on this one, no need to do the "mention" visit. + return; + } + if !state.mentioned.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + // There's some risk that we first do a 'mention' visit and then a full visit. But there's no + // harm in that, the mention visit will trigger all the queries and the results are cached. } - let mut used_items = Vec::new(); + let mut used_items = MonoItems::new(); + let mut mentioned_items = MonoItems::new(); let recursion_depth_reset; // Post-monomorphization errors MVP @@ -373,41 +387,52 @@ fn collect_items_rec<'tcx>( // FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do. let error_count = tcx.dcx().err_count(); + // In `mentioned_items` we collect items that were mentioned in this MIR but possibly do not + // need to be monomorphized. This is done to ensure that optimizing away function calls does not + // hide const-eval errors that those calls would otherwise have triggered. match starting_item.node { MonoItem::Static(def_id) => { - let instance = Instance::mono(tcx, def_id); + recursion_depth_reset = None; - // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, &instance)); + // Statics always get evaluted (which is possible because they can't be generic), so for + // `MentionedItems` collection there's nothing to do here. + if mode == CollectionMode::UsedItems { + let instance = Instance::mono(tcx, def_id); - let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; - // Nested statics have no type. - if !nested { - let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); - } + // Sanity check whether this ended up being collected accidentally + debug_assert!(should_codegen_locally(tcx, instance)); - recursion_depth_reset = None; + let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; + // Nested statics have no type. + if !nested { + let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); + } - if let Ok(alloc) = tcx.eval_static_initializer(def_id) { - for &prov in alloc.inner().provenance().ptrs().values() { - collect_alloc(tcx, prov.alloc_id(), &mut used_items); + if let Ok(alloc) = tcx.eval_static_initializer(def_id) { + for &prov in alloc.inner().provenance().ptrs().values() { + collect_alloc(tcx, prov.alloc_id(), &mut used_items); + } } - } - if tcx.needs_thread_local_shim(def_id) { - used_items.push(respan( - starting_item.span, - MonoItem::Fn(Instance { - def: InstanceDef::ThreadLocalShim(def_id), - args: GenericArgs::empty(), - }), - )); + if tcx.needs_thread_local_shim(def_id) { + used_items.push(respan( + starting_item.span, + MonoItem::Fn(Instance { + def: InstanceDef::ThreadLocalShim(def_id), + args: GenericArgs::empty(), + }), + )); + } } + + // mentioned_items stays empty since there's no codegen for statics. statics don't get + // optimized, and if they did then the const-eval interpreter would have to worry about + // mentioned_items. } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, &instance)); + debug_assert!(should_codegen_locally(tcx, instance)); // Keep track of the monomorphization recursion depth recursion_depth_reset = Some(check_recursion_limit( @@ -420,10 +445,20 @@ fn collect_items_rec<'tcx>( check_type_length_limit(tcx, instance); rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_used_items(tcx, instance, &mut used_items); + collect_items_of_instance( + tcx, + instance, + &mut used_items, + &mut mentioned_items, + mode, + ) }); } MonoItem::GlobalAsm(item_id) => { + assert!( + mode == CollectionMode::UsedItems, + "should never encounter global_asm when collecting mentioned items" + ); recursion_depth_reset = None; let item = tcx.hir().item(item_id); @@ -442,7 +477,7 @@ fn collect_items_rec<'tcx>( } hir::InlineAsmOperand::SymStatic { path: _, def_id } => { let instance = Instance::mono(tcx, *def_id); - if should_codegen_locally(tcx, &instance) { + if should_codegen_locally(tcx, instance) { trace!("collecting static {:?}", def_id); used_items.push(dummy_spanned(MonoItem::Static(*def_id))); } @@ -459,8 +494,10 @@ fn collect_items_rec<'tcx>( } else { span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") } + + // mention_items stays empty as nothing gets optimized here. } - } + }; // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the // mono item graph. @@ -474,10 +511,41 @@ fn collect_items_rec<'tcx>( formatted_item, }); } - usage_map.lock_mut().record_used(starting_item.node, &used_items); + // Only updating `usage_map` for used items as otherwise we may be inserting the same item + // multiple times (if it is first 'mentioned' and then later actuall used), and the usage map + // logic does not like that. + // This is part of the output of collection and hence only relevant for "used" items. + // ("Mentioned" items are only considered internally during collection.) + if mode == CollectionMode::UsedItems { + state.usage_map.lock_mut().record_used(starting_item.node, &used_items); + } + + if mode == CollectionMode::MentionedItems { + assert!(used_items.is_empty(), "'mentioned' collection should never encounter used items"); + } else { + for used_item in used_items { + collect_items_rec( + tcx, + used_item, + state, + recursion_depths, + recursion_limit, + CollectionMode::UsedItems, + ); + } + } - for used_item in used_items { - collect_items_rec(tcx, used_item, visited, recursion_depths, recursion_limit, usage_map); + // Walk over mentioned items *after* used items, so that if an item is both mentioned and used then + // the loop above has fully collected it, so this loop will skip it. + for mentioned_item in mentioned_items { + collect_items_rec( + tcx, + mentioned_item, + state, + recursion_depths, + recursion_limit, + CollectionMode::MentionedItems, + ); } if let Some((def_id, depth)) = recursion_depth_reset { @@ -492,7 +560,7 @@ fn collect_items_rec<'tcx>( /// If the type name is longer than before+after, it will be written to a file. fn shrunk_instance_name<'tcx>( tcx: TyCtxt<'tcx>, - instance: &Instance<'tcx>, + instance: Instance<'tcx>, ) -> (String, Option) { let s = instance.to_string(); @@ -538,7 +606,7 @@ fn check_recursion_limit<'tcx>( if !recursion_limit.value_within_limit(adjusted_recursion_depth) { let def_span = tcx.def_span(def_id); let def_path_str = tcx.def_path_str(def_id); - let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance); + let (shrunk, written_to_path) = shrunk_instance_name(tcx, instance); let mut path = PathBuf::new(); let was_written = if let Some(written_to_path) = written_to_path { path = written_to_path; @@ -580,7 +648,7 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { // // Bail out in these cases to avoid that bad user experience. if !tcx.type_length_limit().value_within_limit(type_length) { - let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance); + let (shrunk, written_to_path) = shrunk_instance_name(tcx, instance); let span = tcx.def_span(instance.def_id()); let mut path = PathBuf::new(); let was_written = if let Some(path2) = written_to_path { @@ -596,21 +664,21 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { struct MirUsedCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, - output: &'a mut MonoItems<'tcx>, + used_items: &'a mut MonoItems<'tcx>, + /// See the comment in `collect_items_of_instance` for the purpose of this set. + /// Note that this contains *not-monomorphized* items! + used_mentioned_items: &'a mut FxHashSet>, instance: Instance<'tcx>, - /// Spans for move size lints already emitted. Helps avoid duplicate lints. - move_size_spans: Vec, visiting_call_terminator: bool, - /// Set of functions for which it is OK to move large data into. - skip_move_check_fns: Option>, + move_check: move_check::MoveCheckState, } impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { - pub fn monomorphize(&self, value: T) -> T + fn monomorphize(&self, value: T) -> T where T: TypeFoldable>, { - debug!("monomorphize: self.instance={:?}", self.instance); + trace!("monomorphize: self.instance={:?}", self.instance); self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::ParamEnv::reveal_all(), @@ -618,122 +686,29 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { ) } - fn check_operand_move_size(&mut self, operand: &mir::Operand<'tcx>, location: Location) { - let limit = self.tcx.move_size_limit(); - if limit.0 == 0 { - return; - } - - // This function is called by visit_operand() which visits _all_ - // operands, including TerminatorKind::Call operands. But if - // check_fn_args_move_size() has been called, the operands have already - // been visited. Do not visit them again. - if self.visiting_call_terminator { - return; - } - - let source_info = self.body.source_info(location); - debug!(?source_info); - - if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) { - self.lint_large_assignment(limit.0, too_large_size, location, source_info.span); - }; - } - - fn check_fn_args_move_size( - &mut self, - callee_ty: Ty<'tcx>, - args: &[Spanned>], - fn_span: Span, - location: Location, - ) { - let limit = self.tcx.move_size_limit(); - if limit.0 == 0 { - return; - } - - if args.is_empty() { - return; - } - - // Allow large moves into container types that themselves are cheap to move - let ty::FnDef(def_id, _) = *callee_ty.kind() else { - return; - }; - if self - .skip_move_check_fns - .get_or_insert_with(|| build_skip_move_check_fns(self.tcx)) - .contains(&def_id) - { - return; - } - - debug!(?def_id, ?fn_span); - - for arg in args { - // Moving args into functions is typically implemented with pointer - // passing at the llvm-ir level and not by memcpy's. So always allow - // moving args into functions. - let operand: &mir::Operand<'tcx> = &arg.node; - if let mir::Operand::Move(_) = operand { - continue; - } - - if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) { - self.lint_large_assignment(limit.0, too_large_size, location, arg.span); - }; - } - } - - fn operand_size_if_too_large( - &mut self, - limit: Limit, - operand: &mir::Operand<'tcx>, - ) -> Option { - let ty = operand.ty(self.body, self.tcx); - let ty = self.monomorphize(ty); - let Ok(layout) = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) else { - return None; - }; - if layout.size.bytes_usize() > limit.0 { - debug!(?layout); - Some(layout.size) - } else { - None - } - } - - fn lint_large_assignment( + /// Evaluates a *not yet monomorphized* constant. + fn eval_constant( &mut self, - limit: usize, - too_large_size: Size, - location: Location, - span: Span, - ) { - let source_info = self.body.source_info(location); - debug!(?source_info); - for reported_span in &self.move_size_spans { - if reported_span.overlaps(span) { - return; + constant: &mir::ConstOperand<'tcx>, + ) -> Option> { + let const_ = self.monomorphize(constant.const_); + let param_env = ty::ParamEnv::reveal_all(); + // Evaluate the constant. This makes const eval failure a collection-time error (rather than + // a codegen-time error). rustc stops after collection if there was an error, so this + // ensures codegen never has to worry about failing consts. + // (codegen relies on this and ICEs will happen if this is violated.) + match const_.eval(self.tcx, param_env, constant.span) { + Ok(v) => Some(v), + Err(ErrorHandled::TooGeneric(..)) => span_bug!( + constant.span, + "collection encountered polymorphic constant: {:?}", + const_ + ), + Err(err @ ErrorHandled::Reported(..)) => { + err.emit_note(self.tcx); + return None; } } - let lint_root = source_info.scope.lint_root(&self.body.source_scopes); - debug!(?lint_root); - let Some(lint_root) = lint_root else { - // This happens when the issue is in a function from a foreign crate that - // we monomorphized in the current crate. We can't get a `HirId` for things - // in other crates. - // FIXME: Find out where to report the lint on. Maybe simply crate-level lint root - // but correct span? This would make the lint at least accept crate-level lint attributes. - return; - }; - self.tcx.emit_node_span_lint( - LARGE_ASSIGNMENTS, - lint_root, - span, - LargeAssignmentsLint { span, size: too_large_size.bytes(), limit: limit as u64 }, - ); - self.move_size_spans.push(span); } } @@ -753,8 +728,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { target_ty, ) | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { - let target_ty = self.monomorphize(target_ty); let source_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items + .insert(MentionedItem::UnsizeCast { source_ty, target_ty }); + let target_ty = self.monomorphize(target_ty); let source_ty = self.monomorphize(source_ty); let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.tcx.at(span), source_ty, target_ty); @@ -769,7 +747,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { target_ty, source_ty, span, - self.output, + self.used_items, ); } } @@ -779,8 +757,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { _, ) => { let fn_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(fn_ty)); let fn_ty = self.monomorphize(fn_ty); - visit_fn_use(self.tcx, fn_ty, false, span, self.output); + visit_fn_use(self.tcx, fn_ty, false, span, self.used_items); } mir::Rvalue::Cast( mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), @@ -788,28 +768,25 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { _, ) => { let source_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Closure(source_ty)); let source_ty = self.monomorphize(source_ty); - match *source_ty.kind() { - ty::Closure(def_id, args) => { - let instance = Instance::resolve_closure( - self.tcx, - def_id, - args, - ty::ClosureKind::FnOnce, - ); - if should_codegen_locally(self.tcx, &instance) { - self.output.push(create_fn_mono_item(self.tcx, instance, span)); - } + if let ty::Closure(def_id, args) = *source_ty.kind() { + let instance = + Instance::resolve_closure(self.tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(self.tcx, instance) { + self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); } - _ => bug!(), + } else { + bug!() } } mir::Rvalue::ThreadLocalRef(def_id) => { assert!(self.tcx.is_thread_local_static(def_id)); let instance = Instance::mono(self.tcx, def_id); - if should_codegen_locally(self.tcx, &instance) { + if should_codegen_locally(self.tcx, instance) { trace!("collecting thread-local static {:?}", def_id); - self.output.push(respan(span, MonoItem::Static(def_id))); + self.used_items.push(respan(span, MonoItem::Static(def_id))); } } _ => { /* not interesting */ } @@ -822,26 +799,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { /// to ensure that the constant evaluates successfully and walk the result. #[instrument(skip(self), level = "debug")] fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) { - let const_ = self.monomorphize(constant.const_); - let param_env = ty::ParamEnv::reveal_all(); - // Evaluate the constant. This makes const eval failure a collection-time error (rather than - // a codegen-time error). rustc stops after collection if there was an error, so this - // ensures codegen never has to worry about failing consts. - // (codegen relies on this and ICEs will happen if this is violated.) - let val = match const_.eval(self.tcx, param_env, Some(constant.span)) { - Ok(v) => v, - Err(ErrorHandled::TooGeneric(..)) => span_bug!( - self.body.source_info(location).span, - "collection encountered polymorphic constant: {:?}", - const_ - ), - Err(err @ ErrorHandled::Reported(..)) => { - err.emit_note(self.tcx); - return; - } - }; - collect_const_value(self.tcx, val, self.output); - MirVisitor::visit_ty(self, const_.ty(), TyContext::Location(location)); + // No `super_constant` as we don't care about `visit_ty`/`visit_ty_const`. + let Some(val) = self.eval_constant(constant) else { return }; + collect_const_value(self.tcx, val, self.used_items); } fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { @@ -851,51 +811,59 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let tcx = self.tcx; let push_mono_lang_item = |this: &mut Self, lang_item: LangItem| { let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source))); - if should_codegen_locally(tcx, &instance) { - this.output.push(create_fn_mono_item(tcx, instance, source)); + if should_codegen_locally(tcx, instance) { + this.used_items.push(create_fn_mono_item(tcx, instance, source)); } }; match terminator.kind { mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { let callee_ty = func.ty(self.body, tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); let callee_ty = self.monomorphize(callee_ty); self.check_fn_args_move_size(callee_ty, args, *fn_span, location); - visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output) + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) } mir::TerminatorKind::Drop { ref place, .. } => { let ty = place.ty(self.body, self.tcx).ty; + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Drop(ty)); let ty = self.monomorphize(ty); - visit_drop_use(self.tcx, ty, true, source, self.output); + visit_drop_use(self.tcx, ty, true, source, self.used_items); } mir::TerminatorKind::InlineAsm { ref operands, .. } => { for op in operands { match *op { mir::InlineAsmOperand::SymFn { ref value } => { - let fn_ty = self.monomorphize(value.const_.ty()); - visit_fn_use(self.tcx, fn_ty, false, source, self.output); + let fn_ty = value.const_.ty(); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(fn_ty)); + let fn_ty = self.monomorphize(fn_ty); + visit_fn_use(self.tcx, fn_ty, false, source, self.used_items); } mir::InlineAsmOperand::SymStatic { def_id } => { let instance = Instance::mono(self.tcx, def_id); - if should_codegen_locally(self.tcx, &instance) { + if should_codegen_locally(self.tcx, instance) { trace!("collecting asm sym static {:?}", def_id); - self.output.push(respan(source, MonoItem::Static(def_id))); + self.used_items.push(respan(source, MonoItem::Static(def_id))); } } _ => {} } } } - mir::TerminatorKind::Assert { ref msg, .. } => { - let lang_item = match &**msg { - mir::AssertKind::BoundsCheck { .. } => LangItem::PanicBoundsCheck, - mir::AssertKind::MisalignedPointerDereference { .. } => { - LangItem::PanicMisalignedPointerDereference - } - _ => LangItem::Panic, - }; - push_mono_lang_item(self, lang_item); - } + mir::TerminatorKind::Assert { ref msg, .. } => match &**msg { + mir::AssertKind::BoundsCheck { .. } => { + push_mono_lang_item(self, LangItem::PanicBoundsCheck); + } + mir::AssertKind::MisalignedPointerDereference { .. } => { + push_mono_lang_item(self, LangItem::PanicMisalignedPointerDereference); + } + _ => { + push_mono_lang_item(self, msg.panic_function()); + } + }, mir::TerminatorKind::UnwindTerminate(reason) => { push_mono_lang_item(self, reason.lang_item()); } @@ -936,6 +904,8 @@ fn visit_drop_use<'tcx>( visit_instance_use(tcx, instance, is_direct_call, source, output); } +/// For every call of this function in the visitor, make sure there is a matching call in the +/// `mentioned_items` pass! fn visit_fn_use<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, @@ -964,7 +934,7 @@ fn visit_instance_use<'tcx>( output: &mut MonoItems<'tcx>, ) { debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); - if !should_codegen_locally(tcx, &instance) { + if !should_codegen_locally(tcx, instance) { return; } if let ty::InstanceDef::Intrinsic(def_id) = instance.def { @@ -976,13 +946,13 @@ fn visit_instance_use<'tcx>( // codegen a call to that function without generating code for the function itself. let def_id = tcx.lang_items().get(LangItem::PanicNounwind).unwrap(); let panic_instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, &panic_instance) { + if should_codegen_locally(tcx, panic_instance) { output.push(create_fn_mono_item(tcx, panic_instance, source)); } } else if tcx.has_attr(def_id, sym::rustc_intrinsic) { // Codegen the fallback body of intrinsics with fallback bodies let instance = ty::Instance::new(def_id, instance.args); - if should_codegen_locally(tcx, &instance) { + if should_codegen_locally(tcx, instance) { output.push(create_fn_mono_item(tcx, instance, source)); } } @@ -997,13 +967,14 @@ fn visit_instance_use<'tcx>( ty::InstanceDef::ThreadLocalShim(..) => { bug!("{:?} being reified", instance); } - ty::InstanceDef::DropGlue(_, None) => { + ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None) => { // Don't need to emit noop drop glue if we are calling directly. if !is_direct_call { output.push(create_fn_mono_item(tcx, instance, source)); } } ty::InstanceDef::DropGlue(_, Some(_)) + | ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) | ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } @@ -1020,7 +991,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. -fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { +pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; @@ -1030,11 +1001,6 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> return false; } - if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) { - // These are implemented by backends directly and have no meaningful body. - return false; - } - if def_id.is_local() { // Local items cannot be referred to locally without monomorphizing them locally. return true; @@ -1129,10 +1095,8 @@ fn find_vtable_types_for_unsizing<'tcx>( }; match (&source_ty.kind(), &target_ty.kind()) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) - | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - ptr_vtable(*a, *b) - } + (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) + | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => ptr_vtable(*a, *b), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty()) } @@ -1202,34 +1166,212 @@ fn create_mono_items_for_vtable_methods<'tcx>( ) { assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars()); - if let ty::Dynamic(trait_ty, ..) = trait_ty.kind() { - if let Some(principal) = trait_ty.principal() { - let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); - assert!(!poly_trait_ref.has_escaping_bound_vars()); - - // Walk all methods of the trait, including those of its supertraits - let entries = tcx.vtable_entries(poly_trait_ref); - let methods = entries - .iter() - .filter_map(|entry| match entry { - VtblEntry::MetadataDropInPlace - | VtblEntry::MetadataSize - | VtblEntry::MetadataAlign - | VtblEntry::Vacant => None, - VtblEntry::TraitVPtr(_) => { - // all super trait items already covered, so skip them. - None - } - VtblEntry::Method(instance) => { - Some(*instance).filter(|instance| should_codegen_locally(tcx, instance)) + let ty::Dynamic(trait_ty, ..) = trait_ty.kind() else { + bug!("create_mono_items_for_vtable_methods: {trait_ty:?} not a trait type"); + }; + if let Some(principal) = trait_ty.principal() { + let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); + assert!(!poly_trait_ref.has_escaping_bound_vars()); + + // Walk all methods of the trait, including those of its supertraits + let entries = tcx.vtable_entries(poly_trait_ref); + debug!(?entries); + let methods = entries + .iter() + .filter_map(|entry| match entry { + VtblEntry::MetadataDropInPlace + | VtblEntry::MetadataSize + | VtblEntry::MetadataAlign + | VtblEntry::Vacant => None, + VtblEntry::TraitVPtr(_) => { + // all super trait items already covered, so skip them. + None + } + VtblEntry::Method(instance) => { + Some(*instance).filter(|instance| should_codegen_locally(tcx, *instance)) + } + }) + .map(|item| create_fn_mono_item(tcx, item, source)); + output.extend(methods); + } + + // Also add the destructor. + visit_drop_use(tcx, impl_ty, false, source, output); +} + +/// Scans the CTFE alloc in order to find function pointers and statics that must be monomorphized. +fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoItems<'tcx>) { + match tcx.global_alloc(alloc_id) { + GlobalAlloc::Static(def_id) => { + assert!(!tcx.is_thread_local_static(def_id)); + let instance = Instance::mono(tcx, def_id); + if should_codegen_locally(tcx, instance) { + trace!("collecting static {:?}", def_id); + output.push(dummy_spanned(MonoItem::Static(def_id))); + } + } + GlobalAlloc::Memory(alloc) => { + trace!("collecting {:?} with {:#?}", alloc_id, alloc); + let ptrs = alloc.inner().provenance().ptrs(); + // avoid `ensure_sufficient_stack` in the common case of "no pointers" + if !ptrs.is_empty() { + rustc_data_structures::stack::ensure_sufficient_stack(move || { + for &prov in ptrs.values() { + collect_alloc(tcx, prov.alloc_id(), output); } - }) - .map(|item| create_fn_mono_item(tcx, item, source)); - output.extend(methods); + }); + } + } + GlobalAlloc::Function(fn_instance) => { + if should_codegen_locally(tcx, fn_instance) { + trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); + output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); + } + } + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + collect_alloc(tcx, alloc_id, output) + } + } +} + +fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { + for impl_def_id in tcx.inherent_impls(def_id).ok()? { + if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + tcx, + fn_ident, + AssocKind::Fn, + def_id, + ) { + return Some(new.def_id); + } + } + return None; +} + +/// Scans the MIR in order to find function calls, closures, and drop-glue. +/// +/// Anything that's found is added to `output`. Furthermore the "mentioned items" of the MIR are returned. +#[instrument(skip(tcx, used_items, mentioned_items), level = "debug")] +fn collect_items_of_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + used_items: &mut MonoItems<'tcx>, + mentioned_items: &mut MonoItems<'tcx>, + mode: CollectionMode, +) { + let body = tcx.instance_mir(instance.def); + // Naively, in "used" collection mode, all functions get added to *both* `used_items` and + // `mentioned_items`. Mentioned items processing will then notice that they have already been + // visited, but at that point each mentioned item has been monomorphized, added to the + // `mentioned_items` worklist, and checked in the global set of visited items. To remove that + // overhead, we have a special optimization that avoids adding items to `mentioned_items` when + // they are already added in `used_items`. We could just scan `used_items`, but that's a linear + // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the + // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already + // added to `used_items` in a hash set, which can efficiently query in the + // `body.mentioned_items` loop below without even having to monomorphize the item. + let mut used_mentioned_items = FxHashSet::>::default(); + let mut collector = MirUsedCollector { + tcx, + body, + used_items, + used_mentioned_items: &mut used_mentioned_items, + instance, + visiting_call_terminator: false, + move_check: MoveCheckState::new(), + }; + + if mode == CollectionMode::UsedItems { + for (bb, data) in traversal::mono_reachable(body, tcx, instance) { + collector.visit_basic_block_data(bb, data) + } + } + + // Always visit all `required_consts`, so that we evaluate them and abort compilation if any of + // them errors. + for const_op in &body.required_consts { + if let Some(val) = collector.eval_constant(const_op) { + collect_const_value(tcx, val, mentioned_items); + } + } + + // Always gather mentioned items. We try to avoid processing items that we have already added to + // `used_items` above. + for item in &body.mentioned_items { + if !collector.used_mentioned_items.contains(&item.node) { + let item_mono = collector.monomorphize(item.node); + visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); + } + } +} + +/// `item` must be already monomorphized. +#[instrument(skip(tcx, span, output), level = "debug")] +fn visit_mentioned_item<'tcx>( + tcx: TyCtxt<'tcx>, + item: &MentionedItem<'tcx>, + span: Span, + output: &mut MonoItems<'tcx>, +) { + match *item { + MentionedItem::Fn(ty) => { + if let ty::FnDef(def_id, args) = *ty.kind() { + let instance = + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); + // `visit_instance_use` was written for "used" item collection but works just as well + // for "mentioned" item collection. + // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway + // can't have their own failing constants. + visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); + } + } + MentionedItem::Drop(ty) => { + visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output); } + MentionedItem::UnsizeCast { source_ty, target_ty } => { + let (source_ty, target_ty) = + find_vtable_types_for_unsizing(tcx.at(span), source_ty, target_ty); + // This could also be a different Unsize instruction, like + // from a fixed sized array to a slice. But we are only + // interested in things that produce a vtable. + if (target_ty.is_trait() && !source_ty.is_trait()) + || (target_ty.is_dyn_star() && !source_ty.is_dyn_star()) + { + create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, span, output); + } + } + MentionedItem::Closure(source_ty) => { + if let ty::Closure(def_id, args) = *source_ty.kind() { + let instance = + Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(tcx, instance) { + output.push(create_fn_mono_item(tcx, instance, span)); + } + } else { + bug!() + } + } + } +} - // Also add the destructor. - visit_drop_use(tcx, impl_ty, false, source, output); +#[instrument(skip(tcx, output), level = "debug")] +fn collect_const_value<'tcx>( + tcx: TyCtxt<'tcx>, + value: mir::ConstValue<'tcx>, + output: &mut MonoItems<'tcx>, +) { + match value { + mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => { + collect_alloc(tcx, ptr.provenance.alloc_id(), output) + } + mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output), + mir::ConstValue::Slice { data, meta: _ } => { + for &prov in data.inner().provenance().ptrs().values() { + collect_alloc(tcx, prov.alloc_id(), output); + } + } + _ => {} } } @@ -1237,9 +1379,47 @@ fn create_mono_items_for_vtable_methods<'tcx>( // Root Collection //=----------------------------------------------------------------------------- +// Find all non-generic items by walking the HIR. These items serve as roots to +// start monomorphizing from. +#[instrument(skip(tcx, mode), level = "debug")] +fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec> { + debug!("collecting roots"); + let mut roots = Vec::new(); + + { + let entry_fn = tcx.entry_fn(()); + + debug!("collect_roots: entry_fn = {:?}", entry_fn); + + let mut collector = RootCollector { tcx, strategy: mode, entry_fn, output: &mut roots }; + + let crate_items = tcx.hir_crate_items(()); + + for id in crate_items.free_items() { + collector.process_item(id); + } + + for id in crate_items.impl_items() { + collector.process_impl_item(id); + } + + collector.push_extra_entry_roots(); + } + + // We can only codegen items that are instantiable - items all of + // whose predicates hold. Luckily, items that aren't instantiable + // can't actually be used, so we can just skip codegenning them. + roots + .into_iter() + .filter_map(|Spanned { node: mono_item, .. }| { + mono_item.is_instantiable(tcx).then_some(mono_item) + }) + .collect() +} + struct RootCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, - mode: MonoItemCollectionMode, + strategy: MonoItemCollectionStrategy, output: &'a mut MonoItems<'tcx>, entry_fn: Option<(DefId, EntryFnType)>, } @@ -1248,7 +1428,7 @@ impl<'v> RootCollector<'_, 'v> { fn process_item(&mut self, id: hir::ItemId) { match self.tcx.def_kind(id.owner_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { - if self.mode == MonoItemCollectionMode::Eager + if self.strategy == MonoItemCollectionStrategy::Eager && self.tcx.generics_of(id.owner_id).count() == 0 { debug!("RootCollector: ADT drop-glue for `{id:?}`",); @@ -1279,7 +1459,7 @@ impl<'v> RootCollector<'_, 'v> { } } DefKind::Impl { .. } => { - if self.mode == MonoItemCollectionMode::Eager { + if self.strategy == MonoItemCollectionStrategy::Eager { create_mono_items_for_default_impls(self.tcx, id, self.output); } } @@ -1298,9 +1478,9 @@ impl<'v> RootCollector<'_, 'v> { fn is_root(&self, def_id: LocalDefId) -> bool { !self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) - && match self.mode { - MonoItemCollectionMode::Eager => true, - MonoItemCollectionMode::Lazy => { + && match self.strategy { + MonoItemCollectionStrategy::Eager => true, + MonoItemCollectionStrategy::Lazy => { self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id) || self.tcx.is_reachable_non_generic(def_id) || self @@ -1427,110 +1607,53 @@ fn create_mono_items_for_default_impls<'tcx>( let instance = ty::Instance::expect_resolve(tcx, param_env, method.def_id, args); let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); - if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, &instance) { + if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, instance) { output.push(mono_item); } } } -/// Scans the CTFE alloc in order to find function calls, closures, and drop-glue. -fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoItems<'tcx>) { - match tcx.global_alloc(alloc_id) { - GlobalAlloc::Static(def_id) => { - assert!(!tcx.is_thread_local_static(def_id)); - let instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, &instance) { - trace!("collecting static {:?}", def_id); - output.push(dummy_spanned(MonoItem::Static(def_id))); - } - } - GlobalAlloc::Memory(alloc) => { - trace!("collecting {:?} with {:#?}", alloc_id, alloc); - for &prov in alloc.inner().provenance().ptrs().values() { - rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_alloc(tcx, prov.alloc_id(), output); - }); - } - } - GlobalAlloc::Function(fn_instance) => { - if should_codegen_locally(tcx, &fn_instance) { - trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); - output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); - } - } - GlobalAlloc::VTable(ty, trait_ref) => { - let alloc_id = tcx.vtable_allocation((ty, trait_ref)); - collect_alloc(tcx, alloc_id, output) - } - } -} +//=----------------------------------------------------------------------------- +// Top-level entry point, tying it all together +//=----------------------------------------------------------------------------- -fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { - for impl_def_id in tcx.inherent_impls(def_id).ok()? { - if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( - tcx, - fn_ident, - AssocKind::Fn, - def_id, - ) { - return Some(new.def_id); - } - } - return None; -} +#[instrument(skip(tcx, strategy), level = "debug")] +pub fn collect_crate_mono_items( + tcx: TyCtxt<'_>, + strategy: MonoItemCollectionStrategy, +) -> (FxHashSet>, UsageMap<'_>) { + let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); -fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec { - let fns = [ - (tcx.lang_items().owned_box(), "new"), - (tcx.get_diagnostic_item(sym::Rc), "new"), - (tcx.get_diagnostic_item(sym::Arc), "new"), - ]; - fns.into_iter() - .filter_map(|(def_id, fn_name)| { - def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) - }) - .collect::>() -} + let roots = tcx + .sess + .time("monomorphization_collector_root_collections", || collect_roots(tcx, strategy)); -/// Scans the MIR in order to find function calls, closures, and drop-glue. -#[instrument(skip(tcx, output), level = "debug")] -fn collect_used_items<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - output: &mut MonoItems<'tcx>, -) { - let body = tcx.instance_mir(instance.def); + debug!("building mono item graph, beginning at roots"); - // Here we rely on the visitor also visiting `required_consts`, so that we evaluate them - // and abort compilation if any of them errors. - MirUsedCollector { - tcx, - body: body, - output, - instance, - move_size_spans: vec![], - visiting_call_terminator: false, - skip_move_check_fns: None, - } - .visit_body(body); -} + let mut state = SharedState { + visited: MTLock::new(FxHashSet::default()), + mentioned: MTLock::new(FxHashSet::default()), + usage_map: MTLock::new(UsageMap::new()), + }; + let recursion_limit = tcx.recursion_limit(); -#[instrument(skip(tcx, output), level = "debug")] -fn collect_const_value<'tcx>( - tcx: TyCtxt<'tcx>, - value: mir::ConstValue<'tcx>, - output: &mut MonoItems<'tcx>, -) { - match value { - mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => { - collect_alloc(tcx, ptr.provenance.alloc_id(), output) - } - mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output), - mir::ConstValue::Slice { data, meta: _ } => { - for &prov in data.inner().provenance().ptrs().values() { - collect_alloc(tcx, prov.alloc_id(), output); - } - } - _ => {} + { + let state: LRef<'_, _> = &mut state; + + tcx.sess.time("monomorphization_collector_graph_walk", || { + par_for_each_in(roots, |root| { + let mut recursion_depths = DefIdMap::default(); + collect_items_rec( + tcx, + dummy_spanned(root), + state, + &mut recursion_depths, + recursion_limit, + CollectionMode::UsedItems, + ); + }); + }); } + + (state.visited.into_inner(), state.usage_map.into_inner()) } diff --git a/compiler/rustc_monomorphize/src/collector/move_check.rs b/compiler/rustc_monomorphize/src/collector/move_check.rs new file mode 100644 index 0000000000000..4cc7275ab8069 --- /dev/null +++ b/compiler/rustc_monomorphize/src/collector/move_check.rs @@ -0,0 +1,155 @@ +use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; + +use super::*; +use crate::errors::LargeAssignmentsLint; + +pub(super) struct MoveCheckState { + /// Spans for move size lints already emitted. Helps avoid duplicate lints. + move_size_spans: Vec, + /// Set of functions for which it is OK to move large data into. + skip_move_check_fns: Option>, +} + +impl MoveCheckState { + pub(super) fn new() -> Self { + MoveCheckState { move_size_spans: vec![], skip_move_check_fns: None } + } +} + +impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { + pub(super) fn check_operand_move_size( + &mut self, + operand: &mir::Operand<'tcx>, + location: Location, + ) { + let limit = self.tcx.move_size_limit(); + if limit.0 == 0 { + return; + } + + // This function is called by visit_operand() which visits _all_ + // operands, including TerminatorKind::Call operands. But if + // check_fn_args_move_size() has been called, the operands have already + // been visited. Do not visit them again. + if self.visiting_call_terminator { + return; + } + + let source_info = self.body.source_info(location); + debug!(?source_info); + + if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) { + self.lint_large_assignment(limit.0, too_large_size, location, source_info.span); + }; + } + + pub(super) fn check_fn_args_move_size( + &mut self, + callee_ty: Ty<'tcx>, + args: &[Spanned>], + fn_span: Span, + location: Location, + ) { + let limit = self.tcx.move_size_limit(); + if limit.0 == 0 { + return; + } + + if args.is_empty() { + return; + } + + // Allow large moves into container types that themselves are cheap to move + let ty::FnDef(def_id, _) = *callee_ty.kind() else { + return; + }; + if self + .move_check + .skip_move_check_fns + .get_or_insert_with(|| build_skip_move_check_fns(self.tcx)) + .contains(&def_id) + { + return; + } + + debug!(?def_id, ?fn_span); + + for arg in args { + // Moving args into functions is typically implemented with pointer + // passing at the llvm-ir level and not by memcpy's. So always allow + // moving args into functions. + let operand: &mir::Operand<'tcx> = &arg.node; + if let mir::Operand::Move(_) = operand { + continue; + } + + if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) { + self.lint_large_assignment(limit.0, too_large_size, location, arg.span); + }; + } + } + + fn operand_size_if_too_large( + &mut self, + limit: Limit, + operand: &mir::Operand<'tcx>, + ) -> Option { + let ty = operand.ty(self.body, self.tcx); + let ty = self.monomorphize(ty); + let Ok(layout) = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) else { + return None; + }; + if layout.size.bytes_usize() > limit.0 { + debug!(?layout); + Some(layout.size) + } else { + None + } + } + + fn lint_large_assignment( + &mut self, + limit: usize, + too_large_size: Size, + location: Location, + span: Span, + ) { + let source_info = self.body.source_info(location); + debug!(?source_info); + for reported_span in &self.move_check.move_size_spans { + if reported_span.overlaps(span) { + return; + } + } + let lint_root = source_info.scope.lint_root(&self.body.source_scopes); + debug!(?lint_root); + let Some(lint_root) = lint_root else { + // This happens when the issue is in a function from a foreign crate that + // we monomorphized in the current crate. We can't get a `HirId` for things + // in other crates. + // FIXME: Find out where to report the lint on. Maybe simply crate-level lint root + // but correct span? This would make the lint at least accept crate-level lint attributes. + return; + }; + self.tcx.emit_node_span_lint( + LARGE_ASSIGNMENTS, + lint_root, + span, + LargeAssignmentsLint { span, size: too_large_size.bytes(), limit: limit as u64 }, + ); + self.move_check.move_size_spans.push(span); + } +} + +fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec { + let fns = [ + (tcx.lang_items().owned_box(), "new"), + (tcx.get_diagnostic_item(sym::Rc), "new"), + (tcx.get_diagnostic_item(sym::Arc), "new"), + ]; + fns.into_iter() + .filter_map(|(def_id, fn_name)| { + def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) + }) + .collect::>() +} diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 7f36ae91f1a03..cbab200618610 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -4,14 +4,17 @@ #[macro_use] extern crate tracing; -#[macro_use] -extern crate rustc_middle; use rustc_hir::lang_items::LangItem; +use rustc_middle::bug; use rustc_middle::query::{Providers, TyCtxtAt}; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; +use rustc_middle::ty::Instance; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; +use rustc_span::def_id::DefId; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::ErrorGuaranteed; mod collector; @@ -20,6 +23,8 @@ mod partitioning; mod polymorphize; mod util; +use collector::should_codegen_locally; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } fn custom_coerce_unsize_info<'tcx>( @@ -45,6 +50,34 @@ fn custom_coerce_unsize_info<'tcx>( } } +/// Returns whether a call from the current crate to the [`Instance`] would produce a call +/// from `compiler_builtins` to a symbol the linker must resolve. +/// +/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some +/// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is +/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any +/// unlinkable calls. +/// +/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. +pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> bool { + fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name { + name.as_str().starts_with("llvm.") + } else { + false + } + } + + let def_id = instance.def_id(); + !def_id.is_local() + && tcx.is_compiler_builtins(LOCAL_CRATE) + && !is_llvm_intrinsic(tcx, def_id) + && !should_codegen_locally(tcx, instance) +} + pub fn provide(providers: &mut Providers) { partitioning::provide(providers); polymorphize::provide(providers); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 15041b9cd4181..8f7b4c6472c90 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -103,6 +103,7 @@ use rustc_data_structures::sync; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE}; use rustc_hir::definitions::DefPathDataName; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_middle::mir::mono::{ @@ -117,7 +118,7 @@ use rustc_session::CodegenUnits; use rustc_span::symbol::Symbol; use crate::collector::UsageMap; -use crate::collector::{self, MonoItemCollectionMode}; +use crate::collector::{self, MonoItemCollectionStrategy}; use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode}; struct PartitioningCx<'a, 'tcx> { @@ -625,7 +626,8 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceDef::Virtual(..) | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) => return None, + | ty::InstanceDef::FnPtrAddrShim(..) + | ty::InstanceDef::AsyncDropGlueCtorShim(..) => return None, }; // If this is a method, we want to put it into the same module as @@ -769,7 +771,9 @@ fn mono_item_visibility<'tcx>( }; let def_id = match instance.def { - InstanceDef::Item(def_id) | InstanceDef::DropGlue(def_id, Some(_)) => def_id, + InstanceDef::Item(def_id) + | InstanceDef::DropGlue(def_id, Some(_)) + | InstanceDef::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id, // We match the visibility of statics here InstanceDef::ThreadLocalShim(def_id) => { @@ -786,6 +790,7 @@ fn mono_item_visibility<'tcx>( | InstanceDef::ConstructCoroutineInClosureShim { .. } | InstanceDef::CoroutineKindShim { .. } | InstanceDef::DropGlue(..) + | InstanceDef::AsyncDropGlueCtorShim(..) | InstanceDef::CloneShim(..) | InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden, }; @@ -1087,30 +1092,30 @@ where } fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) { - let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items { + let collection_strategy = match tcx.sess.opts.unstable_opts.print_mono_items { Some(ref s) => { let mode = s.to_lowercase(); let mode = mode.trim(); if mode == "eager" { - MonoItemCollectionMode::Eager + MonoItemCollectionStrategy::Eager } else { if mode != "lazy" { tcx.dcx().emit_warn(UnknownCguCollectionMode { mode }); } - MonoItemCollectionMode::Lazy + MonoItemCollectionStrategy::Lazy } } None => { if tcx.sess.link_dead_code() { - MonoItemCollectionMode::Eager + MonoItemCollectionStrategy::Eager } else { - MonoItemCollectionMode::Lazy + MonoItemCollectionStrategy::Lazy } } }; - let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_mode); + let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy); // If there was an error during collection (e.g. from one of the constants we evaluated), // then we stop here. This way codegen does not have to worry about failing constants. diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 95b3006666202..6600b92fb3182 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -69,7 +69,8 @@ impl<'a, Infcx: InferCtxtLike, I: Interner> Canonicalizer<'a, Infc let (max_universe, variables) = canonicalizer.finalize(); - Canonical { max_universe, variables, value } + let defining_opaque_types = infcx.defining_opaque_types(); + Canonical { defining_opaque_types, max_universe, variables, value } } fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) { @@ -239,7 +240,7 @@ impl, I: Interner> TypeFolder // FIXME: We should investigate the perf implications of not uniquifying // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased => match self.canonicalize_mode { + ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, @@ -277,7 +278,6 @@ impl, I: Interner> TypeFolder } } } - ty::ReError(_) => return r, }; let existing_bound_var = match self.canonicalize_mode { @@ -297,10 +297,7 @@ impl, I: Interner> TypeFolder Region::new_anon_bound(self.interner(), self.binder_index, var) } - fn fold_ty(&mut self, t: I::Ty) -> I::Ty - where - I::Ty: TypeSuperFoldable, - { + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { @@ -351,8 +348,9 @@ impl, I: Interner> TypeFolder | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) + | ty::Pat(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) | ty::Dynamic(_, _, _) @@ -379,47 +377,48 @@ impl, I: Interner> TypeFolder Ty::new_anon_bound(self.interner(), self.binder_index, var) } - fn fold_const(&mut self, c: I::Const) -> I::Const - where - I::Const: TypeSuperFoldable, - { + fn fold_const(&mut self, c: I::Const) -> I::Const { + // We could canonicalize all consts with static types, but the only ones we + // *really* need to worry about are the ones that we end up putting into `CanonicalVarKind` + // since canonical vars can't reference other canonical vars. + let ty = c + .ty() + .fold_with(&mut RegionsToStatic { interner: self.interner(), binder: ty::INNERMOST }); let kind = match c.kind() { - ty::ConstKind::Infer(i) => { - // FIXME: we should fold the ty too eventually - match i { - ty::InferConst::Var(vid) => { - assert_eq!( - self.infcx.root_ct_var(vid), - vid, - "region vid should have been resolved fully before canonicalization" - ); - assert_eq!( - self.infcx.probe_ct_var(vid), - None, - "region vid should have been resolved fully before canonicalization" - ); - CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), c.ty()) - } - ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect, - ty::InferConst::Fresh(_) => todo!(), + ty::ConstKind::Infer(i) => match i { + ty::InferConst::Var(vid) => { + assert_eq!( + self.infcx.root_ct_var(vid), + vid, + "region vid should have been resolved fully before canonicalization" + ); + assert_eq!( + self.infcx.probe_ct_var(vid), + None, + "region vid should have been resolved fully before canonicalization" + ); + CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), ty) } - } + ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect, + ty::InferConst::Fresh(_) => todo!(), + }, ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( PlaceholderLike::new(placeholder.universe(), self.variables.len().into()), - c.ty(), + ty, ), CanonicalizeMode::Response { .. } => { - CanonicalVarKind::PlaceholderConst(placeholder, c.ty()) + CanonicalVarKind::PlaceholderConst(placeholder, ty) } }, ty::ConstKind::Param(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()), - c.ty(), + ty, ), CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"), }, + // FIXME: See comment above -- we could fold the region separately or something. ty::ConstKind::Bound(_, _) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Value(_) @@ -436,6 +435,35 @@ impl, I: Interner> TypeFolder }), ); - Const::new_anon_bound(self.interner(), self.binder_index, var, c.ty()) + Const::new_anon_bound(self.interner(), self.binder_index, var, ty) + } +} + +struct RegionsToStatic { + interner: I, + binder: ty::DebruijnIndex, +} + +impl TypeFolder for RegionsToStatic { + fn interner(&self) -> I { + self.interner + } + + fn fold_binder(&mut self, t: I::Binder) -> I::Binder + where + T: TypeFoldable, + I::Binder: TypeSuperFoldable, + { + self.binder.shift_in(1); + let t = t.fold_with(self); + self.binder.shift_out(1); + t + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + match r.kind() { + ty::ReBound(db, _) if self.binder > db => r, + _ => Region::new_static(self.interner()), + } } } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index a100e2d47bbbb..873095dca8722 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -14,10 +14,6 @@ parse_array_index_offset_of = array indexing not supported in offset_of parse_assignment_else_not_allowed = ... else {"{"} ... {"}"} is not allowed -parse_assoc_lifetime = associated lifetimes are not supported - .label = the lifetime is given here - .help = if you meant to specify a trait object, write `dyn Trait + 'lifetime` - parse_associated_static_item_not_allowed = associated `static` items are not allowed parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or later @@ -234,6 +230,10 @@ parse_expected_struct_field = expected one of `,`, `:`, or `{"}"}`, found `{$tok parse_expected_trait_in_trait_impl_found_type = expected a trait, found type +parse_expr_rarrow_call = `->` used for field access or method call + .suggestion = try using `.` instead + .help = the `.` operator will dereference the value if needed + parse_extern_crate_name_with_dashes = crate name using dashes are not valid in `extern crate` statements .label = dash-separated idents are not valid .suggestion = if the original crate name uses dashes you need to use underscores in the code @@ -390,8 +390,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else` parse_invalid_identifier_with_leading_number = identifiers cannot start with a number -parse_invalid_interpolated_expression = invalid interpolated expression - parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid .label = invalid suffix `{$suffix}` .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases @@ -447,6 +445,12 @@ parse_lifetime_in_borrow_expression = borrow expressions cannot be annotated wit .suggestion = remove the lifetime annotation .label = annotated with lifetime here +parse_lifetime_in_eq_constraint = lifetimes are not permitted in this context + .label = lifetime is not allowed here + .context_label = this introduces an associated item binding + .help = if you meant to specify a trait object, write `dyn /* Trait */ + {$lifetime}` + .colon_sugg = you might have meant to write a bound here + parse_lone_slash = invalid trailing slash in literal .label = {parse_lone_slash} @@ -570,7 +574,7 @@ parse_more_than_one_char = character literal may only contain one codepoint .remove_non = consider removing the non-printing characters .use_double_quotes = if you meant to write a {$is_byte -> [true] byte string - *[false] `str` + *[false] string } literal, use double quotes parse_multiple_skipped_lines = multiple lines skipped by escaped newline @@ -620,6 +624,8 @@ parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allo parse_out_of_range_hex_escape = out of range hex escape .label = must be a character in the range [\x00-\x7f] +parse_outer_attr_ambiguous = ambiguous outer attributes + parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them parse_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches @@ -655,9 +661,6 @@ parse_question_mark_in_type = invalid `?` in type parse_recover_import_as_use = expected item, found {$token_name} .suggestion = items are imported using the `use` keyword -parse_ref_mut_order_incorrect = the order of `mut` and `ref` is incorrect - .suggestion = try switching the order - parse_remove_let = expected pattern, found `let` .suggestion = remove the unnecessary `let` keyword @@ -835,6 +838,7 @@ parse_unknown_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .note = prefixed identifiers and literals are reserved since Rust 2021 .suggestion_br = use `br` for a raw byte string + .suggestion_str = if you meant to write a string literal, use double quotes .suggestion_whitespace = consider inserting whitespace here parse_unknown_start_of_token = unknown start of token: {$escaped} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 32b56bb7e877e..d06f03a7c1767 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -495,6 +495,15 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse { pub attributes: Span, } +#[derive(Diagnostic)] +#[diag(parse_outer_attr_ambiguous)] +pub(crate) struct AmbiguousOuterAttributes { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: WrapInParentheses, +} + #[derive(Diagnostic)] #[diag(parse_missing_in_in_for_loop)] pub(crate) struct MissingInInForLoop { @@ -850,13 +859,6 @@ pub(crate) struct StructLiteralNotAllowedHereSugg { pub right: Span, } -#[derive(Diagnostic)] -#[diag(parse_invalid_interpolated_expression)] -pub(crate) struct InvalidInterpolatedExpression { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(parse_invalid_literal_suffix_on_tuple_index)] pub(crate) struct InvalidLiteralSuffixOnTupleIndex { @@ -1470,7 +1472,7 @@ impl Subdiagnostic for FnTraitMissingParen { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _: F, + _: &F, ) { diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren); let applicability = if self.machine_applicable { @@ -1994,6 +1996,17 @@ pub enum UnknownPrefixSugg { style = "verbose" )] Whitespace(#[primary_span] Span), + #[multipart_suggestion( + parse_suggestion_str, + applicability = "maybe-incorrect", + style = "verbose" + )] + MeantStr { + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + }, } #[derive(Diagnostic)] @@ -2205,12 +2218,21 @@ pub enum MoreThanOneCharSugg { ch: String, }, #[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")] - Quotes { + QuotesFull { #[primary_span] span: Span, is_byte: bool, sugg: String, }, + #[multipart_suggestion(parse_use_double_quotes, applicability = "machine-applicable")] + Quotes { + #[suggestion_part(code = "{prefix}\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + is_byte: bool, + prefix: &'static str, + }, } #[derive(Subdiagnostic)] @@ -2351,14 +2373,6 @@ pub(crate) struct UnexpectedLifetimeInPattern { pub symbol: Symbol, } -#[derive(Diagnostic)] -#[diag(parse_ref_mut_order_incorrect)] -pub(crate) struct RefMutOrderIncorrect { - #[primary_span] - #[suggestion(code = "ref mut", applicability = "machine-applicable")] - pub span: Span, -} - #[derive(Diagnostic)] pub(crate) enum InvalidMutInPattern { #[diag(parse_mut_on_nested_ident_pattern)] @@ -2618,13 +2632,22 @@ pub(crate) struct GenericsInPath { } #[derive(Diagnostic)] -#[diag(parse_assoc_lifetime)] +#[diag(parse_lifetime_in_eq_constraint)] #[help] -pub(crate) struct AssocLifetime { +pub(crate) struct LifetimeInEqConstraint { #[primary_span] - pub span: Span, #[label] - pub lifetime: Span, + pub span: Span, + pub lifetime: Ident, + #[label(parse_context_label)] + pub binding_label: Span, + #[suggestion( + parse_colon_sugg, + style = "verbose", + applicability = "maybe-incorrect", + code = ": " + )] + pub colon_sugg: Span, } #[derive(Diagnostic)] @@ -2974,3 +2997,12 @@ pub(crate) struct AsyncImpl { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_expr_rarrow_call)] +#[help] +pub(crate) struct ExprRArrowCall { + #[primary_span] + #[suggestion(style = "short", applicability = "machine-applicable", code = ".")] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f57945a52df37..1abb1d29562d9 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -30,12 +30,11 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char}; // // This assertion is in this crate, rather than in `rustc_lexer`, because that // crate cannot depend on `rustc_data_structures`. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12); #[derive(Clone, Debug)] -pub struct UnmatchedDelim { - pub expected_delim: Delimiter, +pub(crate) struct UnmatchedDelim { pub found_delim: Option, pub found_span: Span, pub unclosed_span: Option, @@ -63,6 +62,7 @@ pub(crate) fn parse_token_trees<'psess, 'src>( cursor, override_span, nbsp_is_whitespace: false, + last_lifetime: None, }; let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); @@ -105,6 +105,10 @@ struct StringReader<'psess, 'src> { /// in this file, it's safe to treat further occurrences of the non-breaking /// space character as whitespace. nbsp_is_whitespace: bool, + + /// Track the `Span` for the leading `'` of the last lifetime. Used for + /// diagnostics to detect possible typo where `"` was meant. + last_lifetime: Option, } impl<'psess, 'src> StringReader<'psess, 'src> { @@ -130,6 +134,18 @@ impl<'psess, 'src> StringReader<'psess, 'src> { debug!("next_token: {:?}({:?})", token.kind, self.str_from(start)); + if let rustc_lexer::TokenKind::Semi + | rustc_lexer::TokenKind::LineComment { .. } + | rustc_lexer::TokenKind::BlockComment { .. } + | rustc_lexer::TokenKind::CloseParen + | rustc_lexer::TokenKind::CloseBrace + | rustc_lexer::TokenKind::CloseBracket = token.kind + { + // Heuristic: we assume that it is unlikely we're dealing with an unterminated + // string surrounded by single quotes. + self.last_lifetime = None; + } + // Now "cook" the token, converting the simple `rustc_lexer::TokenKind` enum into a // rich `rustc_ast::TokenKind`. This turns strings into interned symbols and runs // additional validation. @@ -188,6 +204,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { self.ident(start) } rustc_lexer::TokenKind::InvalidIdent + | rustc_lexer::TokenKind::InvalidPrefix // Do not recover an identifier with emoji if the codepoint is a confusable // with a recoverable substitution token, like `➖`. if !UNICODE_ARRAY @@ -247,6 +264,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { // expansion purposes. See #12512 for the gory details of why // this is necessary. let lifetime_name = self.str_from(start); + self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); if starts_with_number { let span = self.mk_sp(start, self.pos); self.dcx().struct_err("lifetimes cannot start with a number") @@ -284,7 +302,9 @@ impl<'psess, 'src> StringReader<'psess, 'src> { rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret), rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent), - rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => { + rustc_lexer::TokenKind::Unknown + | rustc_lexer::TokenKind::InvalidIdent + | rustc_lexer::TokenKind::InvalidPrefix => { // Don't emit diagnostics for sequences of the same invalid token if swallow_next_invalid > 0 { swallow_next_invalid -= 1; @@ -395,10 +415,21 @@ impl<'psess, 'src> StringReader<'psess, 'src> { match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { - self.dcx() + let mut err = self + .dcx() .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal") - .with_code(E0762) - .emit() + .with_code(E0762); + if let Some(lt_sp) = self.last_lifetime { + err.multipart_suggestion( + "if you meant to write a string literal, use double quotes", + vec![ + (lt_sp, "\"".to_string()), + (self.mk_sp(start, start + BytePos(1)), "\"".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + err.emit() } self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' ' } @@ -673,7 +704,19 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) } else if expn_data.is_root() { - Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + 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)) + { + // 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 }; diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index a506f98bf3a53..b5a5a2a90ee16 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -77,7 +77,6 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { for &(_, sp) in &self.diag_info.open_braces { err.span_label(sp, "unclosed delimiter"); self.diag_info.unmatched_delims.push(UnmatchedDelim { - expected_delim: Delimiter::Brace, found_delim: None, found_span: self.token.span, unclosed_span: Some(sp), @@ -163,9 +162,8 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { candidate = Some(*brace_span); } } - let (tok, _) = self.diag_info.open_braces.pop().unwrap(); + let (_, _) = self.diag_info.open_braces.pop().unwrap(); self.diag_info.unmatched_delims.push(UnmatchedDelim { - expected_delim: tok, found_delim: Some(close_delim), found_span: self.token.span, unclosed_span: unclosed_delimiter, diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 3ebad6a9fd73e..fa242a32a1814 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -95,11 +95,21 @@ pub(crate) fn emit_unescape_error( } escaped.push(c); } - let sugg = format!("{prefix}\"{escaped}\""); - MoreThanOneCharSugg::Quotes { - span: full_lit_span, - is_byte: mode == Mode::Byte, - sugg, + if escaped.len() != lit.len() || full_lit_span.is_empty() { + let sugg = format!("{prefix}\"{escaped}\""); + MoreThanOneCharSugg::QuotesFull { + span: full_lit_span, + is_byte: mode == Mode::Byte, + sugg, + } + } else { + MoreThanOneCharSugg::Quotes { + start: full_lit_span + .with_hi(full_lit_span.lo() + BytePos((prefix.len() + 1) as u32)), + end: full_lit_span.with_lo(full_lit_span.hi() - BytePos(1)), + is_byte: mode == Mode::Byte, + prefix, + } } }); dcx.emit_err(UnescapeError::MoreThanOneChar { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index eb9a10f4bdab1..ab5f51eedc307 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -3,11 +3,12 @@ use crate::errors::{ SuffixedLiteralInAttribute, }; use crate::fluent_generated as fluent; +use crate::maybe_whole; use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle}; use rustc_ast as ast; use rustc_ast::attr; -use rustc_ast::token::{self, Delimiter, Nonterminal}; +use rustc_ast::token::{self, Delimiter}; use rustc_errors::{codes::*, Diag, PResult}; use rustc_span::{sym, BytePos, Span}; use thin_vec::ThinVec; @@ -251,25 +252,15 @@ impl<'a> Parser<'a> { /// PATH `=` UNSUFFIXED_LIT /// The delimiters or `=` are still put into the resulting token stream. pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> { - let item = match &self.token.kind { - token::Interpolated(nt) => match &nt.0 { - Nonterminal::NtMeta(item) => Some(item.clone().into_inner()), - _ => None, - }, - _ => None, + maybe_whole!(self, NtMeta, |attr| attr.into_inner()); + + let do_parse = |this: &mut Self| { + let path = this.parse_path(PathStyle::Mod)?; + let args = this.parse_attr_args()?; + Ok(ast::AttrItem { path, args, tokens: None }) }; - Ok(if let Some(item) = item { - self.bump(); - item - } else { - let do_parse = |this: &mut Self| { - let path = this.parse_path(PathStyle::Mod)?; - let args = this.parse_attr_args()?; - Ok(ast::AttrItem { path, args, tokens: None }) - }; - // Attr items don't have attributes - if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) }? - }) + // Attr items don't have attributes + if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) } } /// Parses attributes that appear after the opening of an item. These should @@ -371,22 +362,18 @@ impl<'a> Parser<'a> { /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; /// ``` pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> { - let nt_meta = match &self.token.kind { - token::Interpolated(nt) => match &nt.0 { - token::NtMeta(e) => Some(e.clone()), - _ => None, - }, - _ => None, - }; - - if let Some(item) = nt_meta { - return match item.meta(item.path.span) { + // We can't use `maybe_whole` here because it would bump in the `None` + // case, which we don't want. + if let token::Interpolated(nt) = &self.token.kind + && let token::NtMeta(attr_item) = &nt.0 + { + match attr_item.meta(attr_item.path.span) { Some(meta) => { self.bump(); - Ok(meta) + return Ok(meta); } - None => self.unexpected(), - }; + None => self.unexpected()?, + } } let lo = self.token.span; diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index a1dd7d6f67345..62c8f9f5dacb1 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -454,7 +454,7 @@ fn make_token_stream( } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 7038b8bbe47a0..f256dbf436021 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -29,7 +29,7 @@ use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind}; use rustc_ast::tokenstream::AttrTokenTree; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ - AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingAnnotation, Block, + AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, Generics, HasTokens, Item, ItemKind, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, TyKind, }; @@ -51,7 +51,7 @@ use thin_vec::{thin_vec, ThinVec}; pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param { let pat = P(Pat { id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingAnnotation::NONE, ident, None), + kind: PatKind::Ident(BindingMode::NONE, ident, None), span: ident.span, tokens: None, }); @@ -279,7 +279,7 @@ impl<'a> Parser<'a> { TokenKind::Colon, TokenKind::Comma, TokenKind::Semi, - TokenKind::ModSep, + TokenKind::PathSep, TokenKind::OpenDelim(Delimiter::Brace), TokenKind::OpenDelim(Delimiter::Parenthesis), TokenKind::CloseDelim(Delimiter::Brace), @@ -1169,7 +1169,7 @@ impl<'a> Parser<'a> { return; } - if token::ModSep == self.token.kind && segment.args.is_none() { + if token::PathSep == self.token.kind && segment.args.is_none() { let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); let lo = self.token.span; @@ -1420,7 +1420,7 @@ impl<'a> Parser<'a> { [(token::Lt, 1), (token::Gt, -1), (token::BinOp(token::Shr), -2)]; self.consume_tts(1, &modifiers); - if !&[token::OpenDelim(Delimiter::Parenthesis), token::ModSep] + if !&[token::OpenDelim(Delimiter::Parenthesis), token::PathSep] .contains(&self.token.kind) { // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the @@ -1428,7 +1428,7 @@ impl<'a> Parser<'a> { self.restore_snapshot(snapshot); } } - return if token::ModSep == self.token.kind { + return if token::PathSep == self.token.kind { // We have some certainty that this was a bad turbofish at this point. // `foo< bar >::` if let ExprKind::Binary(o, ..) = inner_op.kind @@ -1784,7 +1784,7 @@ impl<'a> Parser<'a> { } // Do not add `::` to expected tokens. - if self.token == token::ModSep { + if self.token == token::PathSep { if let Some(ty) = base.to_ty() { return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty); } @@ -1799,7 +1799,7 @@ impl<'a> Parser<'a> { ty_span: Span, ty: P, ) -> PResult<'a, P> { - self.expect(&token::ModSep)?; + self.expect(&token::PathSep)?; let mut path = ast::Path { segments: ThinVec::new(), span: DUMMY_SP, tokens: None }; self.parse_path_segments(&mut path.segments, T::PATH_STYLE, None)?; @@ -2787,7 +2787,7 @@ impl<'a> Parser<'a> { } _ => {} }, - PatKind::Ident(BindingAnnotation::NONE, ident, None) => { + PatKind::Ident(BindingMode::NONE, ident, None) => { match &first_pat.kind { PatKind::Ident(_, old_ident, _) => { let path = PatKind::Path( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e27a5f937990e..8ed2a6edf1aec 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -11,12 +11,11 @@ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; use ast::token::IdentIsRaw; -use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment}; +use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment}; use core::mem; use core::ops::ControlFlow; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::Spacing; use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; @@ -328,7 +327,9 @@ impl<'a> Parser<'a> { this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed) })?; - let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); + self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); + let span = lhs_span.to(rhs.span); + lhs = match op { AssocOp::Add | AssocOp::Subtract @@ -427,6 +428,18 @@ impl<'a> Parser<'a> { }); } + fn error_ambiguous_outer_attrs(&self, lhs: &P, lhs_span: Span, rhs_span: Span) { + if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) { + self.dcx().emit_err(errors::AmbiguousOuterAttributes { + span: attr.span.to(rhs_span), + sugg: errors::WrapInParentheses::Expression { + left: attr.span.shrink_to_lo(), + right: lhs_span.shrink_to_hi(), + }, + }); + } + } + /// Possibly translate the current token to an associative operator. /// The method does not advance the current token. /// @@ -507,7 +520,8 @@ impl<'a> Parser<'a> { None }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); + self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span); + let span = lhs.span.to(rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; let range = self.mk_range(Some(lhs), rhs, limits); @@ -723,7 +737,8 @@ impl<'a> Parser<'a> { expr_kind: fn(P, P) -> ExprKind, ) -> PResult<'a, P> { let mk_expr = |this: &mut Self, lhs: P, rhs: P| { - this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs)) + this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span); + this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a @@ -861,6 +876,7 @@ impl<'a> Parser<'a> { ExprKind::MethodCall(_) => "a method call", ExprKind::Call(_, _) => "a function call", ExprKind::Await(_, _) => "`.await`", + ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", ExprKind::Err(_) => return Ok(with_postfix), _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), } @@ -943,7 +959,10 @@ impl<'a> Parser<'a> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure - let res = self.parse_expr_dot_or_call_with_(e0, lo); + let res = ensure_sufficient_stack( + // this expr demonstrates the recursion it guards against + || self.parse_expr_dot_or_call_with_(e0, lo), + ); if attrs.is_empty() { res } else { @@ -976,6 +995,12 @@ impl<'a> Parser<'a> { // we are using noexpect here because we don't expect a `.` directly after a `return` // which could be suggested otherwise self.eat_noexpect(&token::Dot) + } else if self.token.kind == TokenKind::RArrow && self.may_recover() { + // Recovery for `expr->suffix`. + self.bump(); + let span = self.prev_token.span; + self.dcx().emit_err(errors::ExprRArrowCall { span }); + true } else { self.eat(&token::Dot) }; @@ -996,13 +1021,57 @@ impl<'a> Parser<'a> { } pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P) -> PResult<'a, P> { + // At this point we've consumed something like `expr.` and `self.token` holds the token + // after the dot. match self.token.uninterpolate().kind { token::Ident(..) => self.parse_dot_suffix(base, lo), token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { - Ok(self.parse_expr_tuple_field_access(lo, base, symbol, suffix, None)) + let ident_span = self.token.span; + self.bump(); + Ok(self.mk_expr_tuple_field_access(lo, ident_span, base, symbol, suffix)) } token::Literal(token::Lit { kind: token::Float, symbol, suffix }) => { - Ok(self.parse_expr_tuple_field_access_float(lo, base, symbol, suffix)) + Ok(match self.break_up_float(symbol, self.token.span) { + // 1e2 + DestructuredFloat::Single(sym, _sp) => { + // `foo.1e2`: a single complete dot access, fully consumed. We end up with + // the `1e2` token in `self.prev_token` and the following token in + // `self.token`. + let ident_span = self.token.span; + self.bump(); + self.mk_expr_tuple_field_access(lo, ident_span, base, sym, suffix) + } + // 1. + DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { + // `foo.1.`: a single complete dot access and the start of another. + // We end up with the `sym` (`1`) token in `self.prev_token` and a dot in + // `self.token`. + assert!(suffix.is_none()); + self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); + self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); + self.mk_expr_tuple_field_access(lo, ident_span, base, sym, None) + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot( + sym1, + ident1_span, + _dot_span, + sym2, + ident2_span, + ) => { + // `foo.1.2` (or `foo.1.2e3`): two complete dot accesses. We end up with + // the `sym2` (`2` or `2e3`) token in `self.prev_token` and the following + // token in `self.token`. + let next_token2 = + Token::new(token::Ident(sym2, IdentIsRaw::No), ident2_span); + self.bump_with((next_token2, self.token_spacing)); + self.bump(); + let base1 = + self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None); + self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix) + } + DestructuredFloat::Error => base, + }) } _ => { self.error_unexpected_after_dot(); @@ -1116,41 +1185,6 @@ impl<'a> Parser<'a> { } } - fn parse_expr_tuple_field_access_float( - &mut self, - lo: Span, - base: P, - float: Symbol, - suffix: Option, - ) -> P { - match self.break_up_float(float, self.token.span) { - // 1e2 - DestructuredFloat::Single(sym, _sp) => { - self.parse_expr_tuple_field_access(lo, base, sym, suffix, None) - } - // 1. - DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { - assert!(suffix.is_none()); - self.token = Token::new(token::Ident(sym, IdentIsRaw::No), ident_span); - let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); - self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token)) - } - // 1.2 | 1.2e3 - DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) => { - self.token = Token::new(token::Ident(symbol1, IdentIsRaw::No), ident1_span); - // This needs to be `Spacing::Alone` to prevent regressions. - // See issue #76399 and PR #76285 for more details - let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); - let base1 = - self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); - let next_token2 = Token::new(token::Ident(symbol2, IdentIsRaw::No), ident2_span); - self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) - } - DestructuredFloat::Error => base, - } - } - /// Parse the field access used in offset_of, matched by `$(e:expr)+`. /// Currently returns a list of idents. However, it should be possible in /// future to also do array indices, which might be arbitrary expressions. @@ -1252,24 +1286,18 @@ impl<'a> Parser<'a> { Ok(fields.into_iter().collect()) } - fn parse_expr_tuple_field_access( + fn mk_expr_tuple_field_access( &mut self, lo: Span, + ident_span: Span, base: P, field: Symbol, suffix: Option, - next_token: Option<(Token, Spacing)>, ) -> P { - match next_token { - Some(next_token) => self.bump_with(next_token), - None => self.bump(), - } - let span = self.prev_token.span; - let field = ExprKind::Field(base, Ident::new(field, span)); if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(span, suffix); + self.expect_no_tuple_index_suffix(ident_span, suffix); } - self.mk_expr(lo.to(span), field) + self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span))) } /// Parse a function call expression, `expr(...)`. @@ -1376,6 +1404,13 @@ impl<'a> Parser<'a> { return Ok(self.mk_await_expr(self_arg, lo)); } + // Post-fix match + if self.eat_keyword(kw::Match) { + let match_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::postfix_match, match_span); + return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix); + } + let fn_span_lo = self.token.span; let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); @@ -1936,11 +1971,11 @@ impl<'a> Parser<'a> { /// Parse `builtin # ident(args,*)`. fn parse_expr_builtin(&mut self) -> PResult<'a, P> { self.parse_builtin(|this, lo, ident| { - if ident.name == sym::offset_of { - return Ok(Some(this.parse_expr_offset_of(lo)?)); - } - - Ok(None) + Ok(match ident.name { + sym::offset_of => Some(this.parse_expr_offset_of(lo)?), + sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), + _ => None, + }) }) } @@ -1975,6 +2010,7 @@ impl<'a> Parser<'a> { ret } + /// Built-in macro for `offset_of!` expressions. pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P> { let container = self.parse_ty()?; self.expect(&TokenKind::Comma)?; @@ -2004,6 +2040,15 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } + /// Built-in macro for type ascription expressions. + pub(crate) fn parse_expr_type_ascribe(&mut self, lo: Span) -> PResult<'a, P> { + let expr = self.parse_expr()?; + self.expect(&token::Comma)?; + let ty = self.parse_ty()?; + let span = lo.to(self.token.span); + Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) + } + /// Returns a string literal if the next token is a string literal. /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. @@ -2040,16 +2085,6 @@ impl<'a> Parser<'a> { &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { - if let token::Interpolated(nt) = &self.token.kind - && let token::NtExpr(e) | token::NtLiteral(e) = &nt.0 - && matches!(e.kind, ExprKind::Err(_)) - { - let mut err = self - .dcx() - .create_err(errors::InvalidInterpolatedExpression { span: self.token.span }); - err.downgrade_to_delayed_bug(); - return Err(err); - } let token = self.token.clone(); let err = |self_: &Self| { let msg = format!("unexpected token: {}", super::token_descr(&token)); @@ -2756,7 +2791,7 @@ impl<'a> Parser<'a> { }; return if self.token.kind == token::CloseDelim(Delimiter::Parenthesis) { // We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the - // parser state and emit a targetted suggestion. + // parser state and emit a targeted suggestion. let span = vec![start_span, self.token.span]; let right = self.prev_token.span.between(self.look_ahead(1, |t| t.span)); self.bump(); // ) @@ -2891,8 +2926,20 @@ impl<'a> Parser<'a> { /// Parses a `match ... { ... }` expression (`match` token already eaten). fn parse_expr_match(&mut self) -> PResult<'a, P> { let match_span = self.prev_token.span; - let lo = self.prev_token.span; let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + + self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix) + } + + /// Parses the block of a `match expr { ... }` or a `expr.match { ... }` + /// expression. This is after the match token and scrutinee are eaten + fn parse_match_block( + &mut self, + lo: Span, + match_span: Span, + scrutinee: P, + match_kind: MatchKind, + ) -> PResult<'a, P> { if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) { if self.token == token::Semi { e.span_suggestion_short( @@ -2935,7 +2982,7 @@ impl<'a> Parser<'a> { }); return Ok(self.mk_expr_with_attrs( span, - ExprKind::Match(scrutinee, arms), + ExprKind::Match(scrutinee, arms, match_kind), attrs, )); } @@ -2943,7 +2990,7 @@ impl<'a> Parser<'a> { } let hi = self.token.span; self.bump(); - Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)) + Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms, match_kind), attrs)) } /// Attempt to recover from match arm body with statements and no surrounding braces. @@ -3560,8 +3607,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); @@ -3783,16 +3829,6 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Err(guar)) } - /// Create expression span ensuring the span of the parent node - /// is larger than the span of lhs and rhs, including the attributes. - fn mk_expr_sp(&self, lhs: &P, lhs_span: Span, rhs_span: Span) -> Span { - lhs.attrs - .iter() - .find(|a| a.style == AttrStyle::Outer) - .map_or(lhs_span, |a| a.span) - .to(rhs_span) - } - fn collect_tokens_for_expr( &mut self, attrs: AttrWrapper, @@ -3952,7 +3988,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::While(_, _, _) | ExprKind::ForLoop { .. } | ExprKind::Loop(_, _, _) - | ExprKind::Match(_, _) + | ExprKind::Match(_, _, _) | ExprKind::Closure(_) | ExprKind::Block(_, _) | ExprKind::Gen(_, _, _) diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 263b2a9064356..93a15c938ecf1 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -62,7 +62,7 @@ impl<'a> Parser<'a> { let snapshot = self.create_snapshot_for_diagnostic(); match self.parse_ty() { Ok(p) => { - if let TyKind::ImplTrait(_, bounds) = &p.kind { + if let TyKind::ImplTrait(_, bounds, None) = &p.kind { let span = impl_span.to(self.token.span.shrink_to_lo()); let mut err = self.dcx().struct_span_err( span, @@ -481,7 +481,7 @@ impl<'a> Parser<'a> { })) } else { self.maybe_recover_bounds_doubled_colon(&ty)?; - self.unexpected() + self.unexpected_any() } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 79492fe62ed97..848277c4611e3 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -6,6 +6,7 @@ use super::{ }; use crate::errors::{self, MacroExpandsToAdtField}; use crate::fluent_generated as fluent; +use crate::maybe_whole; use ast::token::IdentIsRaw; use rustc_ast::ast::*; use rustc_ast::ptr::P; @@ -115,17 +116,10 @@ impl<'a> Parser<'a> { fn_parse_mode: FnParseMode, force_collect: ForceCollect, ) -> PResult<'a, Option> { - // Don't use `maybe_whole` so that we have precise control - // over when we bump the parser - if let token::Interpolated(nt) = &self.token.kind - && let token::NtItem(item) = &nt.0 - { - let mut item = item.clone(); - self.bump(); - + maybe_whole!(self, NtItem, |item| { attrs.prepend_to_nt_inner(&mut item.attrs); - return Ok(Some(item.into_inner())); - }; + Some(item.into_inner()) + }); let item = self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| { @@ -364,12 +358,12 @@ impl<'a> Parser<'a> { fn is_reuse_path_item(&mut self) -> bool { // no: `reuse ::path` for compatibility reasons with macro invocations self.token.is_keyword(kw::Reuse) - && self.look_ahead(1, |t| t.is_path_start() && t.kind != token::ModSep) + && self.look_ahead(1, |t| t.is_path_start() && t.kind != token::PathSep) } /// Are we sure this could not possibly be a macro invocation? fn isnt_macro_invocation(&mut self) -> bool { - self.check_ident() && self.look_ahead(1, |t| *t != token::Not && *t != token::ModSep) + self.check_ident() && self.look_ahead(1, |t| *t != token::Not && *t != token::PathSep) } /// Recover on encountering a struct or method definition where the user @@ -631,7 +625,7 @@ impl<'a> Parser<'a> { // This notably includes paths passed through `ty` macro fragments (#46438). TyKind::Path(None, path) => path, other => { - if let TyKind::ImplTrait(_, bounds) = other + if let TyKind::ImplTrait(_, bounds, None) = other && let [bound] = bounds.as_slice() { // Suggest removing extra `impl` keyword: @@ -692,6 +686,8 @@ impl<'a> Parser<'a> { (None, self.parse_path(PathStyle::Expr)?) }; + let rename = if self.eat_keyword(kw::As) { Some(self.parse_ident()?) } else { None }; + let body = if self.check(&token::OpenDelim(Delimiter::Brace)) { Some(self.parse_block()?) } else { @@ -701,11 +697,9 @@ impl<'a> Parser<'a> { let span = span.to(self.prev_token.span); self.psess.gated_spans.gate(sym::fn_delegation, span); - let ident = path.segments.last().map(|seg| seg.ident).unwrap_or(Ident::empty()); - Ok(( - ident, - ItemKind::Delegation(Box::new(Delegation { id: DUMMY_NODE_ID, qself, path, body })), - )) + let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident); + let deleg = Delegation { id: DUMMY_NODE_ID, qself, path, rename, body }; + Ok((ident, ItemKind::Delegation(Box::new(deleg)))) } fn parse_item_list( @@ -1026,7 +1020,7 @@ impl<'a> Parser<'a> { { // `use *;` or `use ::*;` or `use {...};` or `use ::{...};` let mod_sep_ctxt = self.token.span.ctxt(); - if self.eat(&token::ModSep) { + if self.eat(&token::PathSep) { prefix .segments .push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); @@ -1037,7 +1031,7 @@ impl<'a> Parser<'a> { // `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;` prefix = self.parse_path(PathStyle::Mod)?; - if self.eat(&token::ModSep) { + if self.eat(&token::PathSep) { self.parse_use_tree_glob_or_nested()? } else { // Recover from using a colon as path separator. @@ -1069,7 +1063,7 @@ impl<'a> Parser<'a> { /// Parses a `UseTreeKind::Nested(list)`. /// /// ```text - /// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`] + /// USE_TREE_LIST = ∅ | (USE_TREE `,`)* USE_TREE [`,`] /// ``` fn parse_use_tree_list(&mut self) -> PResult<'a, ThinVec<(UseTree, ast::NodeId)>> { self.parse_delim_comma_seq(Delimiter::Brace, |p| { @@ -1197,7 +1191,11 @@ impl<'a> Parser<'a> { ident_span: ident.span, const_span, }); - ForeignItemKind::Static(ty, Mutability::Not, expr) + ForeignItemKind::Static(Box::new(StaticForeignItem { + ty, + mutability: Mutability::Not, + expr, + })) } _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), }, @@ -1514,7 +1512,7 @@ impl<'a> Parser<'a> { let ident = this.parse_field_ident("enum", vlo)?; if this.token == token::Not { - if let Err(err) = this.unexpected::<()>() { + if let Err(err) = this.unexpected() { err.with_note(fluent::parse_macro_expands_to_enum_variant).emit(); } @@ -1937,7 +1935,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; if self.token.kind == token::Not { - if let Err(mut err) = self.unexpected::() { + if let Err(mut err) = self.unexpected() { // Encounter the macro invocation err.subdiagnostic(self.dcx(), MacroExpandsToAdtField { adt_ty }); return Err(err); @@ -1974,11 +1972,8 @@ impl<'a> Parser<'a> { } else if matches!(is_raw, IdentIsRaw::No) && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); let err = if self.check_fn_front_matter(false, Case::Sensitive) { - let inherited_vis = Visibility { - span: rustc_span::DUMMY_SP, - kind: VisibilityKind::Inherited, - tokens: None, - }; + let inherited_vis = + Visibility { span: DUMMY_SP, kind: VisibilityKind::Inherited, tokens: None }; // We use `parse_fn` to get a span for the function let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; match self.parse_fn( @@ -2067,7 +2062,7 @@ impl<'a> Parser<'a> { let params = self.parse_token_tree(); // `MacParams` let pspan = params.span(); if !self.check(&token::OpenDelim(Delimiter::Brace)) { - return self.unexpected(); + self.unexpected()?; } let body = self.parse_token_tree(); // `MacBody` // Convert `MacParams MacBody` into `{ MacParams => MacBody }`. @@ -2077,7 +2072,7 @@ impl<'a> Parser<'a> { let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi()); P(DelimArgs { dspan, delim: Delimiter::Brace, tokens }) } else { - return self.unexpected(); + self.unexpected_any()? }; self.psess.gated_spans.gate(sym::decl_macro, lo.to(self.prev_token.span)); @@ -2692,7 +2687,7 @@ impl<'a> Parser<'a> { debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); let (pat, colon) = this.parse_fn_param_pat_colon()?; if !colon { - let mut err = this.unexpected::<()>().unwrap_err(); + let mut err = this.unexpected().unwrap_err(); return if let Some(ident) = this.parameter_without_type(&mut err, pat, is_name_required, first_param) { @@ -2716,12 +2711,12 @@ impl<'a> Parser<'a> { { // This wasn't actually a type, but a pattern looking like a type, // so we are going to rollback and re-parse for recovery. - ty = this.unexpected(); + ty = this.unexpected_any(); } match ty { Ok(ty) => { let ident = Ident::new(kw::Empty, this.prev_token.span); - let bm = BindingAnnotation::NONE; + let bm = BindingMode::NONE; let pat = this.mk_pat_ident(ty.span, bm, ident); (pat, ty) } @@ -2758,7 +2753,7 @@ impl<'a> Parser<'a> { // Is `self` `n` tokens ahead? let is_isolated_self = |this: &Self, n| { this.is_keyword_ahead(n, &[kw::SelfLower]) - && this.look_ahead(n + 1, |t| t != &token::ModSep) + && this.look_ahead(n + 1, |t| t != &token::PathSep) }; // Is `mut self` `n` tokens ahead? let is_isolated_mut_self = diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 12879dc429bc9..7486da33b2173 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -20,16 +20,15 @@ pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; pub use path::PathStyle; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; -use rustc_ast::AttrId; -use rustc_ast::CoroutineKind; -use rustc_ast::DUMMY_NODE_ID; -use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern}; -use rustc_ast::{AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit}; -use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind}; +use rustc_ast::{ + self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs, + Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, StrLit, Unsafe, Visibility, + VisibilityKind, DUMMY_NODE_ID, +}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::PResult; @@ -93,12 +92,13 @@ pub enum TrailingToken { #[macro_export] macro_rules! maybe_whole { ($p:expr, $constructor:ident, |$x:ident| $e:expr) => { - if let token::Interpolated(nt) = &$p.token.kind { - if let token::$constructor(x) = &nt.0 { - let $x = x.clone(); - $p.bump(); - return Ok($e); - } + if let token::Interpolated(nt) = &$p.token.kind + && let token::$constructor(x) = &nt.0 + { + #[allow(unused_mut)] + let mut $x = x.clone(); + $p.bump(); + return Ok($e); } }; } @@ -109,7 +109,7 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath { ($self: expr, $allow_qpath_recovery: expr) => { if $allow_qpath_recovery && $self.may_recover() - && $self.look_ahead(1, |t| t == &token::ModSep) + && $self.look_ahead(1, |t| t == &token::PathSep) && let token::Interpolated(nt) = &$self.token.kind && let token::NtTy(ty) = &nt.0 { @@ -162,7 +162,6 @@ pub struct Parser<'a> { /// /// See the comments in the `parse_path_segment` function for more details. unmatched_angle_bracket_count: u16, - max_angle_bracket_count: u16, angle_bracket_nesting: u16, last_unexpected_token_span: Option, @@ -179,7 +178,7 @@ pub struct Parser<'a> { // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(Parser<'_>, 264); /// Stores span information about a closure. @@ -430,7 +429,6 @@ impl<'a> Parser<'a> { num_bump_calls: 0, break_last_token: false, unmatched_angle_bracket_count: 0, - max_angle_bracket_count: 0, angle_bracket_nesting: 0, last_unexpected_token_span: None, subparser_name, @@ -449,6 +447,7 @@ impl<'a> Parser<'a> { parser } + #[inline] pub fn recovery(mut self, recovery: Recovery) -> Self { self.recovery = recovery; self @@ -461,11 +460,14 @@ impl<'a> Parser<'a> { /// /// Technically, this only needs to restrict eager recovery by doing lookahead at more tokens. /// But making the distinction is very subtle, and simply forbidding all recovery is a lot simpler to uphold. + #[inline] fn may_recover(&self) -> bool { matches!(self.recovery, Recovery::Allowed) } - pub fn unexpected(&mut self) -> PResult<'a, T> { + /// Version of [`unexpected`](Parser::unexpected) that "returns" any type in the `Ok` + /// (both those functions never return "Ok", and so can lie like that in the type). + pub fn unexpected_any(&mut self) -> PResult<'a, T> { match self.expect_one_of(&[], &[]) { Err(e) => Err(e), // We can get `Ok(true)` from `recover_closing_delimiter` @@ -474,6 +476,10 @@ impl<'a> Parser<'a> { } } + pub fn unexpected(&mut self) -> PResult<'a, ()> { + self.unexpected_any() + } + /// Expects and consumes the token `t`. Signals an error if the next token is not `t`. pub fn expect(&mut self, t: &TokenKind) -> PResult<'a, Recovered> { if self.expected_tokens.is_empty() { @@ -542,6 +548,7 @@ impl<'a> Parser<'a> { /// /// This method will automatically add `tok` to `expected_tokens` if `tok` is not /// encountered. + #[inline] fn check(&mut self, tok: &TokenKind) -> bool { let is_present = self.token == *tok; if !is_present { @@ -550,6 +557,7 @@ impl<'a> Parser<'a> { is_present } + #[inline] fn check_noexpect(&self, tok: &TokenKind) -> bool { self.token == *tok } @@ -558,6 +566,7 @@ impl<'a> Parser<'a> { /// /// the main purpose of this function is to reduce the cluttering of the suggestions list /// which using the normal eat method could introduce in some cases. + #[inline] pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { let is_present = self.check_noexpect(tok); if is_present { @@ -567,6 +576,7 @@ impl<'a> Parser<'a> { } /// Consumes a token 'tok' if it exists. Returns whether the given token was present. + #[inline] pub fn eat(&mut self, tok: &TokenKind) -> bool { let is_present = self.check(tok); if is_present { @@ -577,11 +587,13 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, returns `true` without eating it. /// An expectation is also added for diagnostics purposes. + #[inline] fn check_keyword(&mut self, kw: Symbol) -> bool { self.expected_tokens.push(TokenType::Keyword(kw)); self.token.is_keyword(kw) } + #[inline] fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.check_keyword(kw) { return true; @@ -600,6 +612,7 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, eats it and returns `true`. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustfmt usage. + #[inline] pub fn eat_keyword(&mut self, kw: Symbol) -> bool { if self.check_keyword(kw) { self.bump(); @@ -612,6 +625,7 @@ impl<'a> Parser<'a> { /// Eats a keyword, optionally ignoring the case. /// If the case differs (and is ignored) an error is issued. /// This is useful for recovery. + #[inline] fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.eat_keyword(kw) { return true; @@ -629,6 +643,7 @@ impl<'a> Parser<'a> { false } + #[inline] fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); @@ -650,6 +665,7 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) } + #[inline] fn check_or_expected(&mut self, ok: bool, typ: TokenType) -> bool { if ok { true @@ -697,6 +713,7 @@ impl<'a> Parser<'a> { /// Checks to see if the next token is either `+` or `+=`. /// Otherwise returns `false`. + #[inline] fn check_plus(&mut self) -> bool { self.check_or_expected( self.token.is_like_plus(), @@ -759,7 +776,6 @@ impl<'a> Parser<'a> { if ate { // See doc comment for `unmatched_angle_bracket_count`. self.unmatched_angle_bracket_count += 1; - self.max_angle_bracket_count += 1; debug!("eat_lt: (increment) count={:?}", self.unmatched_angle_bracket_count); } ate @@ -879,6 +895,7 @@ impl<'a> Parser<'a> { } // Attempt to keep parsing if it was an omitted separator. + self.last_unexpected_token_span = None; match f(self) { Ok(t) => { // Parsed successfully, therefore most probably the code only @@ -1235,8 +1252,6 @@ impl<'a> Parser<'a> { fn parse_const_block(&mut self, span: Span, pat: bool) -> PResult<'a, P> { if pat { self.psess.gated_spans.gate(sym::inline_const_pat, span); - } else { - self.psess.gated_spans.gate(sym::inline_const, span); } self.eat_keyword(kw::Const); let (attrs, blk) = self.parse_inner_attrs_and_block()?; @@ -1253,6 +1268,11 @@ impl<'a> Parser<'a> { if self.eat_keyword(kw::Mut) { Mutability::Mut } else { Mutability::Not } } + /// Parses reference binding mode (`ref`, `ref mut`, or nothing). + fn parse_byref(&mut self) -> ByRef { + if self.eat_keyword(kw::Ref) { ByRef::Yes(self.parse_mutability()) } else { ByRef::No } + } + /// Possibly parses mutability (`const` or `mut`). fn parse_const_or_mut(&mut self) -> Option { if self.eat_keyword(kw::Mut) { @@ -1278,7 +1298,11 @@ impl<'a> Parser<'a> { } fn parse_delim_args(&mut self) -> PResult<'a, P> { - if let Some(args) = self.parse_delim_args_inner() { Ok(P(args)) } else { self.unexpected() } + if let Some(args) = self.parse_delim_args_inner() { + Ok(P(args)) + } else { + self.unexpected_any() + } } fn parse_attr_args(&mut self) -> PResult<'a, AttrArgs> { @@ -1384,7 +1408,7 @@ impl<'a> Parser<'a> { /// so emit a proper diagnostic. // Public for rustfmt usage. pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> { - maybe_whole!(self, NtVis, |x| x.into_inner()); + maybe_whole!(self, NtVis, |vis| vis.into_inner()); if !self.eat_keyword(kw::Pub) { // We need a span for our `Spanned`, but there's inherently no @@ -1504,7 +1528,7 @@ impl<'a> Parser<'a> { /// `::{` or `::*` fn is_import_coupler(&mut self) -> bool { - self.check(&token::ModSep) + self.check(&token::PathSep) && self.look_ahead(1, |t| { *t == token::OpenDelim(Delimiter::Brace) || *t == token::BinOp(token::Star) }) @@ -1561,8 +1585,21 @@ pub enum FlatToken { Empty, } -#[derive(Debug)] -pub enum ParseNtResult { - Nt(Nonterminal), +// Metavar captures of various kinds. +#[derive(Clone, Debug)] +pub enum ParseNtResult { Tt(TokenTree), + Nt(NtType), +} + +impl ParseNtResult { + pub fn map_nt(self, mut f: F) -> ParseNtResult + where + F: FnMut(T) -> U, + { + match self { + ParseNtResult::Tt(tt) => ParseNtResult::Tt(tt), + ParseNtResult::Nt(nt) => ParseNtResult::Nt(f(nt)), + } + } } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index f1572a18a8be0..73b17353ac90c 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,5 +1,5 @@ use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token}; +use rustc_ast::token::{self, Delimiter, Nonterminal, Nonterminal::*, NonterminalKind, Token}; use rustc_ast::HasTokens; use rustc_ast_pretty::pprust; use rustc_errors::PResult; @@ -62,30 +62,28 @@ impl<'a> Parser<'a> { _ => false, }, NonterminalKind::Path | NonterminalKind::Meta => match &token.kind { - token::ModSep | token::Ident(..) => true, + token::PathSep | token::Ident(..) => true, token::Interpolated(nt) => may_be_ident(&nt.0), _ => false, }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { - match &token.kind { + NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => match &token.kind { token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern token::OpenDelim(Delimiter::Bracket) | // slice pattern token::BinOp(token::And) | // reference token::BinOp(token::Minus) | // negative literal token::AndAnd | // double reference - token::Literal(_) | // literal + token::Literal(_) | // literal token::DotDot | // range pattern (future compat) token::DotDotDot | // range pattern (future compat) - token::ModSep | // path + token::PathSep | // path token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) // leading vert `|` or-pattern token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr), token::Interpolated(nt) => may_be_ident(&nt.0), _ => false, - } - } + }, NonterminalKind::Lifetime => match &token.kind { token::Lifetime(_) => true, token::Interpolated(nt) => { @@ -102,7 +100,10 @@ impl<'a> Parser<'a> { /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call /// site. #[inline] - pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> { + pub fn parse_nonterminal( + &mut self, + kind: NonterminalKind, + ) -> PResult<'a, ParseNtResult> { // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, // which requires having captured tokens available. Since we cannot determine // in advance whether or not a proc-macro will be (transitively) invoked, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 88ae1b5420fe7..78d3d019bf4d2 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -4,11 +4,11 @@ use crate::errors::{ DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, - PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, - SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, - UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, - UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, + PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, + TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, + UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, + UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, + UnexpectedVertVertInPattern, }; use crate::parser::expr::could_be_unclosed_char_literal; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -16,8 +16,8 @@ use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, Delimiter, Token}; use rustc_ast::{ - self as ast, AttrVec, BindingAnnotation, ByRef, Expr, ExprKind, MacCall, Mutability, Pat, - PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, + self as ast, AttrVec, BindingMode, ByRef, Expr, ExprKind, MacCall, Mutability, Pat, PatField, + PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, PResult}; @@ -435,7 +435,7 @@ impl<'a> Parser<'a> { syntax_loc: Option, ) -> PResult<'a, P> { maybe_recover_from_interpolated_ty_qpath!(self, true); - maybe_whole!(self, NtPat, |x| x); + maybe_whole!(self, NtPat, |pat| pat); let mut lo = self.token.span; @@ -476,7 +476,7 @@ impl<'a> Parser<'a> { // Parse `_` PatKind::Wild } else if self.eat_keyword(kw::Mut) { - self.parse_pat_ident_mut(syntax_loc)? + self.parse_pat_ident_mut()? } else if self.eat_keyword(kw::Ref) { if self.check_keyword(kw::Box) { // Suggest `box ref`. @@ -486,7 +486,7 @@ impl<'a> Parser<'a> { } // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); - self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)? + self.parse_pat_ident(BindingMode(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)? } else if self.eat_keyword(kw::Box) { self.parse_pat_box()? } else if self.check_inline_const(0) { @@ -498,17 +498,20 @@ impl<'a> Parser<'a> { } else { PatKind::Lit(const_expr) } + } else if self.is_builtin() { + self.parse_pat_builtin()? + } // Don't eagerly error on semantically invalid tokens when matching // declarative macros, as the input to those doesn't have to be // semantically valid. For attribute/derive proc macros this is not the // case, so doing the recovery for them is fine. - } else if self.can_be_ident_pat() + else if self.can_be_ident_pat() || (self.is_lit_bad_ident().is_some() && self.may_recover()) { // Parse `ident @ pat` // This can give false positives and parse nullary enums, // they are dealt with later in resolve. - self.parse_pat_ident(BindingAnnotation::NONE, syntax_loc)? + self.parse_pat_ident(BindingMode::NONE, syntax_loc)? } else if self.is_start_of_pat_with_path() { // Parse pattern starting with a path let (qself, path) = if self.eat_lt() { @@ -743,13 +746,12 @@ impl<'a> Parser<'a> { } /// Parse a mutable binding with the `mut` token already eaten. - fn parse_pat_ident_mut(&mut self, syntax_loc: Option) -> PResult<'a, PatKind> { + fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { let mut_span = self.prev_token.span; - if self.eat_keyword(kw::Ref) { - self.dcx().emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) }); - return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc); - } + self.recover_additional_muts(); + + let byref = self.parse_byref(); self.recover_additional_muts(); @@ -764,10 +766,11 @@ impl<'a> Parser<'a> { let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?; // If we don't have `mut $ident (@ pat)?`, error. - if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind + if let PatKind::Ident(BindingMode(br @ ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind { // Don't recurse into the subpattern. // `mut` on the outer binding doesn't affect the inner bindings. + *br = byref; *m = Mutability::Mut; } else { // Add `mut` to any binding in the parsed pattern. @@ -775,6 +778,9 @@ impl<'a> Parser<'a> { self.ban_mut_general_pat(mut_span, &pat, changed_any_binding); } + if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(_), Mutability::Mut), ..)) { + self.psess.gated_spans.gate(sym::mut_ref, pat.span); + } Ok(pat.into_inner().kind) } @@ -784,7 +790,7 @@ impl<'a> Parser<'a> { struct AddMut(bool); impl MutVisitor for AddMut { fn visit_pat(&mut self, pat: &mut P) { - if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = + if let PatKind::Ident(BindingMode(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind { self.0 = true; @@ -1008,7 +1014,7 @@ impl<'a> Parser<'a> { && self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(Delimiter::Parenthesis) // A tuple struct pattern. | token::OpenDelim(Delimiter::Brace) // A struct pattern. | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern. - | token::ModSep // A tuple / struct variant pattern. + | token::PathSep // A tuple / struct variant pattern. | token::Not)) // A macro expanding to a pattern. } @@ -1017,7 +1023,7 @@ impl<'a> Parser<'a> { /// error message when parsing mistakes like `ref foo(a, b)`. fn parse_pat_ident( &mut self, - binding_annotation: BindingAnnotation, + binding_annotation: BindingMode, syntax_loc: Option, ) -> PResult<'a, PatKind> { let ident = self.parse_ident_common(false)?; @@ -1119,6 +1125,21 @@ impl<'a> Parser<'a> { .contains(&self.token.kind) } + fn parse_pat_builtin(&mut self) -> PResult<'a, PatKind> { + self.parse_builtin(|self_, _lo, ident| { + Ok(match ident.name { + // builtin#deref(PAT) + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?)), + _ => None, + }) + }) + } + /// Parses `box pat` fn parse_pat_box(&mut self) -> PResult<'a, PatKind> { let box_span = self.prev_token.span; @@ -1140,7 +1161,7 @@ impl<'a> Parser<'a> { None }; - Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub)) + Ok(PatKind::Ident(BindingMode::NONE, Ident::new(kw::Box, box_span), sub)) } else { let pat = self.parse_pat_with_range_pat(false, None, None)?; self.psess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span)); @@ -1321,7 +1342,7 @@ impl<'a> Parser<'a> { if let Some(last) = fields.iter().last() && last.is_shorthand && let PatKind::Ident(binding, ident, None) = last.pat.kind - && binding != BindingAnnotation::NONE + && binding != BindingMode::NONE && self.token == token::Colon // We found `ref mut? ident:`, try to parse a `name,` or `name }`. && let Some(name_span) = self.look_ahead(1, |t| t.is_ident().then(|| t.span)) @@ -1372,16 +1393,12 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `(box) (ref) (mut) fieldname`. let is_box = self.eat_keyword(kw::Box); let boxed_span = self.token.span; - let is_ref = self.eat_keyword(kw::Ref); - let is_mut = self.eat_keyword(kw::Mut); + let mutability = self.parse_mutability(); + let by_ref = self.parse_byref(); + let fieldname = self.parse_field_name()?; hi = self.prev_token.span; - - let mutability = match is_mut { - false => Mutability::Not, - true => Mutability::Mut, - }; - let ann = BindingAnnotation(ByRef::from(is_ref), mutability); + let ann = BindingMode(by_ref, mutability); let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname); let subpat = if is_box { self.mk_pat(lo.to(hi), PatKind::Box(fieldpat)) } else { fieldpat }; @@ -1399,7 +1416,7 @@ impl<'a> Parser<'a> { }) } - pub(super) fn mk_pat_ident(&self, span: Span, ann: BindingAnnotation, ident: Ident) -> P { + pub(super) fn mk_pat_ident(&self, span: Span, ann: BindingMode, ident: Ident) -> P { self.mk_pat(span, PatKind::Ident(ann, ident, None)) } diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 163d10d03f03c..b97ec8c613d91 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -96,7 +96,7 @@ impl<'a> Parser<'a> { } if !self.recover_colon_before_qpath_proj() { - self.expect(&token::ModSep)?; + self.expect(&token::PathSep)?; } let qself = P(QSelf { ty, path_span, position: path.segments.len() }); @@ -200,7 +200,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let mut segments = ThinVec::new(); let mod_sep_ctxt = self.token.span.ctxt(); - if self.eat(&token::ModSep) { + if self.eat(&token::PathSep) { segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); } self.parse_path_segments(&mut segments, style, ty_generics)?; @@ -232,11 +232,11 @@ impl<'a> Parser<'a> { // `PathStyle::Expr` is only provided at the root invocation and never in // `parse_path_segment` to recurse and therefore can be checked to maintain // this invariant. - self.check_trailing_angle_brackets(&segment, &[&token::ModSep]); + self.check_trailing_angle_brackets(&segment, &[&token::PathSep]); } segments.push(segment); - if self.is_import_coupler() || !self.eat(&token::ModSep) { + if self.is_import_coupler() || !self.eat(&token::PathSep) { if style == PathStyle::Expr && self.may_recover() && self.token == token::Colon @@ -291,7 +291,7 @@ impl<'a> Parser<'a> { Ok( if style == PathStyle::Type && check_args_start(self) || style != PathStyle::Mod - && self.check(&token::ModSep) + && self.check(&token::PathSep) && self.look_ahead(1, |t| is_args_start(t)) { // We use `style == PathStyle::Expr` to check if this is in a recursion or not. If @@ -299,11 +299,10 @@ impl<'a> Parser<'a> { // parsing a new path. if style == PathStyle::Expr { self.unmatched_angle_bracket_count = 0; - self.max_angle_bracket_count = 0; } // Generic arguments are found - `<`, `(`, `::<` or `::(`. - self.eat(&token::ModSep); + self.eat(&token::PathSep); let lo = self.token.span; let args = if self.eat_lt() { // `<'a, T, A = U>` @@ -379,7 +378,7 @@ impl<'a> Parser<'a> { let token_before_parsing = self.token.clone(); let mut snapshot = None; if self.may_recover() - && prev_token_before_parsing.kind == token::ModSep + && prev_token_before_parsing.kind == token::PathSep && (style == PathStyle::Expr && self.token.can_begin_expr() || style == PathStyle::Pat && self.token.can_begin_pattern()) { @@ -388,7 +387,7 @@ impl<'a> Parser<'a> { let (inputs, _) = match self.parse_paren_comma_seq(|p| p.parse_ty()) { Ok(output) => output, - Err(mut error) if prev_token_before_parsing.kind == token::ModSep => { + Err(mut error) if prev_token_before_parsing.kind == token::PathSep => { error.span_label( prev_token_before_parsing.span.to(token_before_parsing.span), "while parsing this parenthesized list of type arguments starting here", @@ -470,7 +469,7 @@ impl<'a> Parser<'a> { } } - if let token::ModSep | token::RArrow = self.token.kind { + if let token::PathSep | token::RArrow = self.token.kind { return; } @@ -660,7 +659,7 @@ impl<'a> Parser<'a> { // Add `>` to the list of expected tokens. self.check(&token::Gt); // Handle `,` to `;` substitution - let mut err = self.unexpected::<()>().unwrap_err(); + let mut err = self.unexpected().unwrap_err(); self.bump(); err.span_suggestion_verbose( self.prev_token.span.until(self.token.span), @@ -718,7 +717,11 @@ impl<'a> Parser<'a> { let bounds = self.parse_generic_bounds()?; AssocConstraintKind::Bound { bounds } } else if self.eat(&token::Eq) { - self.parse_assoc_equality_term(ident, self.prev_token.span)? + self.parse_assoc_equality_term( + ident, + gen_args.as_ref(), + self.prev_token.span, + )? } else { unreachable!(); }; @@ -731,8 +734,6 @@ impl<'a> Parser<'a> { && matches!(args.output, ast::FnRetTy::Default(..)) { self.psess.gated_spans.gate(sym::return_type_notation, span); - } else { - self.psess.gated_spans.gate(sym::associated_type_bounds, span); } } let constraint = @@ -755,11 +756,13 @@ impl<'a> Parser<'a> { } /// Parse the term to the right of an associated item equality constraint. - /// That is, parse `` in `Item = `. - /// Right now, this only admits types in ``. + /// + /// That is, parse `$term` in `Item = $term` where `$term` is a type or + /// a const expression (wrapped in curly braces if complex). fn parse_assoc_equality_term( &mut self, ident: Ident, + gen_args: Option<&GenericArgs>, eq: Span, ) -> PResult<'a, AssocConstraintKind> { let arg = self.parse_generic_arg(None)?; @@ -771,9 +774,15 @@ impl<'a> Parser<'a> { c.into() } Some(GenericArg::Lifetime(lt)) => { - let guar = - self.dcx().emit_err(errors::AssocLifetime { span, lifetime: lt.ident.span }); - self.mk_ty(span, ast::TyKind::Err(guar)).into() + let guar = self.dcx().emit_err(errors::LifetimeInEqConstraint { + span: lt.ident.span, + lifetime: lt.ident, + binding_label: span, + colon_sugg: gen_args + .map_or(ident.span, |args| args.span()) + .between(lt.ident.span), + }); + self.mk_ty(lt.ident.span, ast::TyKind::Err(guar)).into() } None => { let after_eq = eq.shrink_to_hi(); diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index fc907760531f7..6601011665b41 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -40,8 +40,8 @@ impl<'a> Parser<'a> { })) } - /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of whether - /// or not we have attributes + /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of + /// whether or not we have attributes. // Public for `cfg_eval` macro expansion. pub fn parse_stmt_without_recovery( &mut self, @@ -51,18 +51,12 @@ impl<'a> Parser<'a> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - // Don't use `maybe_whole` so that we have precise control - // over when we bump the parser - if let token::Interpolated(nt) = &self.token.kind - && let token::NtStmt(stmt) = &nt.0 - { - let mut stmt = stmt.clone(); - self.bump(); + maybe_whole!(self, NtStmt, |stmt| { stmt.visit_attrs(|stmt_attrs| { attrs.prepend_to_nt_inner(stmt_attrs); }); - return Ok(Some(stmt.into_inner())); - } + Some(stmt.into_inner()) + }); if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) { self.bump(); @@ -539,7 +533,7 @@ impl<'a> Parser<'a> { blk_mode: BlockCheckMode, can_be_struct_literal: bool, ) -> PResult<'a, (AttrVec, P)> { - maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x)); + maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block)); let maybe_ident = self.prev_token.clone(); self.maybe_recover_unexpected_block_label(); @@ -643,7 +637,7 @@ impl<'a> Parser<'a> { recover: AttemptLocalParseRecovery, ) -> PResult<'a, Option> { // Skip looking for a trailing semicolon when we have an interpolated statement. - maybe_whole!(self, NtStmt, |x| Some(x.into_inner())); + maybe_whole!(self, NtStmt, |stmt| Some(stmt.into_inner())); let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No)? else { return Ok(None); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2385c18c0891c..7096b201f847a 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,4 +1,4 @@ -use super::{Parser, PathStyle, TokenType, Trailing}; +use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; use crate::errors::{ self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, @@ -14,7 +14,7 @@ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, PolyTraitRef, - TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID, + PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID, }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{kw, sym, Ident}; @@ -82,7 +82,7 @@ enum AllowCVariadic { /// Types can also be of the form `IDENT(u8, u8) -> u8`, however this assumes /// that `IDENT` is not the ident of a fn trait. fn can_continue_type_after_non_fn_ident(t: &Token) -> bool { - t == &token::ModSep || t == &token::Lt || t == &token::BinOp(token::Shl) + t == &token::PathSep || t == &token::Lt || t == &token::BinOp(token::Shl) } fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool { @@ -250,7 +250,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); - maybe_whole!(self, NtTy, |x| x); + maybe_whole!(self, NtTy, |ty| ty); let lo = self.token.span; let mut impl_dyn_multi = false; @@ -316,7 +316,7 @@ impl<'a> Parser<'a> { TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn) } (TyKind::TraitObject(bounds, _), kw::Impl) => { - TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds) + TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, None) } _ => return Err(err), }; @@ -655,7 +655,6 @@ impl<'a> Parser<'a> { /// Parses an `impl B0 + ... + Bn` type. fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { - // Always parse bounds greedily for better error recovery. if self.token.is_lifetime() { self.look_ahead(1, |t| { if let token::Ident(sym, _) = t.kind { @@ -669,9 +668,53 @@ impl<'a> Parser<'a> { } }) } + + // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of + // lifetimes and ident params (including SelfUpper). These are validated later + // for order, duplication, and whether they actually reference params. + let precise_capturing = if self.eat_keyword(kw::Use) { + let use_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::precise_capturing, use_span); + let args = self.parse_precise_capturing_args()?; + Some(P((args, use_span))) + } else { + None + }; + + // Always parse bounds greedily for better error recovery. let bounds = self.parse_generic_bounds()?; + *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus); - Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)) + + Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing)) + } + + fn parse_precise_capturing_args(&mut self) -> PResult<'a, ThinVec> { + Ok(self + .parse_unspanned_seq( + &TokenKind::Lt, + &TokenKind::Gt, + SeqSep::trailing_allowed(token::Comma), + |self_| { + if self_.check_keyword(kw::SelfUpper) { + self_.bump(); + Ok(PreciseCapturingArg::Arg( + ast::Path::from_ident(self_.prev_token.ident().unwrap().0), + DUMMY_NODE_ID, + )) + } else if self_.check_ident() { + Ok(PreciseCapturingArg::Arg( + ast::Path::from_ident(self_.parse_ident()?), + DUMMY_NODE_ID, + )) + } else if self_.check_lifetime() { + Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime())) + } else { + self_.unexpected_any() + } + }, + )? + .0) } /// Is a `dyn B0 + ... + Bn` type allowed here? @@ -957,7 +1000,7 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ) } - TyKind::ImplTrait(_, bounds) + TyKind::ImplTrait(_, bounds, None) if let [GenericBound::Trait(tr, ..), ..] = bounds.as_slice() => { ( diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 79aab47fbe717..faf6ca7846709 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -907,25 +907,47 @@ impl<'a> Parser<'a> { let byte_pos = self.to_span_index(end); let start = InnerOffset(byte_pos.0 + 1); let field = self.argument(start); - // We can only parse `foo.bar` field access, any deeper nesting, - // or another type of expression, like method calls, are not supported + // We can only parse simple `foo.bar` field access or `foo.0` tuple index access, any + // deeper nesting, or another type of expression, like method calls, are not supported if !self.consume('}') { return; } if let ArgumentNamed(_) = arg.position { - if let ArgumentNamed(_) = field.position { - self.errors.insert( - 0, - ParseError { - description: "field access isn't supported".to_string(), - note: None, - label: "not supported".to_string(), - span: InnerSpan::new(arg.position_span.start, field.position_span.end), - secondary_label: None, - suggestion: Suggestion::UsePositional, - }, - ); - } + match field.position { + ArgumentNamed(_) => { + self.errors.insert( + 0, + ParseError { + description: "field access isn't supported".to_string(), + note: None, + label: "not supported".to_string(), + span: InnerSpan::new( + arg.position_span.start, + field.position_span.end, + ), + secondary_label: None, + suggestion: Suggestion::UsePositional, + }, + ); + } + ArgumentIs(_) => { + self.errors.insert( + 0, + ParseError { + description: "tuple index access isn't supported".to_string(), + note: None, + label: "not supported".to_string(), + span: InnerSpan::new( + arg.position_span.start, + field.position_span.end, + ), + secondary_label: None, + suggestion: Suggestion::UsePositional, + }, + ); + } + _ => {} + }; } } } @@ -1065,7 +1087,7 @@ fn unescape_string(string: &str) -> Option { } // Assert a reasonable size for `Piece` -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_index::static_assert_size!(Piece<'_>, 16); #[cfg(test)] diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7fc523ffe0dea..a545c170297de 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -353,7 +353,7 @@ passes_incorrect_meta_item = expected a quoted string literal passes_incorrect_meta_item_suggestion = consider surrounding this with quotes passes_incorrect_target = - `{$name}` language item must be applied to a {$kind} with {$at_least -> + `{$name}` lang item must be applied to a {$kind} with {$at_least -> [true] at least {$num} *[false] {$num} } generic {$num -> @@ -394,12 +394,21 @@ passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export] passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments +passes_lang_item_fn = {$name -> + [panic_impl] `#[panic_handler]` + *[other] `{$name}` lang item +} function + passes_lang_item_fn_with_target_feature = - `{$name}` language item function is not allowed to have `#[target_feature]` - .label = `{$name}` language item function is not allowed to have `#[target_feature]` + {passes_lang_item_fn} is not allowed to have `#[target_feature]` + .label = {passes_lang_item_fn} is not allowed to have `#[target_feature]` + +passes_lang_item_fn_with_track_caller = + {passes_lang_item_fn} is not allowed to have `#[track_caller]` + .label = {passes_lang_item_fn} is not allowed to have `#[target_feature]` passes_lang_item_on_incorrect_target = - `{$name}` language item must be applied to a {$expected_target} + `{$name}` lang item must be applied to a {$expected_target} .label = attribute should be applied to a {$expected_target}, not a {$actual_target} passes_layout_abi = @@ -455,7 +464,7 @@ passes_missing_const_stab_attr = {$descr} has missing const stability attribute passes_missing_lang_item = - language item required, but not found: `{$name}` + lang item required, but not found: `{$name}` .note = this can occur when a binary crate with `#![no_std]` is compiled for a target where `{$name}` is defined in the standard library .help = you may be able to compile for a target that doesn't need `{$name}`, specify a target with `--target` or in `.cargo/config` @@ -696,8 +705,8 @@ passes_unknown_feature = unknown feature `{$feature}` passes_unknown_lang_item = - definition of an unknown language item: `{$name}` - .label = definition of unknown language item `{$name}` + definition of an unknown lang item: `{$name}` + .label = definition of unknown lang item `{$name}` passes_unlabeled_cf_in_while_condition = `break` or `continue` with no label in the condition of a `while` loop diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 986ef69ad8849..0c3dd649997ef 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -1,6 +1,7 @@ use rustc_ast::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_middle::span_bug; use rustc_middle::ty::layout::{FnAbiError, LayoutError}; use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt}; use rustc_span::source_map::Spanned; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index eb2399f7a64f4..c403e9196fab2 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -11,14 +11,15 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::StashKey; use rustc_errors::{Applicability, DiagCtxt, IntoDiagArg, MultiSpan}; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; -use rustc_hir as hir; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir}; use rustc_hir::{ self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, }; use rustc_hir::{MethodKind, Target, Unsafety}; use rustc_macros::LintDiagnostic; +use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::query::Providers; @@ -519,7 +520,26 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.dcx().emit_err(errors::NakedTrackedCaller { attr_span }); false } - Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, + Target::Fn => { + // `#[track_caller]` is not valid on weak lang items because they are called via + // `extern` declarations and `#[track_caller]` would alter their ABI. + if let Some((lang_item, _)) = hir::lang_items::extract(attrs) + && let Some(item) = hir::LangItem::from_name(lang_item) + && item.is_weak() + { + let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); + + self.dcx().emit_err(errors::LangItemWithTrackCaller { + attr_span, + name: lang_item, + sig_span: sig.span, + }); + false + } else { + true + } + } + Target::Method(..) | Target::ForeignFn | Target::Closure => true, // FIXME(#80564): We permit struct fields, match arms and macro defs to have an // `#[track_caller]` attribute with just a lint, because we previously // erroneously allowed it and some crates used it accidentally, to be compatible @@ -602,20 +622,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) -> bool { match target { Target::Fn => { - // `#[target_feature]` is not allowed in language items. + // `#[target_feature]` is not allowed in lang items. if let Some((lang_item, _)) = hir::lang_items::extract(attrs) // Calling functions with `#[target_feature]` is // not unsafe on WASM, see #84988 && !self.tcx.sess.target.is_like_wasm && !self.tcx.sess.opts.actually_rustdoc { - let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else { - unreachable!(); - }; - let hir::ItemKind::Fn(sig, _, _) = item.kind else { - // target is `Fn` - unreachable!(); - }; + let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); self.dcx().emit_err(errors::LangItemWithTargetFeature { attr_span: attr.span, diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 30a65d69c4ef8..eb29a65cb293d 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::hir::nested_filter; use rustc_middle::query::Providers; +use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_span::{sym, Span, Symbol}; @@ -48,7 +49,7 @@ impl NonConstExpr { Self::Match(TryDesugar(_)) => &[sym::const_try], // All other expressions are allowed. - Self::Loop(Loop | While) | Self::Match(Normal | FormatArgs) => &[], + Self::Loop(Loop | While) | Self::Match(Normal | Postfix | FormatArgs) => &[], }; Some(gates) @@ -155,16 +156,11 @@ impl<'tcx> CheckConstVisitor<'tcx> { // // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This // is a pretty narrow case, however. - if tcx.sess.is_nightly_build() { - for gate in missing_secondary { - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] - err.help(format!( - "add `#![feature({gate})]` to the crate attributes to enable" - )); - } - } + tcx.disabled_nightly_features( + &mut err, + def_id.map(|id| tcx.local_def_id_to_hir_id(id)), + missing_secondary.into_iter().map(|gate| (String::new(), *gate)), + ); err.emit(); } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 350f7e166bf9c..933412f677ce8 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -16,6 +16,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; use rustc_span::symbol::{sym, Symbol}; @@ -153,7 +154,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { self.insert_def_id(def.non_enum_variant().fields[index].did); } ty::Tuple(..) => {} - _ => span_bug!(lhs.span, "named field access on non-ADT"), + ty::Error(_) => {} + kind => span_bug!(lhs.span, "named field access on non-ADT: {kind:?}"), } } @@ -525,15 +527,16 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { let tcx = self.tcx; let unconditionally_treat_fields_as_live = self.repr_unconditionally_treats_fields_as_live; let has_repr_simd = self.repr_has_repr_simd; + let effective_visibilities = &tcx.effective_visibilities(()); let live_fields = def.fields().iter().filter_map(|f| { let def_id = f.def_id; if unconditionally_treat_fields_as_live || (f.is_positional() && has_repr_simd) { return Some(def_id); } - if !tcx.visibility(f.hir_id.owner.def_id).is_public() { + if !effective_visibilities.is_reachable(f.hir_id.owner.def_id) { return None; } - if tcx.visibility(def_id).is_public() { Some(def_id) } else { None } + if effective_visibilities.is_reachable(def_id) { Some(def_id) } else { None } }); self.live_symbols.extend(live_fields); @@ -831,7 +834,7 @@ fn create_and_seed_worklist( .collect::>(); let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { check_item(tcx, &mut worklist, &mut struct_constructors, &mut unsolved_impl_item, id); } @@ -1084,7 +1087,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let module_items = tcx.hir_module_items(module); - for item in module_items.items() { + for item in module_items.free_items() { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 2af5a54a0cac7..438c583db49e5 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -7,7 +7,7 @@ use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::config::{sigpipe, CrateType, EntryFnType}; -use rustc_session::parse::feature_err; +use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; @@ -133,16 +133,6 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, return None; } - if main_def.is_import && !tcx.features().imported_main { - let span = main_def.span; - feature_err( - &tcx.sess, - sym::imported_main, - span, - "using an imported function as entry point `main` is experimental", - ) - .emit(); - } return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })); } no_main_err(tcx, visitor); @@ -176,10 +166,14 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { // There is no main function. let mut has_filename = true; - let filename = tcx.sess.local_crate_source_file().unwrap_or_else(|| { - has_filename = false; - Default::default() - }); + let filename = tcx + .sess + .local_crate_source_file() + .map(|src| src.for_scope(&tcx.sess, RemapPathScopeComponents::DIAGNOSTICS).to_path_buf()) + .unwrap_or_else(|| { + has_filename = false; + Default::default() + }); let main_def_opt = tcx.resolutions(()).main_def; let code = E0601; let add_teach_note = tcx.sess.teach(code); diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 3f26ea4507d41..03a607348e88c 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -819,6 +819,16 @@ pub struct MissingLangItem { pub name: Symbol, } +#[derive(Diagnostic)] +#[diag(passes_lang_item_fn_with_track_caller)] +pub struct LangItemWithTrackCaller { + #[primary_span] + pub attr_span: Span, + pub name: Symbol, + #[label] + pub sig_span: Span, +} + #[derive(Diagnostic)] #[diag(passes_lang_item_fn_with_target_feature)] pub struct LangItemWithTargetFeature { @@ -1755,7 +1765,7 @@ impl Subdiagnostic for UnusedVariableStringInterp { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation); diag.multipart_suggestion( diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index dd6c116695784..51a69809c7a57 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -161,4 +161,8 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { let mut inner_visitor = self.new_visitor(self.tcx); inner_visitor.check(i.owner_id, |this| intravisit::walk_impl_item(this, i)); } + + fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) { + self.visit_pat(p) + } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index d742ffc69e450..d7664d1c1fff7 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -264,7 +264,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_foreign_item(self, i) } - fn visit_local(&mut self, l: &'v hir::Local<'v>) { + fn visit_local(&mut self, l: &'v hir::LetStmt<'v>) { self.record("Local", Id::Node(l.hir_id), l); hir_visit::walk_local(self, l) } @@ -300,6 +300,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Path, Tuple, Box, + Deref, Ref, Lit, Range, @@ -351,6 +352,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { TraitObject, Typeof, Infer, + Pat, Err ] ); @@ -387,7 +389,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_fn(self, fk, fd, b, id) } - fn visit_use(&mut self, p: &'v hir::UsePath<'v>, hir_id: hir::HirId) { + fn visit_use(&mut self, p: &'v hir::UsePath<'v>, hir_id: HirId) { // This is `visit_use`, but the type is `Path` so record it that way. self.record("Path", Id::None, p); hir_visit::walk_use(self, p, hir_id) @@ -460,7 +462,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_lifetime(self, lifetime) } - fn visit_path(&mut self, path: &hir::Path<'v>, _id: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'v>, _id: HirId) { self.record("Path", Id::None, path); hir_visit::walk_path(self, path) } @@ -496,7 +498,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { (self, i, i.kind, Id::None, ast, ForeignItem, ForeignItemKind), [Static, Fn, TyAlias, MacCall] ); - ast_visit::walk_foreign_item(self, i) + ast_visit::walk_item(self, i) } fn visit_item(&mut self, i: &'v ast::Item) { @@ -566,6 +568,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Path, Tuple, Box, + Deref, Ref, Lit, Range, @@ -609,6 +612,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { AnonStruct, AnonUnion, Path, + Pat, TraitObject, ImplTrait, Paren, diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index d1368267224a7..c1da8928f3021 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -1,4 +1,4 @@ -//! Detecting language items. +//! Detecting lang items. //! //! Language items are items that represent concepts intrinsic to the language //! itself. Examples are: diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 8d223c23363bb..82d43f078ee27 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -1,6 +1,7 @@ use rustc_ast::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_middle::span_bug; use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_span::source_map::Spanned; diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index e03052bcfede8..d3608759eec70 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -8,13 +8,10 @@ #![doc(rust_logo)] #![feature(rustdoc_internals)] #![allow(internal_features)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(map_try_insert)] #![feature(try_blocks)] -#[macro_use] -extern crate rustc_middle; #[macro_use] extern crate tracing; diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index f0c3f7a385d60..b50cb158b1fed 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -94,6 +94,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet}; use rustc_index::IndexVec; use rustc_middle::query::Providers; +use rustc_middle::span_bug; use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{kw, sym, Symbol}; @@ -342,7 +343,7 @@ impl<'tcx> IrMaps<'tcx> { } impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.add_from_pat(local.pat); if local.els.is_some() { self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id)); @@ -602,7 +603,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { String::from_utf8(wr).unwrap() } - fn log_liveness(&self, entry_ln: LiveNode, hir_id: hir::HirId) { + fn log_liveness(&self, entry_ln: LiveNode, hir_id: HirId) { // hack to skip the loop unless debug! is enabled: debug!( "^^ liveness computation results for body {} (entry={:?})", @@ -1350,7 +1351,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Checking for error conditions impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.check_unused_vars_in_pat(local.pat, None, None, |spans, hir_id, ln, var| { if local.init.is_some() { self.warn_about_dead_assign(spans, hir_id, ln, var); diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index e10a22cdf31b9..4b5c4dfe991b7 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -6,6 +6,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Destination, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::query::Providers; +use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::hygiene::DesugaringKind; diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index bd34b0597e255..d45ee32a624de 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, InlineAsmOperand, StmtKind}; +use rustc_hir::{ExprKind, HirIdSet, InlineAsmOperand, StmtKind}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; @@ -83,8 +83,7 @@ fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) { fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) { for param in params { match param.pat.kind { - hir::PatKind::Wild - | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {} + hir::PatKind::Wild | hir::PatKind::Binding(hir::BindingMode::NONE, _, _, None) => {} _ => { tcx.dcx().emit_err(NoPatterns { span: param.pat.span }); } @@ -94,7 +93,7 @@ fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) { /// Checks that function parameters aren't used in the function body. fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) { - let mut params = hir::HirIdSet::default(); + let mut params = HirIdSet::default(); for param in body.params { param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| { params.insert(hir_id); @@ -105,7 +104,7 @@ fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) struct CheckParameters<'tcx> { tcx: TyCtxt<'tcx>, - params: hir::HirIdSet, + params: HirIdSet, } impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 6eb03bc0f5fbc..d5e1a70fd45c4 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -1,9 +1,14 @@ -// Finds items that are externally reachable, to determine which items -// need to have their metadata (and possibly their AST) serialized. -// All items that can be referred to through an exported name are -// reachable, and when a reachable thing is inline or generic, it -// makes all other generics or inline functions that it references -// reachable as well. +//! Finds local items that are externally reachable, which means that other crates need access to +//! their compiled machine code or their MIR. +//! +//! An item is "externally reachable" if it is relevant for other crates. This obviously includes +//! all public items. However, some of these items cannot be compiled to machine code (because they +//! are generic), and for some the machine code is not sufficient (because we want to cross-crate +//! inline them). These items "need cross-crate MIR". When a reachable function `f` needs +//! cross-crate MIR, then all the functions it calls also become reachable, as they will be +//! necessary to use the MIR of `f` from another crate. Furthermore, an item can become "externally +//! reachable" by having a `const`/`const fn` return a pointer to that item, so we also need to +//! recurse into reachable `const`/`const fn`. use hir::def_id::LocalDefIdSet; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -12,6 +17,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Node; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::middle::privacy::{self, Level}; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc}; @@ -19,9 +25,10 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, ExistentialTraitRef, TyCtxt}; use rustc_privacy::DefIdVisitor; use rustc_session::config::CrateType; -use rustc_target::spec::abi::Abi; -fn item_might_be_inlined(tcx: TyCtxt<'_>, def_id: DefId) -> bool { +/// Determines whether this item is recursive for reachability. See `is_recursively_reachable_local` +/// below for details. +fn recursively_reachable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { tcx.generics_of(def_id).requires_monomorphization(tcx) || tcx.cross_crate_inlinable(def_id) || tcx.is_const_fn(def_id) @@ -54,12 +61,20 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { let res = match expr.kind { hir::ExprKind::Path(ref qpath) => { + // This covers fn ptr casts but also "non-method" calls. Some(self.typeck_results().qpath_res(qpath, expr.hir_id)) } - hir::ExprKind::MethodCall(..) => self - .typeck_results() - .type_dependent_def(expr.hir_id) - .map(|(kind, def_id)| Res::Def(kind, def_id)), + hir::ExprKind::MethodCall(..) => { + // Method calls don't involve a full "path", so we need to determine the callee + // based on the receiver type. + // If this is a method call on a generic type, we might not be able to find the + // callee. That's why `reachable_set` also adds all potential callees for such + // calls, i.e. all trait impl items, to the reachable set. So here we only worry + // about the calls we can identify. + self.typeck_results() + .type_dependent_def(expr.hir_id) + .map(|(kind, def_id)| Res::Def(kind, def_id)) + } hir::ExprKind::Closure(&hir::Closure { def_id, .. }) => { self.reachable_symbols.insert(def_id); None @@ -96,16 +111,24 @@ impl<'tcx> ReachableContext<'tcx> { .expect("`ReachableContext::typeck_results` called outside of body") } - // Returns true if the given def ID represents a local item that is - // eligible for inlining and false otherwise. - fn def_id_represents_local_inlined_item(&self, def_id: DefId) -> bool { + /// Returns true if the given def ID represents a local item that is recursive for reachability, + /// i.e. whether everything mentioned in here also needs to be considered reachable. + /// + /// There are two reasons why an item may be recursively reachable: + /// - It needs cross-crate MIR (see the module-level doc comment above). + /// - It is a `const` or `const fn`. This is *not* because we need the MIR to interpret them + /// (MIR for const-eval and MIR for codegen is separate, and MIR for const-eval is always + /// encoded). Instead, it is because `const fn` can create `fn()` pointers to other items + /// which end up in the evaluated result of the constant and can then be called from other + /// crates. Those items must be considered reachable. + fn is_recursively_reachable_local(&self, def_id: DefId) -> bool { let Some(def_id) = def_id.as_local() else { return false; }; match self.tcx.hir_node_by_def_id(def_id) { Node::Item(item) => match item.kind { - hir::ItemKind::Fn(..) => item_might_be_inlined(self.tcx, def_id.into()), + hir::ItemKind::Fn(..) => recursively_reachable(self.tcx, def_id.into()), _ => false, }, Node::TraitItem(trait_method) => match trait_method.kind { @@ -117,7 +140,7 @@ impl<'tcx> ReachableContext<'tcx> { Node::ImplItem(impl_item) => match impl_item.kind { hir::ImplItemKind::Const(..) => true, hir::ImplItemKind::Fn(..) => { - item_might_be_inlined(self.tcx, impl_item.hir_id().owner.to_def_id()) + recursively_reachable(self.tcx, impl_item.hir_id().owner.to_def_id()) } hir::ImplItemKind::Type(_) => false, }, @@ -141,16 +164,6 @@ impl<'tcx> ReachableContext<'tcx> { if !self.any_library { // If we are building an executable, only explicitly extern // types need to be exported. - let reachable = - if let Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) - | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(sig, ..), .. - }) = *node - { - sig.header.abi != Abi::Rust - } else { - false - }; let codegen_attrs = if self.tcx.def_kind(search_item).has_codegen_attrs() { self.tcx.codegen_fn_attrs(search_item) } else { @@ -159,7 +172,7 @@ impl<'tcx> ReachableContext<'tcx> { let is_extern = codegen_attrs.contains_extern_indicator(); let std_internal = codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); - if reachable || is_extern || std_internal { + if is_extern || std_internal { self.reachable_symbols.insert(search_item); } } else { @@ -174,7 +187,7 @@ impl<'tcx> ReachableContext<'tcx> { Node::Item(item) => { match item.kind { hir::ItemKind::Fn(.., body) => { - if item_might_be_inlined(self.tcx, item.owner_id.into()) { + if recursively_reachable(self.tcx, item.owner_id.into()) { self.visit_nested_body(body); } } @@ -228,7 +241,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit_nested_body(body); } hir::ImplItemKind::Fn(_, body) => { - if item_might_be_inlined(self.tcx, impl_item.hir_id().owner.to_def_id()) { + if recursively_reachable(self.tcx, impl_item.hir_id().owner.to_def_id()) { self.visit_nested_body(body) } } @@ -247,7 +260,7 @@ impl<'tcx> ReachableContext<'tcx> { | Node::Field(_) | Node::Ty(_) | Node::Crate(_) - | Node::AssocOpaqueTy(..) => {} + | Node::Synthetic => {} _ => { bug!( "found unexpected node kind in worklist: {} ({:?})", @@ -316,7 +329,7 @@ impl<'tcx> ReachableContext<'tcx> { self.worklist.push(def_id); } _ => { - if self.def_id_represents_local_inlined_item(def_id.to_def_id()) { + if self.is_recursively_reachable_local(def_id.to_def_id()) { self.worklist.push(def_id); } else { self.reachable_symbols.insert(def_id); @@ -394,6 +407,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) } +/// See module-level doc comment above. fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet { let effective_visibilities = &tcx.effective_visibilities(()); @@ -427,17 +441,19 @@ fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet { } } { - // Some methods from non-exported (completely private) trait impls still have to be - // reachable if they are called from inlinable code. Generally, it's not known until - // monomorphization if a specific trait impl item can be reachable or not. So, we - // conservatively mark all of them as reachable. + // As explained above, we have to mark all functions called from reachable + // `item_might_be_inlined` items as reachable. The issue is, when those functions are + // generic and call a trait method, we have no idea where that call goes! So, we + // conservatively mark all trait impl items as reachable. // FIXME: One possible strategy for pruning the reachable set is to avoid marking impl // items of non-exported traits (or maybe all local traits?) unless their respective // trait items are used from inlinable code through method call syntax or UFCS, or their // trait is a lang item. + // (But if you implement this, don't forget to take into account that vtables can also + // make trait methods reachable!) let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { check_item(tcx, id, &mut reachable_context.worklist, effective_visibilities); } diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs index 4d44e8762b0d1..2eed58d10bb7b 100644 --- a/compiler/rustc_passes/src/upvars.rs +++ b/compiler/rustc_passes/src/upvars.rs @@ -66,7 +66,7 @@ impl CaptureCollector<'_, '_> { } impl<'tcx> Visitor<'tcx> for CaptureCollector<'_, 'tcx> { - fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) { if let Res::Local(var_id) = path.res { self.visit_local_use(var_id, path.span); } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 4eb0c6c275e61..90691ca17908b 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -14,7 +14,7 @@ use crate::errors::{ }; /// Checks the crate for usage of weak lang items, returning a vector of all the -/// language items required by this crate, but not defined yet. +/// lang items required by this crate, but not defined yet. pub fn check_crate(tcx: TyCtxt<'_>, items: &mut lang_items::LanguageItems, krate: &ast::Crate) { // These are never called by user code, they're generated by the compiler. // They will never implicitly be added to the `missing` array unless we do diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index b9bdcb41929dc..0cb47e03441ba 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -22,6 +22,10 @@ smallvec = { version = "1.8.1", features = ["union"] } tracing = "0.1" # tidy-alphabetical-end +[dev-dependencies] +tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "ansi"] } +tracing-tree = "0.3.0" + [features] default = ["rustc"] rustc = [ diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index bbb89576ef7ef..44f09b66bf6c6 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -140,6 +140,34 @@ //! [`ConstructorSet::split`]. The invariants of [`SplitConstructorSet`] are also of interest. //! //! +//! ## Unions +//! +//! Unions allow us to match a value via several overlapping representations at the same time. For +//! example, the following is exhaustive because when seeing the value as a boolean we handled all +//! possible cases (other cases such as `n == 3` would trigger UB). +//! +//! ```rust +//! # fn main() { +//! union U8AsBool { +//! n: u8, +//! b: bool, +//! } +//! let x = U8AsBool { n: 1 }; +//! unsafe { +//! match x { +//! U8AsBool { n: 2 } => {} +//! U8AsBool { b: true } => {} +//! U8AsBool { b: false } => {} +//! } +//! } +//! # } +//! ``` +//! +//! Pattern-matching has no knowledge that e.g. `false as u8 == 0`, so the values we consider in the +//! algorithm look like `U8AsBool { b: true, n: 2 }`. In other words, for the most part a union is +//! treated like a struct with the same fields. The difference lies in how we construct witnesses of +//! non-exhaustiveness. +//! //! //! ## Opaque patterns //! @@ -155,13 +183,13 @@ use std::iter::once; use smallvec::SmallVec; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS}; -use rustc_index::bit_set::GrowableBitSet; +use rustc_index::bit_set::{BitSet, GrowableBitSet}; +use rustc_index::IndexVec; use self::Constructor::*; use self::MaybeInfiniteInt::*; use self::SliceKind::*; -use crate::index; use crate::PatCx; /// Whether we have seen a constructor in the column or not. @@ -678,15 +706,19 @@ pub enum Constructor { Or, /// Wildcard pattern. Wildcard, + /// Never pattern. Only used in `WitnessPat`. An actual never pattern should be lowered as + /// `Wildcard`. + Never, /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used - /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. + /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. Only + /// used in `WitnessPat`. NonExhaustive, - /// Fake extra constructor for variants that should not be mentioned in diagnostics. - /// We use this for variants behind an unstable gate as well as - /// `#[doc(hidden)]` ones. + /// Fake extra constructor for variants that should not be mentioned in diagnostics. We use this + /// for variants behind an unstable gate as well as `#[doc(hidden)]` ones. Only used in + /// `WitnessPat`. Hidden, /// Fake extra constructor for constructors that are not seen in the matrix, as explained at the - /// top of the file. + /// top of the file. Only used for specialization. Missing, /// Fake extra constructor that indicates and empty field that is private. When we encounter one /// we skip the column entirely so we don't observe its emptiness. Only used for specialization. @@ -708,6 +740,7 @@ impl Clone for Constructor { Constructor::Str(value) => Constructor::Str(value.clone()), Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()), Constructor::Or => Constructor::Or, + Constructor::Never => Constructor::Never, Constructor::Wildcard => Constructor::Wildcard, Constructor::NonExhaustive => Constructor::NonExhaustive, Constructor::Hidden => Constructor::Hidden, @@ -814,6 +847,81 @@ impl Constructor { } }) } + + pub(crate) fn fmt_fields( + &self, + f: &mut fmt::Formatter<'_>, + ty: &Cx::Ty, + mut fields: impl Iterator, + ) -> fmt::Result { + let mut first = true; + let mut start_or_continue = |s| { + if first { + first = false; + "" + } else { + s + } + }; + let mut start_or_comma = || start_or_continue(", "); + + match self { + Struct | Variant(_) | UnionField => { + Cx::write_variant_name(f, self, ty)?; + // Without `cx`, we can't know which field corresponds to which, so we can't + // get the names of the fields. Instead we just display everything as a tuple + // struct, which should be good enough. + write!(f, "(")?; + for p in fields { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + write!(f, ")")?; + } + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to detect strings here. However a string literal pattern will never + // be reported as a non-exhaustiveness witness, so we can ignore this issue. + Ref => { + write!(f, "&{:?}", &fields.next().unwrap())?; + } + Slice(slice) => { + write!(f, "[")?; + match slice.kind { + SliceKind::FixedLen(_) => { + for p in fields { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + } + SliceKind::VarLen(prefix_len, _) => { + for p in fields.by_ref().take(prefix_len) { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + write!(f, "{}..", start_or_comma())?; + for p in fields { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + } + } + write!(f, "]")?; + } + Bool(b) => write!(f, "{b}")?, + // Best-effort, will render signed ranges incorrectly + IntRange(range) => write!(f, "{range:?}")?, + F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?, + F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?, + Str(value) => write!(f, "{value:?}")?, + Opaque(..) => write!(f, "")?, + Or => { + for pat in fields { + write!(f, "{}{:?}", start_or_continue(" | "), pat)?; + } + } + Never => write!(f, "!")?, + Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => { + write!(f, "_ : {:?}", ty)? + } + } + Ok(()) + } } #[derive(Debug, Clone, Copy)] @@ -840,10 +948,7 @@ pub enum ConstructorSet { Struct { empty: bool }, /// This type has the following list of constructors. If `variants` is empty and /// `non_exhaustive` is false, don't use this; use `NoConstructors` instead. - Variants { - variants: index::IdxContainer, - non_exhaustive: bool, - }, + Variants { variants: IndexVec, non_exhaustive: bool }, /// The type is `&T`. Ref, /// The type is a union. @@ -897,7 +1002,6 @@ impl ConstructorSet { /// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges /// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation /// and its invariants. - #[instrument(level = "debug", skip(self, ctors), ret)] pub fn split<'a>( &self, ctors: impl Iterator> + Clone, @@ -945,7 +1049,7 @@ impl ConstructorSet { } } ConstructorSet::Variants { variants, non_exhaustive } => { - let mut seen_set = index::IdxSet::new_empty(variants.len()); + let mut seen_set = BitSet::new_empty(variants.len()); for idx in seen.iter().filter_map(|c| c.as_variant()) { seen_set.insert(idx); } @@ -1040,10 +1144,32 @@ impl ConstructorSet { // In a `MaybeInvalid` place even an empty pattern may be reachable. We therefore // add a dummy empty constructor here, which will be ignored if the place is // `ValidOnly`. - missing_empty.push(NonExhaustive); + missing_empty.push(Never); } } SplitConstructorSet { present, missing, missing_empty } } + + /// Whether this set only contains empty constructors. + pub(crate) fn all_empty(&self) -> bool { + match self { + ConstructorSet::Bool + | ConstructorSet::Integers { .. } + | ConstructorSet::Ref + | ConstructorSet::Union + | ConstructorSet::Unlistable => false, + ConstructorSet::NoConstructors => true, + ConstructorSet::Struct { empty } => *empty, + ConstructorSet::Variants { variants, non_exhaustive } => { + !*non_exhaustive + && variants + .iter() + .all(|visibility| matches!(visibility, VariantVisibility::Empty)) + } + ConstructorSet::Slice { array_len, subtype_is_empty } => { + *subtype_is_empty && matches!(array_len, Some(1..)) + } + } + } } diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 75b7b7c8f6784..27f227e6d9c11 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -65,7 +65,7 @@ impl<'tcx> Subdiagnostic for Overlap<'tcx> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _: F, + _: &F, ) { let Overlap { span, range } = self; @@ -113,7 +113,7 @@ impl<'tcx> Subdiagnostic for GappedRange<'tcx> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _: F, + _: &F, ) { let GappedRange { span, gap, first_range } = self; diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 5c57c990323c8..4155540886a65 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -14,55 +14,14 @@ pub mod pat_column; pub mod rustc; pub mod usefulness; -#[macro_use] -extern crate tracing; -#[cfg(feature = "rustc")] -#[macro_use] -extern crate rustc_middle; - #[cfg(feature = "rustc")] rustc_fluent_macro::fluent_messages! { "../messages.ftl" } use std::fmt; -#[cfg(feature = "rustc")] -pub mod index { - // Faster version when the indices of variants are `0..variants.len()`. - pub use rustc_index::bit_set::BitSet as IdxSet; - pub use rustc_index::Idx; - pub use rustc_index::IndexVec as IdxContainer; -} -#[cfg(not(feature = "rustc"))] -pub mod index { - // Slower version when the indices of variants are something else. - pub trait Idx: Copy + PartialEq + Eq + std::hash::Hash {} - impl Idx for T {} - - #[derive(Debug)] - pub struct IdxContainer(pub rustc_hash::FxHashMap); - impl IdxContainer { - pub fn len(&self) -> usize { - self.0.len() - } - pub fn iter_enumerated(&self) -> impl Iterator { - self.0.iter().map(|(k, v)| (*k, v)) - } - } - - #[derive(Debug)] - pub struct IdxSet(pub rustc_hash::FxHashSet); - impl IdxSet { - pub fn new_empty(_len: usize) -> Self { - Self(Default::default()) - } - pub fn contains(&self, elem: T) -> bool { - self.0.contains(&elem) - } - pub fn insert(&mut self, elem: T) { - self.0.insert(elem); - } - } -} +// Re-exports to avoid rustc_index version issues. +pub use rustc_index::Idx; +pub use rustc_index::IndexVec; #[cfg(feature = "rustc")] use rustc_middle::ty::Ty; @@ -90,7 +49,7 @@ pub trait PatCx: Sized + fmt::Debug { /// Errors that can abort analysis. type Error: fmt::Debug; /// The index of an enum variant. - type VariantIdx: Clone + index::Idx + fmt::Debug; + type VariantIdx: Clone + Idx + fmt::Debug; /// A string literal type StrLit: Clone + PartialEq + fmt::Debug; /// Extra data to store in a match arm. @@ -120,7 +79,8 @@ pub trait PatCx: Sized + fmt::Debug { /// `DeconstructedPat`. Only invoqued when `pat.ctor()` is `Struct | Variant(_) | UnionField`. fn write_variant_name( f: &mut fmt::Formatter<'_>, - pat: &crate::pat::DeconstructedPat, + ctor: &crate::constructor::Constructor, + ty: &Self::Ty, ) -> fmt::Result; /// Raise a bug. diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 3ca5ebdb0dd13..1d10c9df4ab29 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -1,11 +1,11 @@ -use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_span::ErrorGuaranteed; - use crate::constructor::Constructor; use crate::errors::{NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered}; use crate::pat_column::PatternColumn; use crate::rustc::{RevealedTy, RustcPatCtxt, WitnessPat}; use crate::MatchArm; +use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; +use rustc_span::ErrorGuaranteed; +use tracing::instrument; /// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned /// in a given column. diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index e3667d44bc976..5f388ee9f8979 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -138,80 +138,11 @@ impl DeconstructedPat { /// This is best effort and not good enough for a `Display` impl. impl fmt::Debug for DeconstructedPat { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let pat = self; - let mut first = true; - let mut start_or_continue = |s| { - if first { - first = false; - "" - } else { - s - } - }; - let mut start_or_comma = || start_or_continue(", "); - let mut fields: Vec<_> = (0..self.arity).map(|_| PatOrWild::Wild).collect(); for ipat in self.iter_fields() { fields[ipat.idx] = PatOrWild::Pat(&ipat.pat); } - - match pat.ctor() { - Struct | Variant(_) | UnionField => { - Cx::write_variant_name(f, pat)?; - // Without `cx`, we can't know which field corresponds to which, so we can't - // get the names of the fields. Instead we just display everything as a tuple - // struct, which should be good enough. - write!(f, "(")?; - for p in fields { - write!(f, "{}", start_or_comma())?; - write!(f, "{p:?}")?; - } - write!(f, ")") - } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to detect strings here. However a string literal pattern will never - // be reported as a non-exhaustiveness witness, so we can ignore this issue. - Ref => { - write!(f, "&{:?}", &fields[0]) - } - Slice(slice) => { - write!(f, "[")?; - match slice.kind { - SliceKind::FixedLen(_) => { - for p in fields { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - } - SliceKind::VarLen(prefix_len, _) => { - for p in &fields[..prefix_len] { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - write!(f, "{}", start_or_comma())?; - write!(f, "..")?; - for p in &fields[prefix_len..] { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - } - } - write!(f, "]") - } - Bool(b) => write!(f, "{b}"), - // Best-effort, will render signed ranges incorrectly - IntRange(range) => write!(f, "{range:?}"), - F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), - F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), - Str(value) => write!(f, "{value:?}"), - Opaque(..) => write!(f, ""), - Or => { - for pat in fields { - write!(f, "{}{:?}", start_or_continue(" | "), pat)?; - } - Ok(()) - } - Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => { - write!(f, "_ : {:?}", pat.ty()) - } - } + self.ctor().fmt_fields(f, self.ty(), fields.into_iter()) } } @@ -294,7 +225,6 @@ impl<'p, Cx: PatCx> fmt::Debug for PatOrWild<'p, Cx> { /// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics /// purposes. As such they don't use interning and can be cloned. -#[derive(Debug)] pub struct WitnessPat { ctor: Constructor, pub(crate) fields: Vec>, @@ -311,18 +241,24 @@ impl WitnessPat { pub(crate) fn new(ctor: Constructor, fields: Vec, ty: Cx::Ty) -> Self { Self { ctor, fields, ty } } - pub(crate) fn wildcard(ty: Cx::Ty) -> Self { - Self::new(Wildcard, Vec::new(), ty) + /// Create a wildcard pattern for this type. If the type is empty, we create a `!` pattern. + pub(crate) fn wildcard(cx: &Cx, ty: Cx::Ty) -> Self { + let is_empty = cx.ctors_for_ty(&ty).is_ok_and(|ctors| ctors.all_empty()); + let ctor = if is_empty { Never } else { Wildcard }; + Self::new(ctor, Vec::new(), ty) } /// Construct a pattern that matches everything that starts with this constructor. /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(crate) fn wild_from_ctor(cx: &Cx, ctor: Constructor, ty: Cx::Ty) -> Self { + if matches!(ctor, Wildcard) { + return Self::wildcard(cx, ty); + } let fields = cx .ctor_sub_tys(&ctor, &ty) .filter(|(_, PrivateUninhabitedField(skip))| !skip) - .map(|(ty, _)| Self::wildcard(ty)) + .map(|(ty, _)| Self::wildcard(cx, ty)) .collect(); Self::new(ctor, fields, ty) } @@ -334,7 +270,22 @@ impl WitnessPat { &self.ty } + pub fn is_never_pattern(&self) -> bool { + match self.ctor() { + Never => true, + Or => self.fields.iter().all(|p| p.is_never_pattern()), + _ => self.fields.iter().any(|p| p.is_never_pattern()), + } + } + pub fn iter_fields(&self) -> impl Iterator> { self.fields.iter() } } + +/// This is best effort and not good enough for a `Display` impl. +impl fmt::Debug for WitnessPat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.ctor().fmt_fields(f, self.ty(), self.fields.iter()) + } +} diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index fd51fbedeef9d..ff68dd81beaf2 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -11,6 +11,7 @@ use rustc_middle::mir::{self, Const}; use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef}; +use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; @@ -186,7 +187,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// Returns the types of the fields for a given constructor. The result must have a length of /// `ctor.arity()`. - #[instrument(level = "trace", skip(self))] pub(crate) fn ctor_sub_tys<'a>( &'a self, ctor: &'a Constructor<'p, 'tcx>, @@ -247,7 +247,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) - | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[], + | Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[], Or => { bug!("called `Fields::wildcards` on an `Or` ctor") } @@ -275,7 +275,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { Ref => 1, Slice(slice) => slice.arity(), Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..) - | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0, + | Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0, Or => bug!("The `Or` constructor doesn't have a fixed arity"), } } @@ -283,7 +283,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// Creates a set that represents all the constructors of `ty`. /// /// See [`crate::constructor`] for considerations of emptiness. - #[instrument(level = "debug", skip(self), ret)] pub fn ctors_for_ty( &self, ty: RevealedTy<'tcx>, @@ -398,9 +397,10 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ty::Float(_) | ty::Str | ty::Foreign(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) + | ty::Pat(_, _) | ty::Dynamic(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -462,6 +462,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), }; } + PatKind::DerefPattern { .. } => { + // FIXME(deref_patterns): At least detect that `box _` is irrefutable. + fields = vec![]; + arity = 0; + ctor = Opaque(OpaqueId::new()); + } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { match ty.kind() { ty::Tuple(fs) => { @@ -824,7 +830,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } &Str(value) => PatKind::Constant { value }, - Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild, + Never if self.tcx.features().never_patterns => PatKind::Never, + Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild, Missing { .. } => bug!( "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, `Missing` should have been processed in `apply_constructors`" @@ -873,13 +880,14 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { fn write_variant_name( f: &mut fmt::Formatter<'_>, - pat: &crate::pat::DeconstructedPat, + ctor: &crate::constructor::Constructor, + ty: &Self::Ty, ) -> fmt::Result { - if let ty::Adt(adt, _) = pat.ty().kind() { + if let ty::Adt(adt, _) = ty.kind() { if adt.is_box() { write!(f, "Box")? } else { - let variant = adt.variant(Self::variant_index_for_adt(pat.ctor(), *adt)); + let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt)); write!(f, "{}", variant.name)?; } } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 3760db8b68894..74af9154f8581 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -709,16 +709,15 @@ //! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific //! reason not to, for example if they crucially depend on a particular feature like `or_patterns`. +use self::PlaceValidity::*; +use crate::constructor::{Constructor, ConstructorSet, IntRange}; +use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat}; +use crate::{Captures, MatchArm, PatCx, PrivateUninhabitedField}; use rustc_hash::FxHashSet; use rustc_index::bit_set::BitSet; use smallvec::{smallvec, SmallVec}; use std::fmt; - -use crate::constructor::{Constructor, ConstructorSet, IntRange}; -use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat}; -use crate::{Captures, MatchArm, PatCx, PrivateUninhabitedField}; - -use self::PlaceValidity::*; +use tracing::{debug, instrument}; #[cfg(feature = "rustc")] use rustc_data_structures::stack::ensure_sufficient_stack; @@ -871,12 +870,14 @@ impl PlaceInfo { where Cx: 'a, { + debug!(?self.ty); if self.private_uninhabited { // Skip the whole column return Ok((smallvec![Constructor::PrivateUninhabited], vec![])); } let ctors_for_ty = cx.ctors_for_ty(&self.ty)?; + debug!(?ctors_for_ty); // We treat match scrutinees of type `!` or `EmptyEnum` differently. let is_toplevel_exception = @@ -895,6 +896,7 @@ impl PlaceInfo { // Analyze the constructors present in this column. let mut split_set = ctors_for_ty.split(ctors); + debug!(?split_set); let all_missing = split_set.present.is_empty(); // Build the set of constructors we will specialize with. It must cover the whole type, so @@ -1042,7 +1044,7 @@ struct MatrixRow<'p, Cx: PatCx> { is_under_guard: bool, /// When we specialize, we remember which row of the original matrix produced a given row of the /// specialized matrix. When we unspecialize, we use this to propagate usefulness back up the - /// callstack. + /// callstack. On creation, this stores the index of the original match arm. parent_row: usize, /// False when the matrix is just built. This is set to `true` by /// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful. @@ -1163,10 +1165,10 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> { place_info: smallvec![place_info], wildcard_row_is_relevant: true, }; - for (row_id, arm) in arms.iter().enumerate() { + for (arm_id, arm) in arms.iter().enumerate() { let v = MatrixRow { pats: PatStack::from_pattern(arm.pat), - parent_row: row_id, // dummy, we don't read it + parent_row: arm_id, is_under_guard: arm.has_guard, useful: false, intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`. @@ -1254,7 +1256,7 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> { /// + true + [Second(true)] + /// + false + [_] + /// + _ + [_, _, tail @ ..] + -/// | ✓ | ? | // column validity +/// | ✓ | ? | // validity /// ``` impl<'p, Cx: PatCx> fmt::Debug for Matrix<'p, Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1285,7 +1287,7 @@ impl<'p, Cx: PatCx> fmt::Debug for Matrix<'p, Cx> { write!(f, " {sep}")?; } if is_validity_row { - write!(f, " // column validity")?; + write!(f, " // validity")?; } write!(f, "\n")?; } @@ -1381,12 +1383,35 @@ impl WitnessStack { /// pats: [(false, "foo"), _, true] /// result: [Enum::Variant { a: (false, "foo"), b: _ }, true] /// ``` - fn apply_constructor(&mut self, pcx: &PlaceCtxt<'_, Cx>, ctor: &Constructor) { + fn apply_constructor( + mut self, + pcx: &PlaceCtxt<'_, Cx>, + ctor: &Constructor, + ) -> SmallVec<[Self; 1]> { let len = self.0.len(); let arity = pcx.ctor_arity(ctor); - let fields = self.0.drain((len - arity)..).rev().collect(); - let pat = WitnessPat::new(ctor.clone(), fields, pcx.ty.clone()); - self.0.push(pat); + let fields: Vec<_> = self.0.drain((len - arity)..).rev().collect(); + if matches!(ctor, Constructor::UnionField) + && fields.iter().filter(|p| !matches!(p.ctor(), Constructor::Wildcard)).count() >= 2 + { + // Convert a `Union { a: p, b: q }` witness into `Union { a: p }` and `Union { b: q }`. + // First add `Union { .. }` to `self`. + self.0.push(WitnessPat::wild_from_ctor(pcx.cx, ctor.clone(), pcx.ty.clone())); + fields + .into_iter() + .enumerate() + .filter(|(_, p)| !matches!(p.ctor(), Constructor::Wildcard)) + .map(|(i, p)| { + let mut ret = self.clone(); + // Fill the `i`th field of the union with `p`. + ret.0.last_mut().unwrap().fields[i] = p; + ret + }) + .collect() + } else { + self.0.push(WitnessPat::new(ctor.clone(), fields, pcx.ty.clone())); + smallvec![self] + } } } @@ -1459,8 +1484,8 @@ impl WitnessMatrix { *self = ret; } else { // Any other constructor we unspecialize as expected. - for witness in self.0.iter_mut() { - witness.apply_constructor(pcx, ctor) + for witness in std::mem::take(&mut self.0) { + self.0.extend(witness.apply_constructor(pcx, ctor)); } } } @@ -1617,7 +1642,6 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( }; // Analyze the constructors present in this column. - debug!("ty: {:?}", place.ty); let ctors = matrix.heads().map(|p| p.ctor()); let (split_ctors, missing_ctors) = place.split_column_ctors(mcx.tycx, ctors)?; @@ -1669,7 +1693,10 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( for row in matrix.rows() { if row.useful { if let PatOrWild::Pat(pat) = row.head() { - mcx.useful_subpatterns.insert(pat.uid); + let newly_useful = mcx.useful_subpatterns.insert(pat.uid); + if newly_useful { + debug!("newly useful: {pat:?}"); + } } } } @@ -1738,6 +1765,9 @@ pub struct UsefulnessReport<'p, Cx: PatCx> { /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. pub non_exhaustiveness_witnesses: Vec>, + /// For each arm, a set of indices of arms above it that have non-empty intersection, i.e. there + /// is a value matched by both arms. This may miss real intersections. + pub arm_intersections: Vec>, } /// Computes whether a match is exhaustive and which of its arms are useful. @@ -1765,9 +1795,24 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( .map(|arm| { debug!(?arm); let usefulness = collect_pattern_usefulness(&cx.useful_subpatterns, arm.pat); + debug!(?usefulness); (arm, usefulness) }) .collect(); - Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }) + let mut arm_intersections: Vec<_> = + arms.iter().enumerate().map(|(i, _)| BitSet::new_empty(i)).collect(); + for row in matrix.rows() { + let arm_id = row.parent_row; + for intersection in row.intersects.iter() { + // Convert the matrix row ids into arm ids (they can differ because we expand or-patterns). + let arm_intersection = matrix.rows[intersection].parent_row; + // Note: self-intersection can happen with or-patterns. + if arm_intersection != arm_id { + arm_intersections[arm_id].insert(arm_intersection); + } + } + } + + Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, arm_intersections }) } diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs new file mode 100644 index 0000000000000..e72fddb9e9a81 --- /dev/null +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -0,0 +1,315 @@ +use rustc_pattern_analysis::{ + constructor::{ + Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility, + }, + usefulness::{PlaceValidity, UsefulnessReport}, + Captures, MatchArm, PatCx, PrivateUninhabitedField, +}; + +/// Sets up `tracing` for easier debugging. Tries to look like the `rustc` setup. +pub fn init_tracing() { + use tracing_subscriber::layer::SubscriberExt; + use tracing_subscriber::util::SubscriberInitExt; + use tracing_subscriber::Layer; + let _ = tracing_tree::HierarchicalLayer::default() + .with_writer(std::io::stderr) + .with_indent_lines(true) + .with_ansi(true) + .with_targets(true) + .with_indent_amount(2) + .with_subscriber( + tracing_subscriber::Registry::default() + .with(tracing_subscriber::EnvFilter::from_default_env()), + ) + .try_init(); +} + +/// A simple set of types. +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum Ty { + /// Booleans + Bool, + /// 8-bit unsigned integers + U8, + /// Tuples. + Tuple(&'static [Ty]), + /// A struct with `arity` fields of type `ty`. + BigStruct { arity: usize, ty: &'static Ty }, + /// A enum with `arity` variants of type `ty`. + BigEnum { arity: usize, ty: &'static Ty }, +} + +/// The important logic. +impl Ty { + pub fn sub_tys(&self, ctor: &Constructor) -> Vec { + use Constructor::*; + match (ctor, *self) { + (Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(), + (Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(), + (Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty], + (Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![], + _ => panic!("Unexpected ctor {ctor:?} for type {self:?}"), + } + } + + pub fn ctor_set(&self) -> ConstructorSet { + match *self { + Ty::Bool => ConstructorSet::Bool, + Ty::U8 => ConstructorSet::Integers { + range_1: IntRange::from_range( + MaybeInfiniteInt::new_finite_uint(0), + MaybeInfiniteInt::new_finite_uint(255), + RangeEnd::Included, + ), + range_2: None, + }, + Ty::Tuple(..) | Ty::BigStruct { .. } => ConstructorSet::Struct { empty: false }, + Ty::BigEnum { arity, .. } => ConstructorSet::Variants { + variants: (0..arity).map(|_| VariantVisibility::Visible).collect(), + non_exhaustive: false, + }, + } + } + + pub fn write_variant_name( + &self, + f: &mut std::fmt::Formatter<'_>, + ctor: &Constructor, + ) -> std::fmt::Result { + match (*self, ctor) { + (Ty::Tuple(..), _) => Ok(()), + (Ty::BigStruct { .. }, _) => write!(f, "BigStruct"), + (Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"), + _ => write!(f, "{:?}::{:?}", self, ctor), + } + } +} + +/// Compute usefulness in our simple context (and set up tracing for easier debugging). +pub fn compute_match_usefulness<'p>( + arms: &[MatchArm<'p, Cx>], + ty: Ty, + scrut_validity: PlaceValidity, + complexity_limit: Option, +) -> Result, ()> { + init_tracing(); + rustc_pattern_analysis::usefulness::compute_match_usefulness( + &Cx, + arms, + ty, + scrut_validity, + complexity_limit, + ) +} + +#[derive(Debug)] +pub struct Cx; + +/// The context for pattern analysis. Forwards anything interesting to `Ty` methods. +impl PatCx for Cx { + type Ty = Ty; + type Error = (); + type VariantIdx = usize; + type StrLit = (); + type ArmData = (); + type PatData = (); + + fn is_exhaustive_patterns_feature_on(&self) -> bool { + false + } + + fn is_min_exhaustive_patterns_feature_on(&self) -> bool { + false + } + + fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize { + ty.sub_tys(ctor).len() + } + + fn ctor_sub_tys<'a>( + &'a self, + ctor: &'a Constructor, + ty: &'a Self::Ty, + ) -> impl Iterator + ExactSizeIterator + Captures<'a> + { + ty.sub_tys(ctor).into_iter().map(|ty| (ty, PrivateUninhabitedField(false))) + } + + fn ctors_for_ty(&self, ty: &Self::Ty) -> Result, Self::Error> { + Ok(ty.ctor_set()) + } + + fn write_variant_name( + f: &mut std::fmt::Formatter<'_>, + ctor: &Constructor, + ty: &Self::Ty, + ) -> std::fmt::Result { + ty.write_variant_name(f, ctor) + } + + fn bug(&self, fmt: std::fmt::Arguments<'_>) -> Self::Error { + panic!("{}", fmt) + } + + /// Abort when reaching the complexity limit. This is what we'll check in tests. + fn complexity_exceeded(&self) -> Result<(), Self::Error> { + Err(()) + } +} + +/// Construct a single pattern; see `pats!()`. +#[allow(unused_macros)] +macro_rules! pat { + ($($rest:tt)*) => {{ + let mut vec = pats!($($rest)*); + vec.pop().unwrap() + }}; +} + +/// A macro to construct patterns. Called like `pats!(type_expr; pattern, pattern, ..)` and returns +/// a `Vec`. A pattern can be nested and looks like `Constructor(pat, pat)` or +/// `Constructor { .i: pat, .j: pat }`, where `Constructor` is `Struct`, `Variant.i` (with index +/// `i`), as well as booleans and integer ranges. +/// +/// The general structure of the macro is a tt-muncher with several stages identified with +/// `@something(args)`. The args are a key-value list (the keys ensure we don't mix the arguments +/// around) which is passed down and modified as needed. We then parse token-trees from +/// left-to-right. Non-trivial recursion happens when we parse the arguments to a pattern: we +/// recurse to parse the tokens inside `{..}`/`(..)`, and then we continue parsing anything that +/// follows. +macro_rules! pats { + // Entrypoint + // Parse `type; ..` + ($ty:expr; $($rest:tt)*) => {{ + #[allow(unused_imports)] + use rustc_pattern_analysis::{ + constructor::{Constructor, IntRange, MaybeInfiniteInt, RangeEnd}, + pat::DeconstructedPat, + }; + let ty = $ty; + // The heart of the macro is designed to push `IndexedPat`s into a `Vec`, so we work around + // that. + let sub_tys = ::std::iter::repeat(&ty); + let mut vec = Vec::new(); + pats!(@ctor(vec:vec, sub_tys:sub_tys, idx:0) $($rest)*); + vec.into_iter().map(|ipat| ipat.pat).collect::>() + }}; + + // Parse `constructor ..` + + (@ctor($($args:tt)*) true $($rest:tt)*) => {{ + let ctor = Constructor::Bool(true); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) false $($rest:tt)*) => {{ + let ctor = Constructor::Bool(false); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) Struct $($rest:tt)*) => {{ + let ctor = Constructor::Struct; + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) ( $($fields:tt)* ) $($rest:tt)*) => {{ + let ctor = Constructor::Struct; // tuples + pats!(@pat($($args)*, ctor:ctor) ( $($fields)* ) $($rest)*) + }}; + (@ctor($($args:tt)*) Variant.$variant:ident $($rest:tt)*) => {{ + let ctor = Constructor::Variant($variant); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) Variant.$variant:literal $($rest:tt)*) => {{ + let ctor = Constructor::Variant($variant); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) _ $($rest:tt)*) => {{ + let ctor = Constructor::Wildcard; + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + + // Integers and int ranges + (@ctor($($args:tt)*) $($start:literal)?..$end:literal $($rest:tt)*) => {{ + let ctor = Constructor::IntRange(IntRange::from_range( + pats!(@rangeboundary- $($start)?), + pats!(@rangeboundary+ $end), + RangeEnd::Excluded, + )); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) $($start:literal)?.. $($rest:tt)*) => {{ + let ctor = Constructor::IntRange(IntRange::from_range( + pats!(@rangeboundary- $($start)?), + pats!(@rangeboundary+), + RangeEnd::Excluded, + )); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) $($start:literal)?..=$end:literal $($rest:tt)*) => {{ + let ctor = Constructor::IntRange(IntRange::from_range( + pats!(@rangeboundary- $($start)?), + pats!(@rangeboundary+ $end), + RangeEnd::Included, + )); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) $int:literal $($rest:tt)*) => {{ + let ctor = Constructor::IntRange(IntRange::from_range( + pats!(@rangeboundary- $int), + pats!(@rangeboundary+ $int), + RangeEnd::Included, + )); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + // Utility to manage range boundaries. + (@rangeboundary $sign:tt $int:literal) => { MaybeInfiniteInt::new_finite_uint($int) }; + (@rangeboundary -) => { MaybeInfiniteInt::NegInfinity }; + (@rangeboundary +) => { MaybeInfiniteInt::PosInfinity }; + + // Parse subfields: `(..)` or `{..}` + + // Constructor with no fields, e.g. `bool` or `Variant.1`. + (@pat($($args:tt)*) $(,)?) => { + pats!(@pat($($args)*) {}) + }; + (@pat($($args:tt)*) , $($rest:tt)*) => { + pats!(@pat($($args)*) {}, $($rest)*) + }; + // `(..)` and `{..}` are treated the same. + (@pat($($args:tt)*) ( $($subpat:tt)* ) $($rest:tt)*) => {{ + pats!(@pat($($args)*) { $($subpat)* } $($rest)*) + }}; + (@pat(vec:$vec:expr, sub_tys:$sub_tys:expr, idx:$idx:expr, ctor:$ctor:expr) { $($fields:tt)* } $($rest:tt)*) => {{ + let sub_tys = $sub_tys; + let index = $idx; + // Silly dance to work with both a vec and `iter::repeat()`. + let ty = *(&sub_tys).clone().into_iter().nth(index).unwrap(); + let ctor = $ctor; + let ctor_sub_tys = &ty.sub_tys(&ctor); + #[allow(unused_mut)] + let mut fields = Vec::new(); + // Parse subpatterns (note the leading comma). + pats!(@fields(idx:0, vec:fields, sub_tys:ctor_sub_tys) ,$($fields)*); + let arity = ctor_sub_tys.len(); + let pat = DeconstructedPat::new(ctor, fields, arity, ty, ()).at_index(index); + $vec.push(pat); + + // Continue parsing further patterns. + pats!(@fields(idx:index+1, vec:$vec, sub_tys:sub_tys) $($rest)*); + }}; + + // Parse fields one by one. + + // No fields left. + (@fields($($args:tt)*) $(,)?) => {}; + // `.i: pat` sets the current index to `i`. + (@fields(idx:$_idx:expr, $($args:tt)*) , .$idx:literal : $($rest:tt)*) => {{ + pats!(@ctor($($args)*, idx:$idx) $($rest)*); + }}; + (@fields(idx:$_idx:expr, $($args:tt)*) , .$idx:ident : $($rest:tt)*) => {{ + pats!(@ctor($($args)*, idx:$idx) $($rest)*); + }}; + // Field without an explicit index; we use the current index which gets incremented above. + (@fields(idx:$idx:expr, $($args:tt)*) , $($rest:tt)*) => {{ + pats!(@ctor($($args)*, idx:$idx) $($rest)*); + }}; +} diff --git a/compiler/rustc_pattern_analysis/tests/complexity.rs b/compiler/rustc_pattern_analysis/tests/complexity.rs new file mode 100644 index 0000000000000..93f455c6257d2 --- /dev/null +++ b/compiler/rustc_pattern_analysis/tests/complexity.rs @@ -0,0 +1,109 @@ +//! Test the pattern complexity limit. +use common::*; +use rustc_pattern_analysis::{pat::DeconstructedPat, usefulness::PlaceValidity, MatchArm}; + +#[macro_use] +mod common; + +/// Analyze a match made of these patterns. Ignore the report; we only care whether we exceeded the +/// limit or not. +fn check(patterns: &[DeconstructedPat], complexity_limit: usize) -> Result<(), ()> { + let ty = *patterns[0].ty(); + let arms: Vec<_> = + patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); + compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, Some(complexity_limit)) + .map(|_report| ()) +} + +/// Asserts that analyzing this match takes exactly `complexity` steps. +#[track_caller] +fn assert_complexity(patterns: Vec>, complexity: usize) { + assert!(check(&patterns, complexity).is_ok()); + assert!(check(&patterns, complexity - 1).is_err()); +} + +/// Construct a match like: +/// ```ignore(illustrative) +/// match ... { +/// BigStruct { field01: true, .. } => {} +/// BigStruct { field02: true, .. } => {} +/// BigStruct { field03: true, .. } => {} +/// BigStruct { field04: true, .. } => {} +/// ... +/// _ => {} +/// } +/// ``` +fn diagonal_match(arity: usize) -> Vec> { + let struct_ty = Ty::BigStruct { arity, ty: &Ty::Bool }; + let mut patterns = vec![]; + for i in 0..arity { + patterns.push(pat!(struct_ty; Struct { .i: true })); + } + patterns.push(pat!(struct_ty; _)); + patterns +} + +/// Construct a match like: +/// ```ignore(illustrative) +/// match ... { +/// BigStruct { field01: true, .. } => {} +/// BigStruct { field02: true, .. } => {} +/// BigStruct { field03: true, .. } => {} +/// BigStruct { field04: true, .. } => {} +/// ... +/// BigStruct { field01: false, .. } => {} +/// BigStruct { field02: false, .. } => {} +/// BigStruct { field03: false, .. } => {} +/// BigStruct { field04: false, .. } => {} +/// ... +/// _ => {} +/// } +/// ``` +fn diagonal_exponential_match(arity: usize) -> Vec> { + let struct_ty = Ty::BigStruct { arity, ty: &Ty::Bool }; + let mut patterns = vec![]; + for i in 0..arity { + patterns.push(pat!(struct_ty; Struct { .i: true })); + } + for i in 0..arity { + patterns.push(pat!(struct_ty; Struct { .i: false })); + } + patterns.push(pat!(struct_ty; _)); + patterns +} + +#[test] +fn test_diagonal_struct_match() { + // These cases are nicely linear: we check `arity` patterns with exactly one `true`, matching + // in 2 branches each, and a final pattern with all `false`, matching only the `_` branch. + assert_complexity(diagonal_match(20), 41); + assert_complexity(diagonal_match(30), 61); + // This case goes exponential. + assert!(check(&diagonal_exponential_match(10), 10000).is_err()); +} + +/// Construct a match like: +/// ```ignore(illustrative) +/// match ... { +/// BigEnum::Variant1(_) => {} +/// BigEnum::Variant2(_) => {} +/// BigEnum::Variant3(_) => {} +/// ... +/// _ => {} +/// } +/// ``` +fn big_enum(arity: usize) -> Vec> { + let enum_ty = Ty::BigEnum { arity, ty: &Ty::Bool }; + let mut patterns = vec![]; + for i in 0..arity { + patterns.push(pat!(enum_ty; Variant.i)); + } + patterns.push(pat!(enum_ty; _)); + patterns +} + +#[test] +fn test_big_enum() { + // We try 2 branches per variant. + assert_complexity(big_enum(20), 40); +} diff --git a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs new file mode 100644 index 0000000000000..4c6c72fa8ecbc --- /dev/null +++ b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs @@ -0,0 +1,77 @@ +//! Test exhaustiveness checking. +use common::*; +use rustc_pattern_analysis::{ + pat::{DeconstructedPat, WitnessPat}, + usefulness::PlaceValidity, + MatchArm, +}; + +#[macro_use] +mod common; + +/// Analyze a match made of these patterns. +fn check(patterns: Vec>) -> Vec> { + let ty = *patterns[0].ty(); + let arms: Vec<_> = + patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); + let report = + compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, None).unwrap(); + report.non_exhaustiveness_witnesses +} + +#[track_caller] +fn assert_exhaustive(patterns: Vec>) { + let witnesses = check(patterns); + if !witnesses.is_empty() { + panic!("non-exaustive match: missing {witnesses:?}"); + } +} + +#[track_caller] +fn assert_non_exhaustive(patterns: Vec>) { + let witnesses = check(patterns); + assert!(!witnesses.is_empty()) +} + +#[test] +fn test_int_ranges() { + let ty = Ty::U8; + assert_exhaustive(pats!(ty; + 0..=255, + )); + assert_exhaustive(pats!(ty; + 0.., + )); + assert_non_exhaustive(pats!(ty; + 0..255, + )); + assert_exhaustive(pats!(ty; + 0..255, + 255, + )); + assert_exhaustive(pats!(ty; + ..10, + 10.. + )); +} + +#[test] +fn test_nested() { + let ty = Ty::BigStruct { arity: 2, ty: &Ty::BigEnum { arity: 2, ty: &Ty::Bool } }; + assert_non_exhaustive(pats!(ty; + Struct(Variant.0, _), + )); + assert_exhaustive(pats!(ty; + Struct(Variant.0, _), + Struct(Variant.1, _), + )); + assert_non_exhaustive(pats!(ty; + Struct(Variant.0, _), + Struct(_, Variant.0), + )); + assert_exhaustive(pats!(ty; + Struct(Variant.0, _), + Struct(_, Variant.0), + Struct(Variant.1, Variant.1), + )); +} diff --git a/compiler/rustc_pattern_analysis/tests/intersection.rs b/compiler/rustc_pattern_analysis/tests/intersection.rs new file mode 100644 index 0000000000000..4d8a21506d760 --- /dev/null +++ b/compiler/rustc_pattern_analysis/tests/intersection.rs @@ -0,0 +1,69 @@ +//! Test the computation of arm intersections. +use common::*; +use rustc_pattern_analysis::{pat::DeconstructedPat, usefulness::PlaceValidity, MatchArm}; + +#[macro_use] +mod common; + +/// Analyze a match made of these patterns and returns the computed arm intersections. +fn check(patterns: Vec>) -> Vec> { + let ty = *patterns[0].ty(); + let arms: Vec<_> = + patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); + let report = + compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, None).unwrap(); + report.arm_intersections.into_iter().map(|bitset| bitset.iter().collect()).collect() +} + +#[track_caller] +fn assert_intersects(patterns: Vec>, intersects: &[&[usize]]) { + let computed_intersects = check(patterns); + assert_eq!(computed_intersects, intersects); +} + +#[test] +fn test_int_ranges() { + let ty = Ty::U8; + assert_intersects( + pats!(ty; + 0..=100, + 100.., + ), + &[&[], &[0]], + ); + assert_intersects( + pats!(ty; + 0..=101, + 100.., + ), + &[&[], &[0]], + ); + assert_intersects( + pats!(ty; + 0..100, + 100.., + ), + &[&[], &[]], + ); +} + +#[test] +fn test_nested() { + let ty = Ty::Tuple(&[Ty::Bool; 2]); + assert_intersects( + pats!(ty; + (true, true), + (true, _), + (_, true), + ), + &[&[], &[0], &[0, 1]], + ); + // Here we shortcut because `(true, true)` is irrelevant, so we fail to detect the intersection. + assert_intersects( + pats!(ty; + (true, _), + (_, true), + ), + &[&[], &[]], + ); +} diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 073982ca5c37d..a78f7e65981ab 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -6,9 +6,6 @@ #![feature(try_blocks)] #![feature(let_chains)] -#[macro_use] -extern crate tracing; - mod errors; use rustc_ast::visit::{try_visit, VisitorResult}; @@ -31,6 +28,7 @@ use rustc_session::lint; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; +use tracing::debug; use std::fmt; use std::marker::PhantomData; @@ -276,6 +274,7 @@ where | ty::Tuple(..) | ty::RawPtr(..) | ty::Ref(..) + | ty::Pat(..) | ty::FnPtr(..) | ty::Param(..) | ty::Bound(..) @@ -1209,7 +1208,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { intravisit::walk_pat(self, pattern); } - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { if let Some(init) = local.init { if self.check_expr_pat_type(init.hir_id, init.span) { // Do not report duplicate errors for `let x = y`. @@ -1696,7 +1695,7 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { } } - for id in module.items() { + for id in module.free_items() { if let ItemKind::Impl(i) = tcx.hir().item(id).kind { if let Some(item) = i.of_trait { let trait_ref = tcx.impl_trait_ref(id.owner_id.def_id).unwrap(); diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 5276715553272..914481d712e8e 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -3,7 +3,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature(generic_nonzero)] #![feature(min_specialization)] #![feature(rustc_attrs)] #![allow(rustc::potential_query_instability, unused_parens)] @@ -13,6 +12,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 +21,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 fbc6db93e01ad..e0d7a4f0451a2 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, +} + +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/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 17f96896a504b..5f1a03502a70f 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -49,6 +49,7 @@ use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; use rustc_data_structures::AtomicRef; use rustc_hir::definitions::DefPathHash; +use rustc_macros::{Decodable, Encodable}; use std::fmt; use std::hash::Hash; @@ -75,8 +76,6 @@ impl DepKind { } } -static_assert_size!(DepKind, 2); - pub fn default_dep_kind_debug(kind: DepKind, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DepKind").field("variant", &kind.variant).finish() } @@ -96,15 +95,6 @@ pub struct DepNode { pub hash: PackedFingerprint, } -// We keep a lot of `DepNode`s in memory during compilation. It's not -// required that their size stay the same, but we don't want to change -// it inadvertently. This assert just ensures we're aware of any change. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -static_assert_size!(DepNode, 18); - -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -static_assert_size!(DepNode, 24); - impl DepNode { /// Creates a new, parameterless DepNode. This method will assert /// that the DepNode corresponding to the given DepKind actually @@ -285,8 +275,7 @@ pub struct DepKindStruct { /// some independent path or string that persists between runs without /// the need to be mapped or unmapped. (This ensures we can serialize /// them even in the absence of a tcx.) -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(Encodable, Decodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] pub struct WorkProductId { hash: Fingerprint, } @@ -316,3 +305,17 @@ unsafe impl StableOrd for WorkProductId { // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well const CAN_USE_UNSTABLE_SORT: bool = true; } + +// Some types are used a lot. Make sure they don't unintentionally get bigger. +#[cfg(target_pointer_width = "64")] +mod size_asserts { + use super::*; + use rustc_data_structures::static_assert_size; + // tidy-alphabetical-start + static_assert_size!(DepKind, 2); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + static_assert_size!(DepNode, 18); + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + static_assert_size!(DepNode, 24); + // tidy-alphabetical-end +} diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 9f067273f3580..76227a78c3d68 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -6,6 +6,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc}; use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; +use rustc_macros::{Decodable, Encodable}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; @@ -13,6 +14,8 @@ use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; use std::sync::atomic::Ordering; +use std::sync::Arc; +use tracing::{debug, instrument}; use super::query::DepGraphQuery; use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex}; @@ -39,8 +42,13 @@ rustc_index::newtype_index! { pub struct DepNodeIndex {} } +// We store a large collection of these in `prev_index_to_index` during +// non-full incremental builds, and want to ensure that the element size +// doesn't inadvertently increase. +rustc_data_structures::static_assert_size!(Option, 4); + impl DepNodeIndex { - const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::from_u32(0); + const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::ZERO; pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1); } @@ -81,7 +89,7 @@ pub(crate) struct DepGraphData { /// The dep-graph from the previous compilation session. It contains all /// nodes and edges as well as all fingerprints of nodes that have them. - previous: SerializedDepGraph, + previous: Arc, colors: DepNodeColorMap, @@ -113,7 +121,7 @@ where impl DepGraph { pub fn new( profiler: &SelfProfilerRef, - prev_graph: SerializedDepGraph, + prev_graph: Arc, prev_work_products: WorkProductMap, encoder: FileEncoder, record_graph: bool, @@ -127,6 +135,7 @@ impl DepGraph { encoder, record_graph, record_stats, + prev_graph.clone(), ); let colors = DepNodeColorMap::new(prev_graph_node_count); @@ -457,7 +466,8 @@ impl DepGraph { } TaskDepsRef::Ignore => return, TaskDepsRef::Forbid => { - panic!("Illegal read of: {dep_node_index:?}") + // Reading is forbidden in this context. ICE with a useful error message. + panic_on_forbidden_read(data, dep_node_index) } }; let task_deps = &mut *task_deps; @@ -1084,6 +1094,7 @@ impl CurrentDepGraph { encoder: FileEncoder, record_graph: bool, record_stats: bool, + previous: Arc, ) -> Self { use std::time::{SystemTime, UNIX_EPOCH}; @@ -1102,11 +1113,6 @@ impl CurrentDepGraph { Err(_) => None, }; - // We store a large collection of these in `prev_index_to_index` during - // non-full incremental builds, and want to ensure that the element size - // doesn't inadvertently increase. - static_assert_size!(Option, 4); - let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200; CurrentDepGraph { @@ -1116,6 +1122,7 @@ impl CurrentDepGraph { record_graph, record_stats, profiler, + previous, ), new_node_to_index: Sharded::new(|| { FxHashMap::with_capacity_and_hasher( @@ -1236,16 +1243,14 @@ impl CurrentDepGraph { match prev_index_to_index[prev_index] { Some(dep_node_index) => dep_node_index, None => { - let key = prev_graph.index_to_node(prev_index); - let edges = prev_graph - .edge_targets_from(prev_index) - .map(|i| prev_index_to_index[i].unwrap()) - .collect(); - let fingerprint = prev_graph.fingerprint_by_index(prev_index); - let dep_node_index = self.encoder.send(key, fingerprint, edges); + let dep_node_index = self.encoder.send_promoted(prev_index, &*prev_index_to_index); prev_index_to_index[prev_index] = Some(dep_node_index); #[cfg(debug_assertions)] - self.record_edge(dep_node_index, key, fingerprint); + self.record_edge( + dep_node_index, + prev_graph.index_to_node(prev_index), + prev_graph.fingerprint_by_index(prev_index), + ); dep_node_index } } @@ -1364,3 +1369,45 @@ pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: Option< eprintln!("end of try_mark_green dep node stack"); } + +#[cold] +#[inline(never)] +fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepNodeIndex) -> ! { + // We have to do an expensive reverse-lookup of the DepNode that + // corresponds to `dep_node_index`, but that's OK since we are about + // to ICE anyway. + let mut dep_node = None; + + // First try to find the dep node among those that already existed in the + // previous session + for (prev_index, index) in data.current.prev_index_to_index.lock().iter_enumerated() { + if index == &Some(dep_node_index) { + dep_node = Some(data.previous.index_to_node(prev_index)); + break; + } + } + + if dep_node.is_none() { + // Try to find it among the new nodes + for shard in data.current.new_node_to_index.lock_shards() { + if let Some((node, _)) = shard.iter().find(|(_, index)| **index == dep_node_index) { + dep_node = Some(*node); + break; + } + } + } + + let dep_node = dep_node.map_or_else( + || format!("with index {:?}", dep_node_index), + |dep_node| format!("`{:?}`", dep_node), + ); + + panic!( + "Error: trying to record dependency on DepNode {dep_node} in a \ + context that does not allow it (e.g. during query deserialization). \ + The most common case of recording a dependency on a DepNode `foo` is \ + when the correspondng query `foo` is invoked. Invoking queries is not \ + allowed as part of loading something from the incremental on-disk cache. \ + See ." + ) +} diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index feb69ecd07867..cbd8029588701 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -11,13 +11,12 @@ pub use graph::{hash_result, DepGraph, DepNodeIndex, TaskDepsRef, WorkProduct, W pub use query::DepGraphQuery; pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; +use self::graph::{print_markframe_trace, MarkFrame}; use crate::ich::StableHashingContext; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_session::Session; - use std::panic; - -use self::graph::{print_markframe_trace, MarkFrame}; +use tracing::instrument; pub trait DepContext: Copy { type Deps: Deps; diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 0c6a63582931e..b426bb888f460 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -41,6 +41,7 @@ use crate::dep_graph::edges::EdgesVec; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fingerprint::PackedFingerprint; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::outline; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; @@ -49,6 +50,8 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixed use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::iter; use std::marker::PhantomData; +use std::sync::Arc; +use tracing::{debug, instrument}; // The maximum value of `SerializedDepNodeIndex` leaves the upper two bits // unused so that we can store multiple index types in `CompressedHybridIndex`, @@ -94,7 +97,7 @@ impl SerializedDepGraph { pub fn edge_targets_from( &self, source: SerializedDepNodeIndex, - ) -> impl Iterator + '_ { + ) -> impl Iterator + Clone + '_ { let header = self.edge_list_indices[source]; let mut raw = &self.edge_list_data[header.start()..]; // Figure out where the edge list for `source` ends by getting the start index of the next @@ -176,7 +179,7 @@ fn mask(bits: usize) -> usize { impl SerializedDepGraph { #[instrument(level = "debug", skip(d))] - pub fn decode(d: &mut MemDecoder<'_>) -> SerializedDepGraph { + pub fn decode(d: &mut MemDecoder<'_>) -> Arc { // The last 16 bytes are the node count and edge count. debug!("position: {:?}", d.position()); let (node_count, edge_count, graph_size) = @@ -254,7 +257,13 @@ impl SerializedDepGraph { index[node.kind.as_usize()].insert(node.hash, idx); } - SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data, index } + Arc::new(SerializedDepGraph { + nodes, + fingerprints, + edge_list_indices, + edge_list_data, + index, + }) } } @@ -299,21 +308,24 @@ impl SerializedNodeHeader { const MAX_INLINE_LEN: usize = (u16::MAX as usize >> (Self::TOTAL_BITS - Self::LEN_BITS)) - 1; #[inline] - fn new(node_info: &NodeInfo) -> Self { + fn new( + node: DepNode, + fingerprint: Fingerprint, + edge_max_index: u32, + edge_count: usize, + ) -> Self { debug_assert_eq!(Self::TOTAL_BITS, Self::LEN_BITS + Self::WIDTH_BITS + Self::KIND_BITS); - let NodeInfo { node, fingerprint, edges } = node_info; - let mut head = node.kind.as_inner(); - let free_bytes = edges.max_index().leading_zeros() as usize / 8; + let free_bytes = edge_max_index.leading_zeros() as usize / 8; let bytes_per_index = (DEP_NODE_SIZE - free_bytes).saturating_sub(1); head |= (bytes_per_index as u16) << Self::KIND_BITS; // Encode number of edges + 1 so that we can reserve 0 to indicate that the len doesn't fit // in this bitfield. - if edges.len() <= Self::MAX_INLINE_LEN { - head |= (edges.len() as u16 + 1) << (Self::KIND_BITS + Self::WIDTH_BITS); + if edge_count <= Self::MAX_INLINE_LEN { + head |= (edge_count as u16 + 1) << (Self::KIND_BITS + Self::WIDTH_BITS); } let hash: Fingerprint = node.hash.into(); @@ -327,10 +339,10 @@ impl SerializedNodeHeader { #[cfg(debug_assertions)] { let res = Self { bytes, _marker: PhantomData }; - assert_eq!(node_info.fingerprint, res.fingerprint()); - assert_eq!(node_info.node, res.node()); + assert_eq!(fingerprint, res.fingerprint()); + assert_eq!(node, res.node()); if let Some(len) = res.len() { - assert_eq!(node_info.edges.len(), len); + assert_eq!(edge_count, len); } } Self { bytes, _marker: PhantomData } @@ -393,21 +405,61 @@ struct NodeInfo { impl NodeInfo { fn encode(&self, e: &mut FileEncoder) { - let header = SerializedNodeHeader::::new(self); + let NodeInfo { node, fingerprint, ref edges } = *self; + let header = + SerializedNodeHeader::::new(node, fingerprint, edges.max_index(), edges.len()); e.write_array(header.bytes); if header.len().is_none() { - e.emit_usize(self.edges.len()); + e.emit_usize(edges.len()); } let bytes_per_index = header.bytes_per_index(); - for node_index in self.edges.iter() { + for node_index in edges.iter() { e.write_with(|dest| { *dest = node_index.as_u32().to_le_bytes(); bytes_per_index }); } } + + /// Encode a node that was promoted from the previous graph. It reads the edges directly from + /// the previous dep graph and expects all edges to already have a new dep node index assigned. + /// This avoids the overhead of constructing `EdgesVec`, which would be needed to call `encode`. + #[inline] + fn encode_promoted( + e: &mut FileEncoder, + node: DepNode, + fingerprint: Fingerprint, + prev_index: SerializedDepNodeIndex, + prev_index_to_index: &IndexVec>, + previous: &SerializedDepGraph, + ) -> usize { + let edges = previous.edge_targets_from(prev_index); + let edge_count = edges.size_hint().0; + + // Find the highest edge in the new dep node indices + let edge_max = + edges.clone().map(|i| prev_index_to_index[i].unwrap().as_u32()).max().unwrap_or(0); + + let header = SerializedNodeHeader::::new(node, fingerprint, edge_max, edge_count); + e.write_array(header.bytes); + + if header.len().is_none() { + e.emit_usize(edge_count); + } + + let bytes_per_index = header.bytes_per_index(); + for node_index in edges { + let node_index = prev_index_to_index[node_index].unwrap(); + e.write_with(|dest| { + *dest = node_index.as_u32().to_le_bytes(); + bytes_per_index + }); + } + + edge_count + } } struct Stat { @@ -417,6 +469,7 @@ struct Stat { } struct EncoderState { + previous: Arc, encoder: FileEncoder, total_node_count: usize, total_edge_count: usize, @@ -428,8 +481,9 @@ struct EncoderState { } impl EncoderState { - fn new(encoder: FileEncoder, record_stats: bool) -> Self { + fn new(encoder: FileEncoder, record_stats: bool, previous: Arc) -> Self { Self { + previous, encoder, total_edge_count: 0, total_node_count: 0, @@ -439,38 +493,96 @@ impl EncoderState { } } - fn encode_node( + #[inline] + fn record( &mut self, - node: &NodeInfo, + node: DepNode, + edge_count: usize, + edges: impl FnOnce(&mut Self) -> Vec, record_graph: &Option>, ) -> DepNodeIndex { let index = DepNodeIndex::new(self.total_node_count); - self.total_node_count += 1; - self.kind_stats[node.node.kind.as_usize()] += 1; - let edge_count = node.edges.len(); + self.total_node_count += 1; + self.kind_stats[node.kind.as_usize()] += 1; self.total_edge_count += edge_count; if let Some(record_graph) = &record_graph { - // Do not ICE when a query is called from within `with_query`. - if let Some(record_graph) = &mut record_graph.try_lock() { - record_graph.push(index, node.node, &node.edges); - } + // Call `edges` before the outlined code to allow the closure to be optimized out. + let edges = edges(self); + + // Outline the build of the full dep graph as it's typically disabled and cold. + outline(move || { + // Do not ICE when a query is called from within `with_query`. + if let Some(record_graph) = &mut record_graph.try_lock() { + record_graph.push(index, node, &edges); + } + }); } if let Some(stats) = &mut self.stats { - let kind = node.node.kind; - - let stat = stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 }); - stat.node_counter += 1; - stat.edge_counter += edge_count as u64; + let kind = node.kind; + + // Outline the stats code as it's typically disabled and cold. + outline(move || { + let stat = + stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 }); + stat.node_counter += 1; + stat.edge_counter += edge_count as u64; + }); } - let encoder = &mut self.encoder; - node.encode::(encoder); index } + /// Encodes a node to the current graph. + fn encode_node( + &mut self, + node: &NodeInfo, + record_graph: &Option>, + ) -> DepNodeIndex { + node.encode::(&mut self.encoder); + 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 + /// the previous dep graph for performance reasons. + /// + /// This differs from `encode_node` where you have to explictly provide the relevant `NodeInfo`. + /// + /// It expects all edges to already have a new dep node index assigned. + #[inline] + fn encode_promoted_node( + &mut self, + prev_index: SerializedDepNodeIndex, + record_graph: &Option>, + prev_index_to_index: &IndexVec>, + ) -> DepNodeIndex { + let node = self.previous.index_to_node(prev_index); + + let fingerprint = self.previous.fingerprint_by_index(prev_index); + let edge_count = NodeInfo::encode_promoted::( + &mut self.encoder, + node, + fingerprint, + prev_index, + prev_index_to_index, + &self.previous, + ); + + self.record( + node, + edge_count, + |this| { + this.previous + .edge_targets_from(prev_index) + .map(|i| prev_index_to_index[i].unwrap()) + .collect() + }, + record_graph, + ) + } + fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { let Self { mut encoder, @@ -479,6 +591,7 @@ impl EncoderState { stats: _, kind_stats, marker: _, + previous: _, } = self; let node_count = total_node_count.try_into().unwrap(); @@ -520,9 +633,10 @@ impl GraphEncoder { record_graph: bool, record_stats: bool, profiler: &SelfProfilerRef, + previous: Arc, ) -> Self { let record_graph = record_graph.then(|| Lock::new(DepGraphQuery::new(prev_node_count))); - let status = Lock::new(Some(EncoderState::new(encoder, record_stats))); + let status = Lock::new(Some(EncoderState::new(encoder, record_stats, previous))); GraphEncoder { status, record_graph, profiler: profiler.clone() } } @@ -596,6 +710,22 @@ impl GraphEncoder { self.status.lock().as_mut().unwrap().encode_node(&node, &self.record_graph) } + /// Encodes a node that was promoted from the previous graph. It reads the information directly from + /// the previous dep graph and expects all edges to already have a new dep node index assigned. + #[inline] + pub(crate) fn send_promoted( + &self, + prev_index: SerializedDepNodeIndex, + prev_index_to_index: &IndexVec>, + ) -> DepNodeIndex { + let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph"); + self.status.lock().as_mut().unwrap().encode_promoted_node( + prev_index, + &self.record_graph, + prev_index_to_index, + ) + } + pub fn finish(&self) -> FileEncodeResult { let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph_finish"); diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 6a959a99e5d42..fa07877ab9f3e 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,18 +1,10 @@ #![feature(assert_matches)] #![feature(core_intrinsics)] -#![feature(generic_nonzero)] #![feature(hash_raw_entry)] #![feature(min_specialization)] #![feature(let_chains)] #![allow(rustc::potential_query_instability, internal_features)] -#[macro_use] -extern crate tracing; -#[macro_use] -extern crate rustc_data_structures; -#[macro_use] -extern crate rustc_macros; - pub mod cache; pub mod dep_graph; mod error; diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 674a0984ae987..acc29b67cccec 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -9,13 +9,6 @@ use rustc_span::def_id::DefId; use rustc_span::def_id::DefIndex; use std::fmt::Debug; use std::hash::Hash; -use std::marker::PhantomData; - -pub trait CacheSelector<'tcx, V> { - type Cache - where - V: Copy; -} pub trait QueryCache: Sized { type Key: Hash + Eq + Copy + Debug; @@ -29,14 +22,6 @@ pub trait QueryCache: Sized { fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)); } -pub struct DefaultCacheSelector(PhantomData); - -impl<'tcx, K: Eq + Hash, V: 'tcx> CacheSelector<'tcx, V> for DefaultCacheSelector { - type Cache = DefaultCache - where - V: Copy; -} - pub struct DefaultCache { cache: Sharded>, } @@ -81,14 +66,6 @@ where } } -pub struct SingleCacheSelector; - -impl<'tcx, V: 'tcx> CacheSelector<'tcx, V> for SingleCacheSelector { - type Cache = SingleCache - where - V: Copy; -} - pub struct SingleCache { cache: OnceLock<(V, DepNodeIndex)>, } @@ -123,16 +100,8 @@ where } } -pub struct VecCacheSelector(PhantomData); - -impl<'tcx, K: Idx, V: 'tcx> CacheSelector<'tcx, V> for VecCacheSelector { - type Cache = VecCache - where - V: Copy; -} - pub struct VecCache { - cache: Sharded>>, + cache: Lock>>, } impl Default for VecCache { @@ -151,46 +120,31 @@ where #[inline(always)] fn lookup(&self, key: &K) -> Option<(V, DepNodeIndex)> { - // FIXME: lock_shard_by_hash will use high bits which are usually zero in the index() passed - // here. This makes sharding essentially useless, always selecting the zero'th shard. - let lock = self.cache.lock_shard_by_hash(key.index() as u64); + let lock = self.cache.lock(); if let Some(Some(value)) = lock.get(*key) { Some(*value) } else { None } } #[inline] fn complete(&self, key: K, value: V, index: DepNodeIndex) { - let mut lock = self.cache.lock_shard_by_hash(key.index() as u64); + let mut lock = self.cache.lock(); lock.insert(key, (value, index)); } fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { - for shard in self.cache.lock_shards() { - for (k, v) in shard.iter_enumerated() { - if let Some(v) = v { - f(&k, &v.0, v.1); - } + for (k, v) in self.cache.lock().iter_enumerated() { + if let Some(v) = v { + f(&k, &v.0, v.1); } } } } -pub struct DefIdCacheSelector; - -impl<'tcx, V: 'tcx> CacheSelector<'tcx, V> for DefIdCacheSelector { - type Cache = DefIdCache - where - V: Copy; -} - pub struct DefIdCache { /// Stores the local DefIds in a dense map. Local queries are much more often dense, so this is /// a win over hashing query keys at marginal memory cost (~5% at most) compared to FxHashMap. /// /// The second element of the tuple is the set of keys actually present in the IndexVec, used /// for faster iteration in `iter()`. - // FIXME: This may want to be sharded, like VecCache. However *how* to shard an IndexVec isn't - // super clear; VecCache is effectively not sharded today (see FIXME there). For now just omit - // that complexity here. local: Lock<(IndexVec>, Vec)>, foreign: DefaultCache, } diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 01b9d458f1e4f..ab4f48fcd3206 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -9,10 +9,7 @@ pub use self::job::{ }; mod caches; -pub use self::caches::{ - CacheSelector, DefIdCacheSelector, DefaultCacheSelector, QueryCache, SingleCacheSelector, - VecCacheSelector, -}; +pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache}; mod config; pub use self::config::{HashResult, QueryConfig}; @@ -23,6 +20,7 @@ use rustc_data_structures::stable_hasher::Hash64; use rustc_data_structures::sync::Lock; use rustc_errors::DiagInner; use rustc_hir::def::DefKind; +use rustc_macros::{Decodable, Encodable}; use rustc_span::def_id::DefId; use rustc_span::Span; use thin_vec::ThinVec; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 3d9848395a20b..d37d5bce9cc36 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -27,6 +27,7 @@ use std::fmt::Debug; use std::hash::Hash; use std::mem; use thin_vec::ThinVec; +use tracing::instrument; use super::QueryConfig; diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index a03bb6acd41a7..f824e4faf5d06 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -11,6 +11,8 @@ resolve_added_macro_use = resolve_ancestor_only = visibilities can only be restricted to ancestor modules +resolve_arguments_macro_use_not_allowed = arguments to `macro_use` are not allowed here + resolve_associated_const_with_similar_name_exists = there is an associated constant with a similar name @@ -20,6 +22,10 @@ resolve_associated_fn_with_similar_name_exists = resolve_associated_type_with_similar_name_exists = there is an associated type with a similar name +resolve_attempt_to_define_builtin_macro_twice = + attempted to define built-in macro more than once + .note = previously defined here + resolve_attempt_to_use_non_constant_value_in_constant = attempt to use a non-constant value in a constant @@ -32,6 +38,11 @@ resolve_attempt_to_use_non_constant_value_in_constant_with_suggestion = resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion = this would need to be a `{$suggestion}` +resolve_attributes_starting_with_rustc_are_reserved = + attributes starting with `rustc` are reserved for use by the `rustc` compiler + +resolve_bad_macro_import = bad macro import + resolve_binding_in_never_pattern = never patterns cannot contain variable bindings .suggestion = use a wildcard `_` instead @@ -62,12 +73,19 @@ resolve_cannot_determine_macro_resolution = cannot determine resolution for the {$kind} `{$path}` .note = import resolution is stuck, try simplifying macro imports +resolve_cannot_find_builtin_macro_with_name = + cannot find a built-in macro with name `{$ident}` + resolve_cannot_find_ident_in_this_scope = cannot find {$expected} `{$ident}` in this scope resolve_cannot_glob_import_possible_crates = cannot glob-import all possible crates +resolve_cannot_use_through_an_import = + cannot use {$article} {$descr} through an import + .note = the {$descr} imported here + resolve_change_import_binding = you can use `as` to change the binding name of the import @@ -80,6 +98,12 @@ resolve_consider_adding_macro_export = resolve_consider_declaring_with_pub = consider declaring type or module `{$ident}` with `pub` +resolve_consider_making_the_field_public = + { $number_of_fields -> + [one] consider making the field publicly accessible + *[other] consider making the fields publicly accessible + } + resolve_consider_marking_as_pub = consider marking `{$ident}` as `pub` in the imported module @@ -100,17 +124,44 @@ resolve_const_param_in_non_trivial_anon_const = resolve_const_param_in_ty_of_const_param = const parameters may not be used in the type of const parameters -resolve_expected_found = +resolve_constructor_private_if_any_field_private = + a constructor is private if any of the fields is private + +resolve_elided_anonymous_lifetime_report_error = + `&` without an explicit lifetime name cannot be used here + .label = explicit lifetime name needed here + +resolve_elided_anonymous_lifetime_report_error_suggestion = + consider introducing a higher-ranked lifetime here + +resolve_expected_module_found = expected module, found {$res} `{$path_str}` .label = not a module +resolve_explicit_anonymous_lifetime_report_error = + `'_` cannot be used here + .label = `'_` is a reserved lifetime name + resolve_explicit_unsafe_traits = unsafe traits like `{$ident}` should be implemented explicitly +resolve_extern_crate_loading_macro_not_at_crate_root = + an `extern crate` loading macros must be at the crate root + +resolve_extern_crate_self_requires_renaming = + `extern crate self;` requires renaming + .suggestion = rename the `self` crate to be able to import it + resolve_forward_declared_generic_param = generic parameters with a default cannot use forward declared identifiers .label = defaulted generic parameters cannot be forward declared +resolve_found_an_item_configured_out = + found an item that was configured out + +resolve_generic_arguments_in_macro_path = + generic arguments in macro path + resolve_generic_params_from_outer_item = can't use {$is_self -> [true] `Self` @@ -135,7 +186,6 @@ resolve_generic_params_from_outer_item_static = a `static` is a separate item fr resolve_generic_params_from_outer_item_ty_param = type parameter from outer item - resolve_ident_bound_more_than_once_in_parameter_list = identifier `{$identifier}` is bound more than once in this parameter list .label = used as parameter more than once @@ -144,8 +194,18 @@ resolve_ident_bound_more_than_once_in_same_pattern = identifier `{$identifier}` is bound more than once in the same pattern .label = used in a pattern more than once +resolve_ident_imported_here_but_it_is_desc = + `{$imported_ident}` is imported here, but it is {$imported_ident_desc} + +resolve_ident_in_scope_but_it_is_desc = + `{$imported_ident}` is in scope, but it is {$imported_ident_desc} + +resolve_implicit_elided_lifetimes_not_allowed_here = implicit elided lifetime not allowed here + resolve_imported_crate = `$crate` may not be imported +resolve_imported_macro_not_found = imported macro not found + resolve_imports_cannot_refer_to = imports cannot refer to {$what} @@ -161,6 +221,13 @@ resolve_is_not_directly_importable = `{$target}` is not directly importable .label = cannot be imported directly +resolve_is_private = + {$ident_descr} `{$ident}` is private + .label = private {$ident_descr} + +resolve_item_was_behind_feature = + the item is gated behind the `{$feature}` feature + resolve_items_in_traits_are_not_importable = items in traits are not importable @@ -183,11 +250,23 @@ resolve_lowercase_self = resolve_macro_defined_later = a macro with the same name exists, but it appears later at here +resolve_macro_expanded_extern_crate_cannot_shadow_extern_arguments = + macro-expanded `extern crate` items cannot shadow names passed with `--extern` + resolve_macro_expected_found = expected {$expected}, found {$found} `{$macro_path}` + .label = not {$article} {$expected} + +resolve_macro_extern_deprecated = + `#[macro_escape]` is a deprecated synonym for `#[macro_use]` + .help = try an outer attribute: `#[macro_use]` resolve_macro_use_extern_crate_self = `#[macro_use]` is not supported on `extern crate self` +resolve_macro_use_name_already_in_use = + `{$name}` is already in scope + .note = macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560) + resolve_method_not_member_of_trait = method `{$method}` is not a member of trait `{$trait_}` .label = not a member of trait `{$trait_}` @@ -197,11 +276,45 @@ resolve_missing_macro_rules_name = maybe you have forgotten to define a name for resolve_module_only = visibility must resolve to a module +resolve_name_defined_multiple_time = + the name `{$name}` is defined multiple times + .note = `{$name}` must be defined only once in the {$descr} namespace of this {$container} + +resolve_name_defined_multiple_time_old_binding_definition = + previous definition of the {$old_kind} `{$name}` here + +resolve_name_defined_multiple_time_old_binding_import = + previous import of the {$old_kind} `{$name}` here + +resolve_name_defined_multiple_time_redefined = + `{$name}` redefined here + +resolve_name_defined_multiple_time_reimported = + `{$name}` reimported here + resolve_name_is_already_used_as_generic_parameter = the name `{$name}` is already used for a generic parameter in this item's generic parameters .label = already used .first_use_of_name = first use of `{$name}` +resolve_name_reserved_in_attribute_namespace = + name `{$ident}` is reserved in attribute namespace + +resolve_note_and_refers_to_the_item_defined_here = + {$first -> + [true] {$dots -> + [true] the {$binding_descr} `{$binding_name}` is defined here... + *[false] the {$binding_descr} `{$binding_name}` is defined here + } + *[false] {$dots -> + [true] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here... + *[false] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here + } + } + +resolve_outer_ident_is_not_publicly_reexported = + {$outer_ident_descr} `{$outer_ident}` is not publicly re-exported + resolve_param_in_enum_discriminant = generic parameters may not be used in enum discriminant values .label = cannot perform const operation using `{$name}` @@ -217,6 +330,8 @@ resolve_param_in_ty_of_const_param = the type of const parameters must not depend on other generic parameters .label = the type must not depend on the parameter `{$name}` +resolve_pattern_doesnt_bind_name = pattern doesn't bind `{$name}` + resolve_proc_macro_same_crate = can't use a procedural macro from the same crate that defines it .help = you can define integration tests in a directory named `tests` @@ -233,6 +348,8 @@ resolve_relative_2018 = resolve_remove_surrounding_derive = remove from the surrounding `derive()` +resolve_remove_unnecessary_import = remove unnecessary import + resolve_self_import_can_only_appear_once_in_the_list = `self` import can only appear once in an import list .label = can only appear once in an import list @@ -254,16 +371,43 @@ resolve_self_in_generic_param_default = generic parameters cannot use `Self` in their defaults .label = `Self` in generic parameter default +resolve_similarly_named_defined_here = + similarly named {$candidate_descr} `{$candidate}` defined here + +resolve_single_item_defined_here = + {$candidate_descr} `{$candidate}` defined here + +resolve_static_lifetime_is_reserved = invalid lifetime parameter name: `{$lifetime}` + .label = 'static is a reserved lifetime name + +resolve_suggestion_import_ident_directly = + import `{$ident}` directly + +resolve_suggestion_import_ident_through_reexport = + import `{$ident}` through the re-export + resolve_tool_module_imported = cannot use a tool module through an import .note = the tool module imported here +resolve_tool_only_accepts_identifiers = + `{$tool}` only accepts identifiers + .label = not an identifier + +resolve_tool_was_already_registered = + tool `{$tool}` was already registered + .label = already registered here + resolve_trait_impl_duplicate = duplicate definitions with name `{$name}`: .label = duplicate definition .old_span_label = previous definition here .trait_item_span = item in trait +resolve_trait_impl_mismatch = + item `{$name}` is an associated {$kind}, which doesn't match its trait `{$trait_path}` + .label = does not match trait + .trait_impl_mismatch_label_item = item in trait resolve_try_using_similarly_named_label = try using similarly named label @@ -284,12 +428,18 @@ resolve_undeclared_label = use of undeclared label `{$name}` .label = undeclared label `{$name}` +resolve_underscore_lifetime_is_reserved = `'_` cannot be used here + .label = `'_` is a reserved lifetime name + resolve_unexpected_res_change_ty_to_const_param_sugg = you might have meant to write a const parameter here resolve_unexpected_res_use_at_op_in_slice_pat_with_range_sugg = if you meant to collect the rest of the slice in `{$ident}`, use the at operator +resolve_unnamed_crate_root_import = + crate root imports need to be explicitly named: `use crate as name;` + resolve_unreachable_label = use of unreachable label `{$name}` .label = unreachable label `{$name}` @@ -312,3 +462,8 @@ resolve_variable_bound_with_different_mode = variable `{$variable_name}` is bound inconsistently across alternatives separated by `|` .label = bound in different ways .first_binding_span = first binding + +resolve_variable_is_not_bound_in_all_patterns = + variable `{$name}` is not bound in all patterns + +resolve_variable_not_in_all_patterns = variable not in all patterns diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 375f20dd809f2..8068ea1a40773 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -19,7 +19,6 @@ use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind}; use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId}; use rustc_attr as attr; use rustc_data_structures::sync::Lrc; -use rustc_errors::{codes::*, struct_span_code_err, Applicability}; use rustc_expand::expand::AstFragment; use rustc_hir::def::{self, *}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; @@ -529,11 +528,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { } if ident.name == kw::Crate { - self.r.dcx().span_err( - ident.span, - "crate root imports need to be explicitly named: \ - `use crate as name;`", - ); + self.r.dcx().emit_err(errors::UnnamedCrateRootImport { span: ident.span }); } let kind = ImportKind::Single { @@ -848,16 +843,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { let expansion = parent_scope.expansion; let (used, module, binding) = if orig_name.is_none() && ident.name == kw::SelfLower { - self.r - .dcx() - .struct_span_err(item.span, "`extern crate self;` requires renaming") - .with_span_suggestion( - item.span, - "rename the `self` crate to be able to import it", - "extern crate self as name;", - Applicability::HasPlaceholders, - ) - .emit(); + self.r.dcx().emit_err(errors::ExternCrateSelfRequiresRenaming { span: sp }); return; } else if orig_name == Some(kw::SelfLower) { Some(self.r.graph_root) @@ -897,9 +883,11 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { if parent == self.r.graph_root { if let Some(entry) = self.r.extern_prelude.get(&ident.normalize_to_macros_2_0()) { if expansion != LocalExpnId::ROOT && orig_name.is_some() && !entry.is_import() { - let msg = "macro-expanded `extern crate` items cannot \ - shadow names passed with `--extern`"; - self.r.dcx().span_err(item.span, msg); + self.r.dcx().emit_err( + errors::MacroExpandedExternCrateCannotShadowExternArguments { + span: item.span, + }, + ); // `return` is intended to discard this binding because it's an // unregistered ambiguity error which would result in a panic // caused by inconsistency `path_res` @@ -1030,10 +1018,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { allow_shadowing: bool, ) { if self.r.macro_use_prelude.insert(name, binding).is_some() && !allow_shadowing { - let msg = format!("`{name}` is already in scope"); - let note = - "macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)"; - self.r.dcx().struct_span_err(span, msg).with_note(note).emit(); + self.r.dcx().emit_err(errors::MacroUseNameAlreadyInUse { span, name }); } } @@ -1044,13 +1029,9 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { for attr in &item.attrs { if attr.has_name(sym::macro_use) { if self.parent_scope.module.parent.is_some() { - struct_span_code_err!( - self.r.dcx(), - item.span, - E0468, - "an `extern crate` loading macros must be at the crate root" - ) - .emit(); + self.r.dcx().emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { + span: item.span, + }); } if let ItemKind::ExternCrate(Some(orig_name)) = item.kind { if orig_name == kw::SelfLower { @@ -1058,7 +1039,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { } } let ill_formed = |span| { - struct_span_code_err!(self.r.dcx(), span, E0466, "bad macro import").emit(); + self.r.dcx().emit_err(errors::BadMacroImport { span }); }; match attr.meta() { Some(meta) => match meta.kind { @@ -1143,13 +1124,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { allow_shadowing, ); } else { - struct_span_code_err!( - self.r.dcx(), - ident.span, - E0469, - "imported macro not found" - ) - .emit(); + self.r.dcx().emit_err(errors::ImportedMacroNotFound { span: ident.span }); } } } @@ -1160,18 +1135,16 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { for attr in attrs { if attr.has_name(sym::macro_escape) { - let msg = "`#[macro_escape]` is a deprecated synonym for `#[macro_use]`"; - let mut err = self.r.dcx().struct_span_warn(attr.span, msg); - if let ast::AttrStyle::Inner = attr.style { - err.help("try an outer attribute: `#[macro_use]`"); - } - err.emit(); + let inner_attribute = matches!(attr.style, ast::AttrStyle::Inner).then_some(()); + self.r + .dcx() + .emit_warn(errors::MacroExternDeprecated { span: attr.span, inner_attribute }); } else if !attr.has_name(sym::macro_use) { continue; } if !attr.is_word() { - self.r.dcx().span_err(attr.span, "arguments to `macro_use` are not allowed here"); + self.r.dcx().emit_err(errors::ArgumentsMacroUseNotAllowed { span: attr.span }); } return true; } @@ -1331,11 +1304,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { visit::walk_item(self, item); macro_rules_scope } - ItemKind::MacCall(..) => { - let macro_rules_scope = self.visit_invoc_in_module(item.id); - visit::walk_item(self, item); - macro_rules_scope - } + ItemKind::MacCall(..) => self.visit_invoc_in_module(item.id), _ => { let orig_macro_rules_scope = self.parent_scope.macro_rules; self.build_reduced_graph_for_item(item); @@ -1366,7 +1335,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { } self.build_reduced_graph_for_foreign_item(foreign_item); - visit::walk_foreign_item(self, foreign_item); + visit::walk_item(self, foreign_item); } fn visit_block(&mut self, block: &'b Block) { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index bef95aca0d1cc..a27a6bceda335 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -136,14 +136,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { opt_macro_data = Some(macro_data); DefKind::Macro(macro_kind) } - ItemKind::MacCall(..) => { - visit::walk_item(self, i); - return self.visit_macro_invoc(i.id); - } ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, - ItemKind::Use(..) => { - return visit::walk_item(self, i); - } + ItemKind::Use(..) => return visit::walk_item(self, i), + ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), }; let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span); @@ -214,7 +209,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { let def_kind = match fi.kind { - ForeignItemKind::Static(_, mutability, _) => { + ForeignItemKind::Static(box StaticForeignItem { ty: _, mutability, expr: _ }) => { DefKind::Static { mutability, nested: false } } ForeignItemKind::Fn(_) => DefKind::Fn, @@ -224,7 +219,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { let def = self.create_def(fi.id, fi.ident.name, def_kind, fi.span); - self.with_parent(def, |this| visit::walk_foreign_item(this, fi)); + self.with_parent(def, |this| visit::walk_item(this, fi)); } fn visit_variant(&mut self, v: &'a Variant) { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 476b31f44ae66..12484462f82f4 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -6,8 +6,8 @@ use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - codes::*, pluralize, report_ambiguity_error, struct_span_code_err, Applicability, Diag, - DiagCtxt, ErrorGuaranteed, MultiSpan, SuggestionStyle, + codes::*, report_ambiguity_error, struct_span_code_err, Applicability, Diag, DiagCtxt, + ErrorGuaranteed, MultiSpan, SuggestionStyle, }; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; @@ -29,10 +29,9 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Span, SyntaxContext}; use thin_vec::{thin_vec, ThinVec}; -use crate::errors::{AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion}; use crate::errors::{ - ConsiderAddingADerive, ExplicitUnsafeTraits, MacroDefinedLater, MacroSuggMovePosition, - MaybeMissingMacroRulesName, + self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, + ExplicitUnsafeTraits, MacroDefinedLater, MacroSuggMovePosition, MaybeMissingMacroRulesName, }; use crate::imports::{Import, ImportKind}; use crate::late::{PatternSource, Rib}; @@ -226,16 +225,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ModuleKind::Block => "block", }; - let old_noun = match old_binding.is_import_user_facing() { - true => "import", - false => "definition", - }; - - let new_participle = match new_binding.is_import_user_facing() { - true => "imported", - false => "defined", - }; - let (name, span) = (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); @@ -254,35 +243,51 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { (TypeNS, _) => "type", }; - let msg = format!("the name `{name}` is defined multiple times"); - - let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { - (true, true) => struct_span_code_err!(self.dcx(), span, E0259, "{}", msg), + let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { + (true, true) => E0259, (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { - true => struct_span_code_err!(self.dcx(), span, E0254, "{}", msg), - false => struct_span_code_err!(self.dcx(), span, E0260, "{}", msg), + true => E0254, + false => E0260, }, _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) { - (false, false) => struct_span_code_err!(self.dcx(), span, E0428, "{}", msg), - (true, true) => struct_span_code_err!(self.dcx(), span, E0252, "{}", msg), - _ => struct_span_code_err!(self.dcx(), span, E0255, "{}", msg), + (false, false) => E0428, + (true, true) => E0252, + _ => E0255, }, }; - err.note(format!( - "`{}` must be defined only once in the {} namespace of this {}", - name, - ns.descr(), - container - )); - - err.span_label(span, format!("`{name}` re{new_participle} here")); - if !old_binding.span.is_dummy() && old_binding.span != span { - err.span_label( - self.tcx.sess.source_map().guess_head_span(old_binding.span), - format!("previous {old_noun} of the {old_kind} `{name}` here"), - ); - } + let label = match new_binding.is_import_user_facing() { + true => errors::NameDefinedMultipleTimeLabel::Reimported { span, name }, + false => errors::NameDefinedMultipleTimeLabel::Redefined { span, name }, + }; + + let old_binding_label = + (!old_binding.span.is_dummy() && old_binding.span != span).then(|| { + let span = self.tcx.sess.source_map().guess_head_span(old_binding.span); + match old_binding.is_import_user_facing() { + true => errors::NameDefinedMultipleTimeOldBindingLabel::Import { + span, + name, + old_kind, + }, + false => errors::NameDefinedMultipleTimeOldBindingLabel::Definition { + span, + name, + old_kind, + }, + } + }); + + let mut err = self + .dcx() + .create_err(errors::NameDefinedMultipleTime { + span, + descr: ns.descr(), + container, + label, + old_binding_label, + }) + .with_code(code); // See https://github.com/rust-lang/rust/issues/32354 use NameBindingKind::Import; @@ -330,20 +335,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { match import { Some((import, span, true)) if should_remove_import && import.is_nested() => { - self.add_suggestion_for_duplicate_nested_use(&mut err, import, span) + self.add_suggestion_for_duplicate_nested_use(&mut err, import, span); } Some((import, _, true)) if should_remove_import && !import.is_glob() => { // Simple case - remove the entire import. Due to the above match arm, this can // only be a single use so just remove it entirely. - err.tool_only_span_suggestion( - import.use_span_with_attributes, - "remove unnecessary import", - "", - Applicability::MaybeIncorrect, + err.subdiagnostic( + self.tcx.dcx(), + errors::ToolOnlyRemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }, ); } Some((import, span, _)) => { - self.add_suggestion_for_rename_of_use(&mut err, name, import, span) + self.add_suggestion_for_rename_of_use(&mut err, name, import, span); } _ => {} } @@ -444,7 +449,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { binding_span: Span, ) { assert!(import.is_nested()); - let message = "remove unnecessary import"; // Two examples will be used to illustrate the span manipulations we're doing: // @@ -460,22 +464,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // previous imports. if found_closing_brace { if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) { - err.tool_only_span_suggestion(span, message, "", Applicability::MaybeIncorrect); + err.subdiagnostic(self.dcx(), errors::ToolOnlyRemoveUnnecessaryImport { span }); } else { // Remove the entire line if we cannot extend the span back, this indicates an // `issue_52891::{self}` case. - err.span_suggestion( - import.use_span_with_attributes, - message, - "", - Applicability::MaybeIncorrect, + err.subdiagnostic( + self.dcx(), + errors::RemoveUnnecessaryImport { span: import.use_span_with_attributes }, ); } return; } - err.span_suggestion(span, message, "", Applicability::MachineApplicable); + err.subdiagnostic(self.dcx(), errors::RemoveUnnecessaryImport { span }); } pub(crate) fn lint_if_path_starts_with_module( @@ -571,14 +573,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { resolution_error: ResolutionError<'a>, ) -> Diag<'_> { match resolution_error { - ResolutionError::GenericParamsFromOuterItem(outer_res, has_generic_params, def_kind) => { + ResolutionError::GenericParamsFromOuterItem( + outer_res, + has_generic_params, + def_kind, + ) => { use errs::GenericParamsFromOuterItemLabel as Label; let static_or_const = match def_kind { - DefKind::Static{ .. } => Some(errs::GenericParamsFromOuterItemStaticOrConst::Static), + DefKind::Static { .. } => { + Some(errs::GenericParamsFromOuterItemStaticOrConst::Static) + } DefKind::Const => Some(errs::GenericParamsFromOuterItemStaticOrConst::Const), _ => None, }; - let is_self = matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }); + let is_self = + matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }); let mut err = errs::GenericParamsFromOuterItem { span, label: None, @@ -677,18 +686,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let origin_sp = origin.iter().copied().collect::>(); let msp = MultiSpan::from_spans(target_sp.clone()); - let mut err = struct_span_code_err!( - self.dcx(), - msp, - E0408, - "variable `{}` is not bound in all patterns", - name, - ); + let mut err = self + .dcx() + .create_err(errors::VariableIsNotBoundInAllPatterns { multispan: msp, name }); for sp in target_sp { - err.span_label(sp, format!("pattern doesn't bind `{name}`")); + err.subdiagnostic(self.dcx(), errors::PatternDoesntBindName { span: sp, name }); } for sp in origin_sp { - err.span_label(sp, "variable not in all patterns"); + err.subdiagnostic(self.dcx(), errors::VariableNotInAllPatterns { span: sp }); } if could_be_path { let import_suggestions = self.lookup_import_candidates( @@ -961,17 +966,16 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { code, trait_item_span, trait_path, - } => { - self.dcx().struct_span_err( + } => self + .dcx() + .create_err(errors::TraitImplMismatch { span, - format!( - "item `{name}` is an associated {kind}, which doesn't match its trait `{trait_path}`", - ), - ) - .with_code(code) - .with_span_label(span, "does not match trait") - .with_span_label(trait_item_span, "item in trait") - } + name, + kind, + trait_path, + trait_item_span, + }) + .with_code(code), ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => self .dcx() .create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }), @@ -1005,7 +1009,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ResolutionError::FailedToResolve { segment: None, label, suggestion, module: None }, ), VisResolutionError::ExpectedFound(span, path_str, res) => { - self.dcx().create_err(errs::ExpectedFound { span, res, path_str }) + self.dcx().create_err(errs::ExpectedModuleFound { span, res, path_str }) } VisResolutionError::Indeterminate(span) => { self.dcx().create_err(errs::Indeterminate(span)) @@ -1532,17 +1536,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; if let crate::NameBindingKind::Import { import, .. } = binding.kind { if !import.span.is_dummy() { - err.span_note( - import.span, - format!("`{ident}` is imported here, but it is {desc}"), - ); + let note = errors::IdentImporterHereButItIsDesc { + span: import.span, + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(self.tcx.dcx(), note); // Silence the 'unused import' warning we might get, // since this diagnostic already covers that import. self.record_use(ident, binding, Used::Other); return; } } - err.note(format!("`{ident}` is in scope, but it is {desc}")); + let note = errors::IdentInScopeButItIsDesc { + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(self.tcx.dcx(), note); return; } } @@ -1582,20 +1592,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // | ^ return false; } - let prefix = match suggestion.target { - SuggestionTarget::SimilarlyNamed => "similarly named ", - SuggestionTarget::SingleItem => "", + let span = self.tcx.sess.source_map().guess_head_span(def_span); + let candidate_descr = suggestion.res.descr(); + let candidate = suggestion.candidate; + let label = match suggestion.target { + SuggestionTarget::SimilarlyNamed => { + errors::DefinedHere::SimilarlyNamed { span, candidate_descr, candidate } + } + SuggestionTarget::SingleItem => { + errors::DefinedHere::SingleItem { span, candidate_descr, candidate } + } }; - - err.span_label( - self.tcx.sess.source_map().guess_head_span(def_span), - format!( - "{}{} `{}` defined here", - prefix, - suggestion.res.descr(), - suggestion.candidate, - ), - ); + err.subdiagnostic(self.tcx.dcx(), label); } let (span, sugg, post) = if let SuggestionTarget::SimilarlyNamed = suggestion.target @@ -1749,16 +1757,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { |b: NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; // Print the primary message. - let descr = get_descr(binding); - let mut err = struct_span_code_err!( - self.dcx(), - ident.span, - E0603, - "{} `{}` is private", - descr, - ident - ); - err.span_label(ident.span, format!("private {descr}")); + let ident_descr = get_descr(binding); + let mut err = + self.dcx().create_err(errors::IsPrivate { span: ident.span, ident_descr, ident }); let mut not_publicly_reexported = false; if let Some((this_res, outer_ident)) = outermost_res { @@ -1782,10 +1783,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // If we suggest importing a public re-export, don't point at the definition. if point_to_def && ident.span != outer_ident.span { not_publicly_reexported = true; - err.span_label( - outer_ident.span, - format!("{} `{outer_ident}` is not publicly re-exported", this_res.descr()), - ); + let label = errors::OuterIdentIsNotPubliclyReexported { + span: outer_ident.span, + outer_ident_descr: this_res.descr(), + outer_ident, + }; + err.subdiagnostic(self.tcx.dcx(), label); } } @@ -1799,18 +1802,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { { non_exhaustive = Some(attr.span); } else if let Some(span) = ctor_fields_span { - err.span_label(span, "a constructor is private if any of the fields is private"); + let label = errors::ConstructorPrivateIfAnyFieldPrivate { span }; + err.subdiagnostic(self.tcx.dcx(), label); if let Res::Def(_, d) = res && let Some(fields) = self.field_visibility_spans.get(&d) { - err.multipart_suggestion_verbose( - format!( - "consider making the field{} publicly accessible", - pluralize!(fields.len()) - ), - fields.iter().map(|span| (*span, "pub ".to_string())).collect(), - Applicability::MaybeIncorrect, - ); + let spans = fields.iter().map(|span| *span).collect(); + let sugg = + errors::ConsiderMakingTheFieldPublic { spans, number_of_fields: fields.len() }; + err.subdiagnostic(self.tcx.dcx(), sugg); } } @@ -1893,13 +1893,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { NameBindingKind::Res(_) | NameBindingKind::Module(_) => {} } let first = binding == first_binding; - let msg = format!( - "{and_refers_to}the {item} `{name}`{which} is defined here{dots}", - and_refers_to = if first { "" } else { "...and refers to " }, - item = get_descr(binding), - which = if first { "" } else { " which" }, - dots = if next_binding.is_some() { "..." } else { "" }, - ); let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); let mut note_span = MultiSpan::from_span(def_span); if !first && binding.vis.is_public() { @@ -1919,7 +1912,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { "cannot be constructed because it is `#[non_exhaustive]`", ); } - err.span_note(note_span, msg); + let note = errors::NoteAndRefersToTheItemDefinedHere { + span: note_span, + binding_descr: get_descr(binding), + binding_name: name, + first, + dots: next_binding.is_some(), + }; + err.subdiagnostic(self.tcx.dcx(), note); } // We prioritize shorter paths, non-core imports and direct imports over the alternatives. sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0] == "core", *reexport)); @@ -1933,15 +1933,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { continue; } let path = sugg.join("::"); - err.span_suggestion_verbose( - dedup_span, - format!( - "import `{ident}` {}", - if reexport { "through the re-export" } else { "directly" } - ), - path, - Applicability::MachineApplicable, - ); + let sugg = if reexport { + errors::ImportIdent::ThroughReExport { span: dedup_span, ident, path } + } else { + errors::ImportIdent::Directly { span: dedup_span, ident, path } + }; + err.subdiagnostic(self.tcx.dcx(), sugg); break; } @@ -2521,13 +2518,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { continue; } - err.span_note(name.span, "found an item that was configured out"); + let note = errors::FoundItemConfigureOut { span: name.span }; + err.subdiagnostic(self.tcx.dcx(), note); if let MetaItemKind::List(nested) = &cfg.kind && let NestedMetaItem::MetaItem(meta_item) = &nested[0] && let MetaItemKind::NameValue(feature_name) = &meta_item.kind { - err.note(format!("the item is gated behind the `{}` feature", feature_name.symbol)); + let note = errors::ItemWasBehindFeature { feature: feature_name.symbol }; + err.subdiagnostic(self.tcx.dcx(), note); } } } @@ -2730,7 +2729,7 @@ pub(crate) fn import_candidates( ); } -type PathString<'a> = (String, &'a str, Option, &'a Option, bool); +type PathString<'a> = (String, &'a str, Option, &'a Option, bool); /// When an entity with a given name is not available in scope, we search for /// entities with that name in all crates. This method allows outputting the @@ -2762,7 +2761,7 @@ fn show_candidates( accessible_path_strings.push(( pprust::path_to_string(&c.path), c.descr, - c.did, + c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), &c.note, c.via_import, )) @@ -2771,7 +2770,7 @@ fn show_candidates( inaccessible_path_strings.push(( pprust::path_to_string(&c.path), c.descr, - c.did, + c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), &c.note, c.via_import, )) @@ -2889,15 +2888,14 @@ fn show_candidates( } else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagMode::Import { .. })) { let prefix = if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; - if let [(name, descr, def_id, note, _)] = &inaccessible_path_strings[..] { + if let [(name, descr, source_span, note, _)] = &inaccessible_path_strings[..] { let msg = format!( "{prefix}{descr} `{name}`{} exists but is inaccessible", if let DiagMode::Pattern = mode { ", which" } else { "" } ); - if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { - let span = tcx.source_span(local_def_id); - let span = tcx.sess.source_map().guess_head_span(span); + if let Some(source_span) = source_span { + let span = tcx.sess.source_map().guess_head_span(*source_span); let mut multi_span = MultiSpan::from_span(span); multi_span.push_span_label(span, "not accessible"); err.span_note(multi_span, msg); @@ -2925,10 +2923,9 @@ fn show_candidates( let mut has_colon = false; let mut spans = Vec::new(); - for (name, _, def_id, _, _) in &inaccessible_path_strings { - if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) { - let span = tcx.source_span(local_def_id); - let span = tcx.sess.source_map().guess_head_span(span); + for (name, _, source_span, _, _) in &inaccessible_path_strings { + if let Some(source_span) = source_span { + let span = tcx.sess.source_map().guess_head_span(*source_span); spans.push((name, span)); } else { if !has_colon { diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 5eee6a51fd2da..edfeacec7e3b7 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_errors::{codes::*, Applicability}; +use rustc_errors::{codes::*, Applicability, ElidedLifetimeInPathSubdiag, MultiSpan}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{ symbol::{Ident, Symbol}, @@ -495,8 +495,8 @@ pub(crate) struct Relative2018 { pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span); #[derive(Diagnostic)] -#[diag(resolve_expected_found, code = E0577)] -pub(crate) struct ExpectedFound { +#[diag(resolve_expected_module_found, code = E0577)] +pub(crate) struct ExpectedModuleFound { #[primary_span] #[label] pub(crate) span: Span, @@ -525,8 +525,10 @@ pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span); #[diag(resolve_macro_expected_found)] pub(crate) struct MacroExpectedFound<'a> { #[primary_span] + #[label] pub(crate) span: Span, pub(crate) found: &'a str, + pub(crate) article: &'static str, pub(crate) expected: &'a str, pub(crate) macro_path: &'a str, #[subdiagnostic] @@ -801,3 +803,423 @@ pub(crate) struct UnexpectedResUseAtOpInSlicePatWithRangeSugg { pub ident: Ident, pub snippet: String, } + +#[derive(Diagnostic)] +#[diag(resolve_extern_crate_loading_macro_not_at_crate_root, code = E0468)] +pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_bad_macro_import, code = E0466)] +pub(crate) struct BadMacroImport { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_extern_crate_self_requires_renaming)] +pub(crate) struct ExternCrateSelfRequiresRenaming { + #[primary_span] + #[suggestion(code = "extern crate self as name;", applicability = "has-placeholders")] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_macro_use_name_already_in_use)] +#[note] +pub(crate) struct MacroUseNameAlreadyInUse { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_imported_macro_not_found, code = E0469)] +pub(crate) struct ImportedMacroNotFound { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_macro_extern_deprecated)] +pub(crate) struct MacroExternDeprecated { + #[primary_span] + pub(crate) span: Span, + #[help] + pub inner_attribute: Option<()>, +} + +#[derive(Diagnostic)] +#[diag(resolve_arguments_macro_use_not_allowed)] +pub(crate) struct ArgumentsMacroUseNotAllowed { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_unnamed_crate_root_import)] +pub(crate) struct UnnamedCrateRootImport { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_macro_expanded_extern_crate_cannot_shadow_extern_arguments)] +pub(crate) struct MacroExpandedExternCrateCannotShadowExternArguments { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_elided_anonymous_lifetime_report_error, code = E0637)] +pub(crate) struct ElidedAnonymousLivetimeReportError { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) suggestion: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + resolve_elided_anonymous_lifetime_report_error_suggestion, + applicability = "machine-applicable" +)] +pub(crate) struct ElidedAnonymousLivetimeReportErrorSuggestion { + #[suggestion_part(code = "for<'a> ")] + pub(crate) lo: Span, + #[suggestion_part(code = "'a ")] + pub(crate) hi: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_explicit_anonymous_lifetime_report_error, code = E0637)] +pub(crate) struct ExplicitAnonymousLivetimeReportError { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_implicit_elided_lifetimes_not_allowed_here, code = E0726)] +pub(crate) struct ImplicitElidedLifetimeNotAllowedHere { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) subdiag: ElidedLifetimeInPathSubdiag, +} + +#[derive(Diagnostic)] +#[diag(resolve_underscore_lifetime_is_reserved, code = E0637)] +pub(crate) struct UnderscoreLifetimeIsReserved { + #[primary_span] + #[label] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_static_lifetime_is_reserved, code = E0262)] +pub(crate) struct StaticLifetimeIsReserved { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) lifetime: Ident, +} + +#[derive(Diagnostic)] +#[diag(resolve_attempt_to_define_builtin_macro_twice, code = E0773)] +pub(crate) struct AttemptToDefineBuiltinMacroTwice { + #[primary_span] + pub(crate) span: Span, + #[note] + pub(crate) note_span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_variable_is_not_bound_in_all_patterns, code = E0408)] +pub(crate) struct VariableIsNotBoundInAllPatterns { + #[primary_span] + pub(crate) multispan: MultiSpan, + pub(crate) name: Symbol, +} + +#[derive(Subdiagnostic, Debug, Clone)] +#[label(resolve_pattern_doesnt_bind_name)] +pub(crate) struct PatternDoesntBindName { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} + +#[derive(Subdiagnostic, Debug, Clone)] +#[label(resolve_variable_not_in_all_patterns)] +pub(crate) struct VariableNotInAllPatterns { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_name_defined_multiple_time)] +#[note] +pub(crate) struct NameDefinedMultipleTime { + #[primary_span] + pub(crate) span: Span, + pub(crate) descr: &'static str, + pub(crate) container: &'static str, + #[subdiagnostic] + pub(crate) label: NameDefinedMultipleTimeLabel, + #[subdiagnostic] + pub(crate) old_binding_label: Option, +} + +#[derive(Subdiagnostic)] +pub(crate) enum NameDefinedMultipleTimeLabel { + #[label(resolve_name_defined_multiple_time_reimported)] + Reimported { + #[primary_span] + span: Span, + name: Symbol, + }, + #[label(resolve_name_defined_multiple_time_redefined)] + Redefined { + #[primary_span] + span: Span, + name: Symbol, + }, +} + +#[derive(Subdiagnostic)] +pub(crate) enum NameDefinedMultipleTimeOldBindingLabel { + #[label(resolve_name_defined_multiple_time_old_binding_import)] + Import { + #[primary_span] + span: Span, + name: Symbol, + old_kind: &'static str, + }, + #[label(resolve_name_defined_multiple_time_old_binding_definition)] + Definition { + #[primary_span] + span: Span, + name: Symbol, + old_kind: &'static str, + }, +} + +#[derive(Diagnostic)] +#[diag(resolve_is_private, code = E0603)] +pub(crate) struct IsPrivate<'a> { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) ident_descr: &'a str, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag(resolve_generic_arguments_in_macro_path)] +pub(crate) struct GenericArgumentsInMacroPath { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_attributes_starting_with_rustc_are_reserved)] +pub(crate) struct AttributesStartingWithRustcAreReserved { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_cannot_use_through_an_import)] +pub(crate) struct CannotUseThroughAnImport { + #[primary_span] + pub(crate) span: Span, + pub(crate) article: &'static str, + pub(crate) descr: &'static str, + #[note] + pub(crate) binding_span: Option, +} + +#[derive(Diagnostic)] +#[diag(resolve_name_reserved_in_attribute_namespace)] +pub(crate) struct NameReservedInAttributeNamespace { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag(resolve_cannot_find_builtin_macro_with_name)] +pub(crate) struct CannotFindBuiltinMacroWithName { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag(resolve_tool_was_already_registered)] +pub(crate) struct ToolWasAlreadyRegistered { + #[primary_span] + pub(crate) span: Span, + pub(crate) tool: Ident, + #[label] + pub(crate) old_ident_span: Span, +} + +#[derive(Diagnostic)] +#[diag(resolve_tool_only_accepts_identifiers)] +pub(crate) struct ToolOnlyAcceptsIdentifiers { + #[label] + #[primary_span] + pub(crate) span: Span, + pub(crate) tool: Symbol, +} + +#[derive(Subdiagnostic)] +pub(crate) enum DefinedHere { + #[label(resolve_similarly_named_defined_here)] + SimilarlyNamed { + #[primary_span] + span: Span, + candidate_descr: &'static str, + candidate: Symbol, + }, + #[label(resolve_single_item_defined_here)] + SingleItem { + #[primary_span] + span: Span, + candidate_descr: &'static str, + candidate: Symbol, + }, +} + +#[derive(Subdiagnostic)] +#[label(resolve_outer_ident_is_not_publicly_reexported)] +pub(crate) struct OuterIdentIsNotPubliclyReexported { + #[primary_span] + pub(crate) span: Span, + pub(crate) outer_ident_descr: &'static str, + pub(crate) outer_ident: Ident, +} + +#[derive(Subdiagnostic)] +#[label(resolve_constructor_private_if_any_field_private)] +pub(crate) struct ConstructorPrivateIfAnyFieldPrivate { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + resolve_consider_making_the_field_public, + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct ConsiderMakingTheFieldPublic { + #[suggestion_part(code = "pub ")] + pub(crate) spans: Vec, + pub(crate) number_of_fields: usize, +} + +#[derive(Subdiagnostic)] +pub(crate) enum ImportIdent { + #[suggestion( + resolve_suggestion_import_ident_through_reexport, + code = "{path}", + applicability = "machine-applicable", + style = "verbose" + )] + ThroughReExport { + #[primary_span] + span: Span, + ident: Ident, + path: String, + }, + #[suggestion( + resolve_suggestion_import_ident_directly, + code = "{path}", + applicability = "machine-applicable", + style = "verbose" + )] + Directly { + #[primary_span] + span: Span, + ident: Ident, + path: String, + }, +} + +#[derive(Subdiagnostic)] +#[note(resolve_note_and_refers_to_the_item_defined_here)] +pub(crate) struct NoteAndRefersToTheItemDefinedHere<'a> { + #[primary_span] + pub(crate) span: MultiSpan, + pub(crate) binding_descr: &'a str, + pub(crate) binding_name: Ident, + pub(crate) first: bool, + pub(crate) dots: bool, +} + +#[derive(Subdiagnostic)] +#[suggestion(resolve_remove_unnecessary_import, code = "", applicability = "maybe-incorrect")] +pub(crate) struct RemoveUnnecessaryImport { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + resolve_remove_unnecessary_import, + code = "", + applicability = "maybe-incorrect", + style = "tool-only" +)] +pub(crate) struct ToolOnlyRemoveUnnecessaryImport { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[note(resolve_ident_imported_here_but_it_is_desc)] +pub(crate) struct IdentImporterHereButItIsDesc<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) imported_ident: Ident, + pub(crate) imported_ident_desc: &'a str, +} + +#[derive(Subdiagnostic)] +#[note(resolve_ident_in_scope_but_it_is_desc)] +pub(crate) struct IdentInScopeButItIsDesc<'a> { + pub(crate) imported_ident: Ident, + pub(crate) imported_ident_desc: &'a str, +} + +#[derive(Subdiagnostic)] +#[note(resolve_found_an_item_configured_out)] +pub(crate) struct FoundItemConfigureOut { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[note(resolve_item_was_behind_feature)] +pub(crate) struct ItemWasBehindFeature { + pub(crate) feature: Symbol, +} + +#[derive(Diagnostic)] +#[diag(resolve_trait_impl_mismatch)] +pub(crate) struct TraitImplMismatch { + #[primary_span] + #[label] + pub(crate) span: Span, + pub(crate) name: Symbol, + pub(crate) kind: &'static str, + pub(crate) trait_path: String, + #[label(resolve_trait_impl_mismatch_label_item)] + pub(crate) trait_item_span: Span, +} diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index b24ed573ff97d..43a43e01a9adf 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -605,6 +605,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && !this.tcx.features().f16 && !ident.span.allows_unstable(sym::f16) && finalize.is_some() + && innermost_result.is_none() { feature_err( this.tcx.sess, @@ -618,6 +619,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { && !this.tcx.features().f128 && !ident.span.allows_unstable(sym::f128) && finalize.is_some() + && innermost_result.is_none() { feature_err( this.tcx.sess, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 9bf3e9ccabdf1..3d9380a3ebd35 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -19,6 +19,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, MultiSpan}; use rustc_hir::def::{self, DefKind, PartialRes}; +use rustc_hir::def_id::DefId; use rustc_middle::metadata::ModChild; use rustc_middle::metadata::Reexport; use rustc_middle::span_bug; @@ -250,6 +251,9 @@ struct UnresolvedImportError { note: Option, suggestion: Option, candidates: Option>, + segment: Option, + /// comes from `PathRes::Failed { module }` + module: Option, } // Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;` @@ -528,7 +532,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut seen_spans = FxHashSet::default(); let mut errors = vec![]; - let mut prev_root_id: NodeId = NodeId::from_u32(0); + let mut prev_root_id: NodeId = NodeId::ZERO; let determined_imports = mem::take(&mut self.determined_imports); let indeterminate_imports = mem::take(&mut self.indeterminate_imports); @@ -552,8 +556,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - if prev_root_id.as_u32() != 0 - && prev_root_id.as_u32() != import.root_id.as_u32() + if prev_root_id != NodeId::ZERO + && prev_root_id != import.root_id && !errors.is_empty() { // In the case of a new import line, throw a diagnostic message @@ -579,16 +583,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.kind, import.span, ); - let err = UnresolvedImportError { - span: import.span, - label: None, - note: None, - suggestion: None, - candidates: None, - }; // FIXME: there should be a better way of doing this than // formatting this as a string then checking for `::` if path.contains("::") { + let err = UnresolvedImportError { + span: import.span, + label: None, + note: None, + suggestion: None, + candidates: None, + segment: None, + module: None, + }; errors.push((*import, err)) } } @@ -738,15 +744,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - match &import.kind { - ImportKind::Single { source, .. } => { - if let Some(ModuleOrUniformRoot::Module(module)) = import.imported_module.get() - && let Some(module) = module.opt_def_id() - { - self.find_cfg_stripped(&mut diag, &source.name, module) - } - } - _ => {} + if matches!(import.kind, ImportKind::Single { .. }) + && let Some(segment) = err.segment + && let Some(module) = err.module + { + self.find_cfg_stripped(&mut diag, &segment, module) } } @@ -916,10 +918,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { span, label, suggestion, + module, + segment_name, .. } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); + let module = if let Some(ModuleOrUniformRoot::Module(m)) = module { + m.opt_def_id() + } else { + None + }; let err = match self.make_path_suggestion( span, import.module_path.clone(), @@ -935,6 +944,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Applicability::MaybeIncorrect, )), candidates: None, + segment: Some(segment_name), + module, }, None => UnresolvedImportError { span, @@ -942,6 +953,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { note: None, suggestion, candidates: None, + segment: Some(segment_name), + module, }, }; return Some(err); @@ -990,6 +1003,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { note: None, suggestion: None, candidates: None, + segment: None, + module: None, }); } } @@ -1199,6 +1214,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } else { None }, + module: import.imported_module.get().and_then(|module| { + if let ModuleOrUniformRoot::Module(m) = module { + m.opt_def_id() + } else { + None + } + }), + segment: Some(ident.name), }) } else { // `resolve_ident_in_module` reported a privacy error. @@ -1368,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, @@ -1375,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/late.rs b/compiler/rustc_resolve/src/late.rs index 837c069b5997d..6c870ddfd227a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -6,8 +6,7 @@ //! If you wonder why there's no `early.rs`, that's because it's split into three files - //! `build_reduced_graph.rs`, `macros.rs` and `imports.rs`. -use crate::errors::ImportsCannotReferTo; -use crate::{path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding}; +use crate::{errors, path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding}; use crate::{BindingKey, Used}; use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult}; use crate::{ResolutionError, Resolver, Segment, UseError}; @@ -16,14 +15,13 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_errors::{ - codes::*, struct_span_code_err, Applicability, DiagArgValue, IntoDiagArg, StashKey, -}; +use rustc_errors::{codes::*, Applicability, DiagArgValue, IntoDiagArg, StashKey}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::{PrimTy, TraitCandidate}; +use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate}; use rustc_middle::middle::resolve_bound_vars::Set1; +use rustc_middle::ty::DelegationFnSig; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; use rustc_session::lint; @@ -44,14 +42,12 @@ type Res = def::Res; type IdentMap = FxHashMap; -use diagnostics::{ - ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind, -}; +use diagnostics::{ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime}; #[derive(Copy, Clone, Debug)] struct BindingInfo { span: Span, - annotation: BindingAnnotation, + annotation: BindingMode, } #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -798,7 +794,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, self.r.record_partial_res(ty.id, PartialRes::new(res)); visit::walk_ty(self, ty) } - TyKind::ImplTrait(node_id, _) => { + TyKind::ImplTrait(node_id, _, _) => { let candidates = self.lifetime_elision_candidates.take(); visit::walk_ty(self, ty); self.record_lifetime_params_for_impl_trait(*node_id); @@ -890,7 +886,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, kind: LifetimeBinderKind::Item, span: generics.span, }, - |this| visit::walk_foreign_item(this, foreign_item), + |this| visit::walk_item(this, foreign_item), ); } ForeignItemKind::Fn(box Fn { ref generics, .. }) => { @@ -902,13 +898,11 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, kind: LifetimeBinderKind::Function, span: generics.span, }, - |this| visit::walk_foreign_item(this, foreign_item), + |this| visit::walk_item(this, foreign_item), ); } ForeignItemKind::Static(..) => { - self.with_static_rib(def_kind, |this| { - visit::walk_foreign_item(this, foreign_item); - }); + self.with_static_rib(def_kind, |this| visit::walk_item(this, foreign_item)) } ForeignItemKind::MacCall(..) => { panic!("unexpanded macro in resolve!") @@ -1052,10 +1046,39 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, }); self.diag_metadata.current_function = previous_value; } + fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) { self.resolve_lifetime(lifetime, use_ctxt) } + fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) { + match arg { + // Lower the lifetime regularly; we'll resolve the lifetime and check + // it's a parameter later on in HIR lowering. + PreciseCapturingArg::Lifetime(_) => {} + + PreciseCapturingArg::Arg(path, id) => { + // we want `impl use` to try to resolve `C` as both a type parameter or + // a const parameter. Since the resolver specifically doesn't allow having + // two generic params with the same name, even if they're a different namespace, + // it doesn't really matter which we try resolving first, but just like + // `Ty::Param` we just fall back to the value namespace only if it's missing + // from the type namespace. + let mut check_ns = |ns| { + self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns).is_some() + }; + // Like `Ty::Param`, we try resolving this as both a const and a type. + if !check_ns(TypeNS) && check_ns(ValueNS) { + self.smart_resolve_path(*id, &None, path, PathSource::Expr(None)); + } else { + self.smart_resolve_path(*id, &None, path, PathSource::Type); + } + } + } + + visit::walk_precise_capturing_arg(self, arg) + } + fn visit_generics(&mut self, generics: &'ast Generics) { self.visit_generic_params(&generics.params, self.diag_metadata.current_self_item.is_some()); for p in &generics.where_clause.predicates { @@ -1637,22 +1660,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); - let missing_lifetime = MissingLifetime { - id: lifetime.id, - span: lifetime.ident.span, - kind: if elided { - MissingLifetimeKind::Ampersand - } else { - MissingLifetimeKind::Underscore - }, - count: 1, - }; + let kind = + if elided { MissingLifetimeKind::Ampersand } else { MissingLifetimeKind::Underscore }; + let missing_lifetime = + MissingLifetime { id: lifetime.id, span: lifetime.ident.span, kind, count: 1 }; let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for (i, rib) in self.lifetime_ribs.iter().enumerate().rev() { debug!(?rib.kind); match rib.kind { LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { - let res = self.create_fresh_lifetime(lifetime.ident, binder); + let res = self.create_fresh_lifetime(lifetime.ident, binder, kind); self.record_lifetime_res(lifetime.id, res, elision_candidate); return; } @@ -1674,18 +1691,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ); } LifetimeRibKind::AnonymousReportError => { - let (msg, note) = if elided { - ( - "`&` without an explicit lifetime name cannot be used here", - "explicit lifetime name needed here", - ) - } else { - ("`'_` cannot be used here", "`'_` is a reserved lifetime name") - }; - let mut diag = - struct_span_code_err!(self.r.dcx(), lifetime.ident.span, E0637, "{}", msg,); - diag.span_label(lifetime.ident.span, note); if elided { + let mut suggestion = None; for rib in self.lifetime_ribs[i..].iter().rev() { if let LifetimeRibKind::Generics { span, @@ -1693,19 +1700,23 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { .. } = &rib.kind { - diag.multipart_suggestion( - "consider introducing a higher-ranked lifetime here", - vec![ - (span.shrink_to_lo(), "for<'a> ".into()), - (lifetime.ident.span.shrink_to_hi(), "'a ".into()), - ], - Applicability::MachineApplicable, - ); + suggestion = + Some(errors::ElidedAnonymousLivetimeReportErrorSuggestion { + lo: span.shrink_to_lo(), + hi: lifetime.ident.span.shrink_to_hi(), + }); break; } } - } - diag.emit(); + self.r.dcx().emit_err(errors::ElidedAnonymousLivetimeReportError { + span: lifetime.ident.span, + suggestion, + }); + } else { + self.r.dcx().emit_err(errors::ExplicitAnonymousLivetimeReportError { + span: lifetime.ident.span, + }); + }; self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } @@ -1744,13 +1755,18 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn create_fresh_lifetime(&mut self, ident: Ident, binder: NodeId) -> LifetimeRes { + fn create_fresh_lifetime( + &mut self, + ident: Ident, + binder: NodeId, + kind: MissingLifetimeKind, + ) -> LifetimeRes { debug_assert_eq!(ident.name, kw::UnderscoreLifetime); debug!(?ident.span); // Leave the responsibility to create the `LocalDefId` to lowering. let param = self.r.next_node_id(); - let res = LifetimeRes::Fresh { param, binder }; + let res = LifetimeRes::Fresh { param, binder, kind }; self.record_lifetime_param(param, res); // Record the created lifetime parameter so lowering can pick it up and add it to HIR. @@ -1844,14 +1860,15 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }; let ident = Ident::new(kw::UnderscoreLifetime, elided_lifetime_span); + let kind = if segment.has_generic_args { + MissingLifetimeKind::Comma + } else { + MissingLifetimeKind::Brackets + }; let missing_lifetime = MissingLifetime { id: node_ids.start, span: elided_lifetime_span, - kind: if segment.has_generic_args { - MissingLifetimeKind::Comma - } else { - MissingLifetimeKind::Brackets - }, + kind, count: expected_lifetimes, }; let mut should_lint = true; @@ -1866,21 +1883,17 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. } | LifetimeRibKind::AnonymousWarn(_) => { let sess = self.r.tcx.sess; - let mut err = struct_span_code_err!( - sess.dcx(), - path_span, - E0726, - "implicit elided lifetime not allowed here" - ); - rustc_errors::add_elided_lifetime_in_path_suggestion( + let subdiag = rustc_errors::elided_lifetime_in_path_suggestion( sess.source_map(), - &mut err, expected_lifetimes, path_span, !segment.has_generic_args, elided_lifetime_span, ); - err.emit(); + self.r.dcx().emit_err(errors::ImplicitElidedLifetimeNotAllowedHere { + span: path_span, + subdiag, + }); should_lint = false; for id in node_ids { @@ -1897,7 +1910,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // Group all suggestions into the first record. let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { - let res = self.create_fresh_lifetime(ident, binder); + let res = self.create_fresh_lifetime(ident, binder, kind); self.record_lifetime_res( id, res, @@ -2315,7 +2328,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let report_error = |this: &Self, ns| { if this.should_report_errs() { let what = if ns == TypeNS { "type parameters" } else { "local variables" }; - this.r.dcx().emit_err(ImportsCannotReferTo { span: ident.span, what }); + this.r.dcx().emit_err(errors::ImportsCannotReferTo { span: ident.span, what }); } }; @@ -2533,7 +2546,17 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } ItemKind::Delegation(ref delegation) => { - self.resolve_delegation(delegation); + let span = delegation.path.segments.last().unwrap().ident.span; + self.with_generic_param_rib( + &[], + RibKind::Item(HasGenericParams::Yes(span), def_kind), + LifetimeRibKind::Generics { + binder: item.id, + kind: LifetimeBinderKind::Function, + span, + }, + |this| this.resolve_delegation(delegation), + ); } ItemKind::ExternCrate(..) => {} @@ -2625,29 +2648,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } if param.ident.name == kw::UnderscoreLifetime { - struct_span_code_err!( - self.r.dcx(), - param.ident.span, - E0637, - "`'_` cannot be used here" - ) - .with_span_label(param.ident.span, "`'_` is a reserved lifetime name") - .emit(); + self.r + .dcx() + .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }); // Record lifetime res, so lowering knows there is something fishy. self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } if param.ident.name == kw::StaticLifetime { - struct_span_code_err!( - self.r.dcx(), - param.ident.span, - E0262, - "invalid lifetime parameter name: `{}`", - param.ident, - ) - .with_span_label(param.ident.span, "'static is a reserved lifetime name") - .emit(); + self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { + span: param.ident.span, + lifetime: param.ident, + }); // Record lifetime res, so lowering knows there is something fishy. self.record_lifetime_param(param.id, LifetimeRes::Error); continue; @@ -2821,7 +2834,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { walk_assoc_item(self, generics, LifetimeBinderKind::Function, item); } AssocItemKind::Delegation(delegation) => { - self.resolve_delegation(delegation); + self.with_generic_param_rib( + &[], + RibKind::AssocItem, + LifetimeRibKind::Generics { + binder: item.id, + kind: LifetimeBinderKind::Function, + span: delegation.path.segments.last().unwrap().ident.span, + }, + |this| this.resolve_delegation(delegation), + ); } AssocItemKind::Type(box TyAlias { generics, .. }) => self .with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { @@ -3071,16 +3093,28 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } AssocItemKind::Delegation(box delegation) => { debug!("resolve_implementation AssocItemKind::Delegation"); - self.check_trait_item( - item.id, - item.ident, - &item.kind, - ValueNS, - item.span, - seen_trait_items, - |i, s, c| MethodNotMemberOfTrait(i, s, c), + self.with_generic_param_rib( + &[], + RibKind::AssocItem, + LifetimeRibKind::Generics { + binder: item.id, + kind: LifetimeBinderKind::Function, + span: delegation.path.segments.last().unwrap().ident.span, + }, + |this| { + this.check_trait_item( + item.id, + item.ident, + &item.kind, + ValueNS, + item.span, + seen_trait_items, + |i, s, c| MethodNotMemberOfTrait(i, s, c), + ); + + this.resolve_delegation(delegation) + }, ); - self.resolve_delegation(delegation); } AssocItemKind::MacCall(_) => { panic!("unexpanded macro in resolve!") @@ -3618,14 +3652,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn try_resolve_as_non_binding( &mut self, pat_src: PatternSource, - ann: BindingAnnotation, + ann: BindingMode, ident: Ident, has_sub: bool, ) -> Option { // An immutable (no `mut`) by-value (no `ref`) binding pattern without // a sub pattern (no `@ $pat`) is syntactically ambiguous as it could // also be interpreted as a path to e.g. a constant, variant, etc. - let is_syntactic_ambiguity = !has_sub && ann == BindingAnnotation::NONE; + let is_syntactic_ambiguity = !has_sub && ann == BindingMode::NONE; let ls_binding = self.maybe_resolve_ident_in_lexical_scope(ident, ValueNS)?; let (res, binding) = match ls_binding { @@ -4665,7 +4699,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } fn lint_unused_qualifications(&mut self, path: &[Segment], ns: Namespace, finalize: Finalize) { - if path.iter().any(|seg| seg.ident.span.from_expansion()) { + // Don't lint on global paths because the user explicitly wrote out the full path. + if let Some(seg) = path.first() + && seg.ident.name == kw::PathRoot + { + return; + } + + if finalize.path_span.from_expansion() + || path.iter().any(|seg| seg.ident.span.from_expansion()) + { return; } @@ -4705,12 +4748,13 @@ struct ItemInfoCollector<'a, 'b, 'tcx> { impl ItemInfoCollector<'_, '_, '_> { fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId) { - let def_id = self.r.local_def_id(id); - self.r.fn_parameter_counts.insert(def_id, sig.decl.inputs.len()); - - if sig.decl.has_self() { - self.r.has_self.insert(def_id); - } + let sig = DelegationFnSig { + header: sig.header, + param_count: sig.decl.inputs.len(), + has_self: sig.decl.has_self(), + c_variadic: sig.decl.c_variadic(), + }; + self.r.delegation_fn_sigs.insert(self.r.local_def_id(id), sig); } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 47f8cc5613991..64451030adf0f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -24,7 +24,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; -use rustc_hir::PrimTy; +use rustc_hir::{MissingLifetimeKind, PrimTy}; use rustc_session::lint; use rustc_session::Session; use rustc_span::edit_distance::find_best_match_for_name; @@ -109,18 +109,6 @@ pub(super) struct MissingLifetime { pub count: usize, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub(super) enum MissingLifetimeKind { - /// An explicit `'_`. - Underscore, - /// An elided lifetime `&' ty`. - Ampersand, - /// An elided lifetime in brackets with written brackets. - Comma, - /// An elided lifetime with elided brackets. - Brackets, -} - /// Description of the lifetimes appearing in a function parameter. /// This is used to provide a literal explanation to the elision failure. #[derive(Clone, Debug)] @@ -1604,18 +1592,23 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { match (res, source) { ( - Res::Def(DefKind::Macro(MacroKind::Bang), _), + Res::Def(DefKind::Macro(MacroKind::Bang), def_id), PathSource::Expr(Some(Expr { kind: ExprKind::Index(..) | ExprKind::Call(..), .. })) | PathSource::Struct, ) => { + // Don't suggest macro if it's unstable. + let suggestable = def_id.is_local() + || self.r.tcx.lookup_stability(def_id).map_or(true, |s| s.is_stable()); + err.span_label(span, fallback_label.to_string()); // Don't suggest `!` for a macro invocation if there are generic args if path .last() .is_some_and(|segment| !segment.has_generic_args && !segment.has_lifetime_args) + && suggestable { err.span_suggestion_verbose( span.shrink_to_hi(), @@ -2046,7 +2039,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn { called }, ast::AssocItemKind::Type(..) => AssocSuggestion::AssocType, ast::AssocItemKind::Delegation(..) - if self.r.has_self.contains(&self.r.local_def_id(assoc_item.id)) => + if self.r.delegation_fn_sigs[&self.r.local_def_id(assoc_item.id)] + .has_self => { AssocSuggestion::MethodWithSelf { called } } @@ -2069,7 +2063,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if filter_fn(res) { let def_id = res.def_id(); let has_self = match def_id.as_local() { - Some(def_id) => self.r.has_self.contains(&def_id), + Some(def_id) => { + self.r.delegation_fn_sigs.get(&def_id).map_or(false, |sig| sig.has_self) + } None => self .r .tcx @@ -3128,7 +3124,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .inputs .iter() .filter_map(|param| match ¶m.ty.kind { - TyKind::ImplTrait(_, bounds) => Some(bounds), + TyKind::ImplTrait(_, bounds, _) => Some(bounds), _ => None, }) .flat_map(|bounds| bounds.into_iter()) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index dfc2d029d4cfd..af0b4792136cf 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -38,12 +38,12 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{FreezeReadGuard, Lrc}; use rustc_errors::{Applicability, Diag, ErrCode}; -use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind}; +use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::NonMacroAttrKind; use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes, PerNS}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap, LocalDefIdSet}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{PrimTy, TraitCandidate}; use rustc_index::IndexVec; @@ -52,8 +52,8 @@ use rustc_middle::metadata::ModChild; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::query::Providers; use rustc_middle::span_bug; -use rustc_middle::ty::{self, MainDefinition, RegisteredTools, TyCtxt, TyCtxtFeed}; -use rustc_middle::ty::{Feed, ResolverGlobalCtxt, ResolverOutputs}; +use rustc_middle::ty::{self, DelegationFnSig, Feed, MainDefinition, RegisteredTools}; +use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs, TyCtxt, TyCtxtFeed}; use rustc_query_system::ich::StableHashingContext; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; use rustc_session::lint::LintBuffer; @@ -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() { @@ -415,6 +415,19 @@ enum PathResult<'a> { label: String, suggestion: Option, is_error_from_last_segment: bool, + /// The final module being resolved, for instance: + /// + /// ```compile_fail + /// mod a { + /// mod b { + /// mod c {} + /// } + /// } + /// + /// use a::not_exist::c; + /// ``` + /// + /// In this case, `module` will point to `a`. module: Option>, /// The segment name of target segment_name: Symbol, @@ -946,7 +959,7 @@ enum BuiltinMacroState { } struct DeriveData { - resolutions: DeriveResolutions, + resolutions: Vec, helper_attrs: Vec<(usize, Ident)>, has_derive_copy: bool, } @@ -979,7 +992,6 @@ pub struct Resolver<'a, 'tcx> { extern_prelude: FxHashMap>, /// N.B., this is used only for better diagnostics, not name resolution itself. - has_self: LocalDefIdSet, field_def_ids: LocalDefIdMap<&'tcx [DefId]>, /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax. @@ -1029,7 +1041,7 @@ pub struct Resolver<'a, 'tcx> { block_map: NodeMap>, /// A fake module that contains no definition and no prelude. Used so that /// some AST passes can generate identifiers that only resolve to local or - /// language items. + /// lang items. empty_module: Module<'a>, module_map: FxHashMap>, binding_parent_modules: FxHashMap, Module<'a>>, @@ -1136,8 +1148,7 @@ pub struct Resolver<'a, 'tcx> { legacy_const_generic_args: FxHashMap>>, /// Amount of lifetime parameters for each item in the crate. item_generics_num_lifetimes: FxHashMap, - /// Amount of parameters for each function in the crate. - fn_parameter_counts: LocalDefIdMap, + delegation_fn_sigs: LocalDefIdMap, main_def: Option, trait_impls: FxIndexMap>, @@ -1386,7 +1397,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { prelude: None, extern_prelude, - has_self: Default::default(), field_def_ids: Default::default(), field_visibility_spans: FxHashMap::default(), @@ -1495,7 +1505,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), all_macro_rules: Default::default(), - fn_parameter_counts: Default::default(), + delegation_fn_sigs: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); @@ -1605,12 +1615,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .into_items() .map(|(k, f)| (k, f.key())) .collect(), - def_id_to_node_id: self.def_id_to_node_id, trait_map: self.trait_map, lifetime_elision_allowed: self.lifetime_elision_allowed, lint_buffer: Steal::new(self.lint_buffer), - has_self: self.has_self, - fn_parameter_counts: self.fn_parameter_counts, + delegation_fn_sigs: self.delegation_fn_sigs, }; ResolverOutputs { global_ctxt, ast_lowering } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index dbca2f9895d20..82e41b6c31436 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -14,8 +14,8 @@ use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; -use rustc_errors::{codes::*, struct_span_code_err, Applicability, StashKey}; -use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand}; +use rustc_errors::{Applicability, StashKey}; +use rustc_expand::base::{Annotatable, DeriveResolution, Indeterminate, ResolverExpand}; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion}; @@ -123,20 +123,18 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { match nested_meta.ident() { Some(ident) => { if let Some(old_ident) = registered_tools.replace(ident) { - let msg = format!("{} `{}` was already registered", "tool", ident); - tcx.dcx() - .struct_span_err(ident.span, msg) - .with_span_label(old_ident.span, "already registered here") - .emit(); + tcx.dcx().emit_err(errors::ToolWasAlreadyRegistered { + span: ident.span, + tool: ident, + old_ident_span: old_ident.span, + }); } } None => { - let msg = format!("`{}` only accepts identifiers", sym::register_tool); - let span = nested_meta.span(); - tcx.dcx() - .struct_span_err(span, msg) - .with_span_label(span, "not an identifier") - .emit(); + tcx.dcx().emit_err(errors::ToolOnlyAcceptsIdentifiers { + span: nested_meta.span(), + tool: sym::register_tool, + }); } } } @@ -346,7 +344,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { &mut self, expn_id: LocalExpnId, force: bool, - derive_paths: &dyn Fn() -> DeriveResolutions, + derive_paths: &dyn Fn() -> Vec, ) -> Result<(), Indeterminate> { // Block expansion of the container until we resolve all derives in it. // This is required for two reasons: @@ -362,11 +360,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { has_derive_copy: false, }); let parent_scope = self.invocation_parent_scopes[&expn_id]; - for (i, (path, _, opt_ext, _)) in entry.resolutions.iter_mut().enumerate() { - if opt_ext.is_none() { - *opt_ext = Some( + for (i, resolution) in entry.resolutions.iter_mut().enumerate() { + if resolution.exts.is_none() { + resolution.exts = Some( match self.resolve_macro_path( - path, + &resolution.path, Some(MacroKind::Derive), &parent_scope, true, @@ -374,7 +372,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { - let last_seg = path.segments.last().unwrap(); + let last_seg = resolution.path.segments.last().unwrap(); let span = last_seg.ident.span.normalize_to_macros_2_0(); entry.helper_attrs.extend( ext.helper_attrs @@ -418,7 +416,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { Ok(()) } - fn take_derive_resolutions(&mut self, expn_id: LocalExpnId) -> Option { + fn take_derive_resolutions(&mut self, expn_id: LocalExpnId) -> Option> { self.derive_data.remove(&expn_id).map(|data| data.resolutions) } @@ -485,13 +483,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Report errors for the resolved macro. for segment in &path.segments { if let Some(args) = &segment.args { - self.dcx().span_err(args.span(), "generic arguments in macro path"); + self.dcx().emit_err(errors::GenericArgumentsInMacroPath { span: args.span() }); } if kind == MacroKind::Attr && segment.ident.as_str().starts_with("rustc") { - self.dcx().span_err( - segment.ident.span, - "attributes starting with `rustc` are reserved for use by the `rustc` compiler", - ); + self.dcx().emit_err(errors::AttributesStartingWithRustcAreReserved { + span: segment.ident.span, + }); } } @@ -535,6 +532,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut err = MacroExpectedFound { span: path.span, expected, + article, found: res.descr(), macro_path: &path_str, remove_surrounding_derive: None, @@ -550,10 +548,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { err.add_as_non_derive = Some(AddAsNonDerive { macro_path: &path_str }); } - self.dcx() - .create_err(err) - .with_span_label(path.span, format!("not {article} {expected}")) - .emit(); + self.dcx().emit_err(err); return Ok((self.dummy_ext(kind), Res::Err)); } @@ -872,13 +867,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) { if let Some(Res::NonMacroAttr(kind)) = res { if kind != NonMacroAttrKind::Tool && binding.map_or(true, |b| b.is_import()) { - let msg = - format!("cannot use {} {} through an import", kind.article(), kind.descr()); - let mut err = self.dcx().struct_span_err(span, msg); - if let Some(binding) = binding { - err.span_note(binding.span, format!("the {} imported here", kind.descr())); - } - err.emit(); + let binding_span = binding.map(|binding| binding.span); + self.dcx().emit_err(errors::CannotUseThroughAnImport { + span, + article: kind.article(), + descr: kind.descr(), + binding_span, + }); } } } @@ -889,10 +884,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if ident.name == sym::cfg || ident.name == sym::cfg_attr { let macro_kind = self.get_macro(res).map(|macro_data| macro_data.ext.macro_kind()); if macro_kind.is_some() && sub_namespace_match(macro_kind, Some(MacroKind::Attr)) { - self.dcx().span_err( - ident.span, - format!("name `{ident}` is reserved in attribute namespace"), - ); + self.dcx() + .emit_err(errors::NameReservedInAttributeNamespace { span: ident.span, ident }); } } } @@ -916,19 +909,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { rule_spans = Vec::new(); } BuiltinMacroState::AlreadySeen(span) => { - struct_span_code_err!( - self.dcx(), - item.span, - E0773, - "attempted to define built-in macro more than once" - ) - .with_span_note(span, "previously defined here") - .emit(); + self.dcx().emit_err(errors::AttemptToDefineBuiltinMacroTwice { + span: item.span, + note_span: span, + }); } } } else { - let msg = format!("cannot find a built-in macro with name `{}`", item.ident); - self.dcx().span_err(item.span, msg); + self.dcx().emit_err(errors::CannotFindBuiltinMacroWithName { + span: item.span, + ident: item.ident, + }); } } diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 0ebcad3cdb809..aace0f3fef9f1 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -6,8 +6,8 @@ use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; +use std::mem; use std::ops::Range; -use std::{cmp, mem}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum DocFragmentKind { @@ -129,17 +129,20 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) { let Some(min_indent) = docs .iter() .map(|fragment| { - fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { - if line.chars().all(|c| c.is_whitespace()) { - min_indent - } else { + fragment + .doc + .as_str() + .lines() + .filter(|line| line.chars().any(|c| !c.is_whitespace())) + .map(|line| { // Compare against either space or tab, ignoring whether they are // mixed or not. let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count(); - cmp::min(min_indent, whitespace) - + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add } - } - }) + whitespace + + (if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }) + }) + .min() + .unwrap_or(usize::MAX) }) .min() else { @@ -151,13 +154,13 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) { continue; } - let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { + let indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { min_indent - add } else { min_indent }; - fragment.indent = min_indent; + fragment.indent = indent; } } @@ -194,12 +197,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 +214,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 +495,36 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option { /// 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, fragments: &[DocFragment], ) -> Option { + 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_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml new file mode 100644 index 0000000000000..aea2f7dda7f36 --- /dev/null +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rustc_sanitizers" +version = "0.0.0" +edition = "2021" + +[dependencies] +bitflags = "2.5.0" +tracing = "0.1" +twox-hash = "1.6.3" +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_hir = { path = "../rustc_hir" } +rustc_middle = { path = "../rustc_middle" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_sanitizers/README.md b/compiler/rustc_sanitizers/README.md new file mode 100644 index 0000000000000..d2e8f5d3a97b5 --- /dev/null +++ b/compiler/rustc_sanitizers/README.md @@ -0,0 +1,2 @@ +The `rustc_sanitizers` crate contains the source code for providing support for +the [sanitizers](https://github.com/google/sanitizers) to the Rust compiler. diff --git a/compiler/rustc_sanitizers/src/cfi/mod.rs b/compiler/rustc_sanitizers/src/cfi/mod.rs new file mode 100644 index 0000000000000..90dab5e0333a1 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/mod.rs @@ -0,0 +1,6 @@ +//! LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI support for the Rust compiler. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +pub mod typeid; +pub use crate::cfi::typeid::{typeid_for_fnabi, typeid_for_instance, TypeIdOptions}; diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs new file mode 100644 index 0000000000000..40cd0c14b05f4 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -0,0 +1,753 @@ +//! Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium +//! C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that +//! are not used across the FFI boundary. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use rustc_data_structures::base_n; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_middle::bug; +use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::{ + self, Const, ExistentialPredicate, FloatTy, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, TypeFoldable, UintTy, +}; +use rustc_span::def_id::DefId; +use rustc_span::sym; +use rustc_target::abi::Integer; +use rustc_target::spec::abi::Abi; +use std::fmt::Write as _; +use tracing::instrument; + +use crate::cfi::typeid::itanium_cxx_abi::transform::{TransformTy, TransformTyOptions}; +use crate::cfi::typeid::TypeIdOptions; + +/// Options for encode_ty. +pub type EncodeTyOptions = TypeIdOptions; + +/// Substitution dictionary key. +#[derive(Eq, Hash, PartialEq)] +pub enum DictKey<'tcx> { + Ty(Ty<'tcx>, TyQ), + Region(Region<'tcx>), + Const(Const<'tcx>), + Predicate(ExistentialPredicate<'tcx>), +} + +/// Type and extended type qualifiers. +#[derive(Eq, Hash, PartialEq)] +pub enum TyQ { + None, + Const, + Mut, +} + +/// Substitutes a component if found in the substitution dictionary (see +/// ). +fn compress<'tcx>( + dict: &mut FxHashMap, usize>, + key: DictKey<'tcx>, + comp: &mut String, +) { + match dict.get(&key) { + Some(num) => { + comp.clear(); + let _ = write!(comp, "S{}_", to_seq_id(*num)); + } + None => { + dict.insert(key, dict.len()); + } + } +} + +/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust +/// types that are not used at the FFI boundary. +fn encode_args<'tcx>( + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, + dict: &mut FxHashMap, usize>, + options: EncodeTyOptions, +) -> String { + // [IE] as part of vendor extended type + let mut s = String::new(); + let args: Vec> = args.iter().collect(); + if !args.is_empty() { + s.push('I'); + for arg in args { + match arg.unpack() { + GenericArgKind::Lifetime(region) => { + s.push_str(&encode_region(region, dict)); + } + GenericArgKind::Type(ty) => { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + GenericArgKind::Const(c) => { + s.push_str(&encode_const(tcx, c, dict, options)); + } + } + } + s.push('E'); + } + s +} + +/// Encodes a const using the Itanium C++ ABI as a literal argument (see +/// ). +fn encode_const<'tcx>( + tcx: TyCtxt<'tcx>, + c: Const<'tcx>, + dict: &mut FxHashMap, usize>, + options: EncodeTyOptions, +) -> String { + // L[n][]E as literal argument + let mut s = String::from('L'); + + match c.kind() { + // Const parameters + ty::ConstKind::Param(..) => { + // LE as literal argument + + // Element type + s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + } + + // Literal arguments + ty::ConstKind::Value(..) => { + // L[n]E as literal argument + + // Element type + s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + + // The only allowed types of const values are bool, u8, u16, u32, + // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The + // bool value false is encoded as 0 and true as 1. + match c.ty().kind() { + ty::Int(ity) => { + let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; + if val < 0 { + s.push('n'); + } + let _ = write!(s, "{val}"); + } + ty::Uint(_) => { + let val = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); + let _ = write!(s, "{val}"); + } + ty::Bool => { + let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); + let _ = write!(s, "{val}"); + } + _ => { + bug!("encode_const: unexpected type `{:?}`", c.ty()); + } + } + } + + _ => { + bug!("encode_const: unexpected kind `{:?}`", c.kind()); + } + } + + // Close the "L..E" pair + s.push('E'); + + compress(dict, DictKey::Const(c), &mut s); + + s +} + +/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_fnsig<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: &FnSig<'tcx>, + dict: &mut FxHashMap, usize>, + options: TypeIdOptions, +) -> String { + // Function types are delimited by an "F..E" pair + let mut s = String::from("F"); + + let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); + match fn_sig.abi { + Abi::C { .. } => { + encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); + } + _ => { + encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + } + } + + // Encode the return type + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); + let mut type_folder = TransformTy::new(tcx, transform_ty_options); + let ty = fn_sig.output().fold_with(&mut type_folder); + s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); + + // Encode the parameter types + let tys = fn_sig.inputs(); + if !tys.is_empty() { + for ty in tys { + let ty = ty.fold_with(&mut type_folder); + s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); + } + + if fn_sig.c_variadic { + s.push('z'); + } + } else { + if fn_sig.c_variadic { + s.push('z'); + } else { + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + s.push('v') + } + } + + // Close the "F..E" pair + s.push('E'); + + s +} + +/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + predicate: ty::PolyExistentialPredicate<'tcx>, + dict: &mut FxHashMap, usize>, + options: EncodeTyOptions, +) -> String { + // u[IE], where is , as vendor + // extended type. + let mut s = String::new(); + match predicate.as_ref().skip_binder() { + ty::ExistentialPredicate::Trait(trait_ref) => { + let name = encode_ty_name(tcx, trait_ref.def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_args(tcx, trait_ref.args, dict, options)); + } + ty::ExistentialPredicate::Projection(projection) => { + let name = encode_ty_name(tcx, projection.def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_args(tcx, projection.args, dict, options)); + match projection.term.unpack() { + TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)), + TermKind::Const(c) => s.push_str(&encode_const(tcx, c, dict, options)), + } + } + ty::ExistentialPredicate::AutoTrait(def_id) => { + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + } + }; + compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s); + s +} + +/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +fn encode_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, + dict: &mut FxHashMap, usize>, + options: EncodeTyOptions, +) -> String { + // E as part of vendor extended type + let mut s = String::new(); + let predicates: Vec> = predicates.iter().collect(); + for predicate in predicates { + s.push_str(&encode_predicate(tcx, predicate, dict, options)); + } + s +} + +/// Encodes a region using the Itanium C++ ABI as a vendor extended type. +fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap, usize>) -> String { + // u6region[I[][]E] as vendor extended type + let mut s = String::new(); + match region.kind() { + RegionKind::ReBound(debruijn, r) => { + s.push_str("u6regionI"); + // Debruijn index, which identifies the binder, as region disambiguator + let num = debruijn.index() as u64; + if num > 0 { + s.push_str(&to_disambiguator(num)); + } + // Index within the binder + let _ = write!(s, "{}", r.var.index() as u64); + s.push('E'); + compress(dict, DictKey::Region(region), &mut s); + } + RegionKind::ReErased => { + s.push_str("u6region"); + compress(dict, DictKey::Region(region), &mut s); + } + RegionKind::ReEarlyParam(..) + | RegionKind::ReLateParam(..) + | RegionKind::ReStatic + | RegionKind::ReError(_) + | RegionKind::ReVar(..) + | RegionKind::RePlaceholder(..) => { + bug!("encode_region: unexpected `{:?}`", region.kind()); + } + } + s +} + +/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for +/// Rust types that are not used at the FFI boundary. +#[instrument(level = "trace", skip(tcx, dict))] +pub fn encode_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + dict: &mut FxHashMap, usize>, + options: EncodeTyOptions, +) -> String { + let mut typeid = String::new(); + + match ty.kind() { + // Primitive types + + // Rust's bool has the same layout as C17's _Bool, that is, its size and alignment are + // implementation-defined. Any bool can be cast into an integer, taking on the values 1 + // (true) or 0 (false). + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) + ty::Bool => { + typeid.push('b'); + } + + ty::Int(..) | ty::Uint(..) => { + // u as vendor extended type + let mut s = String::from(match ty.kind() { + ty::Int(IntTy::I8) => "u2i8", + ty::Int(IntTy::I16) => "u3i16", + ty::Int(IntTy::I32) => "u3i32", + ty::Int(IntTy::I64) => "u3i64", + ty::Int(IntTy::I128) => "u4i128", + ty::Int(IntTy::Isize) => "u5isize", + ty::Uint(UintTy::U8) => "u2u8", + ty::Uint(UintTy::U16) => "u3u16", + ty::Uint(UintTy::U32) => "u3u32", + ty::Uint(UintTy::U64) => "u3u64", + ty::Uint(UintTy::U128) => "u4u128", + ty::Uint(UintTy::Usize) => "u5usize", + _ => bug!("encode_ty: unexpected `{:?}`", ty.kind()), + }); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Rust's f16, f32, f64, and f126 half (16-bit), single (32-bit), double (64-bit), and + // quad (128-bit) precision floating-point types have IEEE-754 binary16, binary32, + // binary64, and binary128 floating-point layouts, respectively. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#fixed-width-floating-point-types.) + ty::Float(float_ty) => { + typeid.push_str(match float_ty { + FloatTy::F16 => "Dh", + FloatTy::F32 => "f", + FloatTy::F64 => "d", + FloatTy::F128 => "g", + }); + } + + ty::Char => { + // u4char as vendor extended type + let mut s = String::from("u4char"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Str => { + // u3str as vendor extended type + let mut s = String::from("u3str"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Never => { + // u5never as vendor extended type + let mut s = String::from("u5never"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Compound types + // () in Rust is equivalent to void return type in C + _ if ty.is_unit() => { + typeid.push('v'); + } + + // Sequence types + ty::Tuple(tys) => { + // u5tupleIE as vendor extended type + let mut s = String::from("u5tupleI"); + for ty in tys.iter() { + s.push_str(&encode_ty(tcx, ty, dict, options)); + } + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Array(ty0, len) => { + // A + let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); + let mut s = String::from("A"); + let _ = write!(s, "{}", &len); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Pat(ty0, pat) => { + // u3patIE as vendor extended type + let mut s = String::from("u3patI"); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + write!(s, "{:?}", **pat).unwrap(); + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Slice(ty0) => { + // u5sliceIE as vendor extended type + let mut s = String::from("u5sliceI"); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // User-defined types + ty::Adt(adt_def, args) => { + let mut s = String::new(); + let def_id = adt_def.did(); + if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) { + // Use user-defined CFI encoding for type + if let Some(value_str) = cfi_encoding.value_str() { + let value_str = value_str.to_string(); + let str = value_str.trim(); + if !str.is_empty() { + s.push_str(str); + // Don't compress user-defined builtin types (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let builtin_types = [ + "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y", + "n", "o", "f", "d", "e", "g", "z", "Dh", + ]; + if !builtin_types.contains(&str) { + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + } + } else { + #[allow( + rustc::diagnostic_outside_of_impl, + rustc::untranslatable_diagnostic + )] + tcx.dcx() + .struct_span_err( + cfi_encoding.span, + format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), + ) + .emit(); + } + } else { + bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind()); + } + } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() { + // For cross-language LLVM CFI support, the encoding must be compatible at the FFI + // boundary. For instance: + // + // struct type1 {}; + // void foo(struct type1* bar) {} + // + // Is encoded as: + // + // _ZTSFvP5type1E + // + // So, encode any repr(C) user-defined type for extern function types with the "C" + // calling convention (or extern types [i.e., ty::Foreign]) as , where + // is . + let name = tcx.item_name(def_id).to_string(); + let _ = write!(s, "{}{}", name.len(), &name); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + } else { + // u[IE], where is + // , as vendor extended type. + let name = encode_ty_name(tcx, def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_args(tcx, args, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + } + typeid.push_str(&s); + } + + ty::Foreign(def_id) => { + // , where is + let mut s = String::new(); + if let Some(cfi_encoding) = tcx.get_attr(*def_id, sym::cfi_encoding) { + // Use user-defined CFI encoding for type + if let Some(value_str) = cfi_encoding.value_str() { + if !value_str.to_string().trim().is_empty() { + s.push_str(value_str.to_string().trim()); + } else { + #[allow( + rustc::diagnostic_outside_of_impl, + rustc::untranslatable_diagnostic + )] + tcx.dcx() + .struct_span_err( + cfi_encoding.span, + format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), + ) + .emit(); + } + } else { + bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind()); + } + } else { + let name = tcx.item_name(*def_id).to_string(); + let _ = write!(s, "{}{}", name.len(), &name); + } + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Function types + ty::FnDef(def_id, args) | ty::Closure(def_id, args) => { + // u[IE], where is , + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_args(tcx, args, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::CoroutineClosure(def_id, args) => { + // u[IE], where is , + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args()); + s.push_str(&encode_args(tcx, parent_args, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::Coroutine(def_id, args, ..) => { + // u[IE], where is , + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + // Encode parent args only + s.push_str(&encode_args( + tcx, + tcx.mk_args(args.as_coroutine().parent_args()), + dict, + options, + )); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Pointer types + ty::Ref(region, ty0, ..) => { + // [U3mut]u3refIE as vendor extended type qualifier and type + let mut s = String::new(); + s.push_str("u3refI"); + s.push_str(&encode_ty(tcx, *ty0, dict, options)); + s.push('E'); + compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s); + if ty.is_mutable_ptr() { + s = format!("{}{}", "U3mut", &s); + compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s); + } + typeid.push_str(&s); + } + + ty::RawPtr(ptr_ty, _mutbl) => { + // FIXME: This can definitely not be so spaghettified. + // P[K] + let mut s = String::new(); + s.push_str(&encode_ty(tcx, *ptr_ty, dict, options)); + if !ty.is_mutable_ptr() { + s = format!("{}{}", "K", &s); + compress(dict, DictKey::Ty(*ptr_ty, TyQ::Const), &mut s); + }; + s = format!("{}{}", "P", &s); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + ty::FnPtr(fn_sig) => { + // PFE + let mut s = String::from("P"); + s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::empty())); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Trait types + ty::Dynamic(predicates, region, kind) => { + // u3dynIE, where is , as + // vendor extended type. + let mut s = String::from(match kind { + ty::Dyn => "u3dynI", + ty::DynStar => "u7dynstarI", + }); + s.push_str(&encode_predicates(tcx, predicates, dict, options)); + s.push_str(&encode_region(*region, dict)); + s.push('E'); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Type parameters + ty::Param(..) => { + // u5param as vendor extended type + let mut s = String::from("u5param"); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // Unexpected types + ty::Alias(..) + | ty::Bound(..) + | ty::Error(..) + | ty::CoroutineWitness(..) + | ty::Infer(..) + | ty::Placeholder(..) => { + bug!("encode_ty: unexpected `{:?}`", ty.kind()); + } + }; + + typeid +} + +/// Encodes a ty:Ty name, including its crate and path disambiguators and names. +fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { + // Encode for use in u[IE], where + // is , using v0's without v0's extended form of paths: + // + // N..N + // C + // .. + // + // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: + // + // pub type Type1 = impl Send; + // let _: Type1 = >::foo; + // fn foo1(_: Type1) { } + // + // pub type Type2 = impl Send; + // let _: Type2 = >::foo; + // fn foo2(_: Type2) { } + // + // pub type Type3 = impl Send; + // let _: Type3 = >::foo; + // fn foo3(_: Type3) { } + // + // pub type Type4 = impl Send; + // let _: Type4 = as Trait1>::foo; + // fn foo3(_: Type4) { } + // + // Are encoded as: + // + // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE + // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE + // + // The reason for not using v0's extended form of paths is to use a consistent and simpler + // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e., + // keep symbol names close to how methods are represented in error messages). See + // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. + let mut s = String::new(); + + // Start and namespace tags + let mut def_path = tcx.def_path(def_id); + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + s.push('N'); + s.push_str(match disambiguated_data.data { + hir::definitions::DefPathData::Impl => "I", // Not specified in v0's + hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's + hir::definitions::DefPathData::TypeNs(..) => "t", + hir::definitions::DefPathData::ValueNs(..) => "v", + hir::definitions::DefPathData::Closure => "C", + hir::definitions::DefPathData::Ctor => "c", + hir::definitions::DefPathData::AnonConst => "k", + hir::definitions::DefPathData::OpaqueTy => "i", + hir::definitions::DefPathData::CrateRoot + | hir::definitions::DefPathData::Use + | hir::definitions::DefPathData::GlobalAsm + | hir::definitions::DefPathData::MacroNs(..) + | hir::definitions::DefPathData::LifetimeNs(..) + | hir::definitions::DefPathData::AnonAdt => { + bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); + } + }); + } + + // Crate disambiguator and name + s.push('C'); + s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64())); + let crate_name = tcx.crate_name(def_path.krate).to_string(); + let _ = write!(s, "{}{}", crate_name.len(), &crate_name); + + // Disambiguators and names + def_path.data.reverse(); + for disambiguated_data in &def_path.data { + let num = disambiguated_data.disambiguator as u64; + if num > 0 { + s.push_str(&to_disambiguator(num)); + } + + let name = disambiguated_data.data.to_string(); + let _ = write!(s, "{}", name.len()); + + // Prepend a '_' if name starts with a digit or '_' + if let Some(first) = name.as_bytes().first() { + if first.is_ascii_digit() || *first == b'_' { + s.push('_'); + } + } else { + bug!("encode_ty_name: invalid name `{:?}`", name); + } + + s.push_str(&name); + } + + s +} + +/// Converts a number to a disambiguator (see +/// ). +fn to_disambiguator(num: u64) -> String { + if let Some(num) = num.checked_sub(1) { + format!("s{}_", base_n::encode(num as u128, base_n::ALPHANUMERIC_ONLY)) + } else { + "s_".to_string() + } +} + +/// Converts a number to a sequence number (see +/// ). +fn to_seq_id(num: usize) -> String { + if let Some(num) = num.checked_sub(1) { + base_n::encode(num as u128, base_n::CASE_INSENSITIVE).to_uppercase() + } else { + "".to_string() + } +} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs new file mode 100644 index 0000000000000..b6182dc4e635d --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs @@ -0,0 +1,123 @@ +//! Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow +//! Integrity (CFI) and cross-language LLVM CFI support. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::bug; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_target::abi::call::{Conv, FnAbi, PassMode}; +use tracing::instrument; + +mod encode; +mod transform; +use crate::cfi::typeid::itanium_cxx_abi::encode::{encode_ty, DictKey, EncodeTyOptions}; +use crate::cfi::typeid::itanium_cxx_abi::transform::{ + transform_instance, TransformTy, TransformTyOptions, +}; +use crate::cfi::typeid::TypeIdOptions; + +/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor +/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. +#[instrument(level = "trace", skip(tcx))] +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions + // its type. + let mut typeid = String::from("_Z"); + + // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type + // metadata identifiers for function pointers. The typeinfo name encoding is a two-character + // code (i.e., 'TS') prefixed to the type encoding for the function. + typeid.push_str("TS"); + + // Function types are delimited by an "F..E" pair + typeid.push('F'); + + // A dictionary of substitution candidates used for compression (see + // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). + let mut dict: FxHashMap, usize> = FxHashMap::default(); + + let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + match fn_abi.conv { + Conv::C => { + encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); + } + _ => { + encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); + } + } + + // Encode the return type + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); + let mut type_folder = TransformTy::new(tcx, transform_ty_options); + let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + + // Encode the parameter types + + // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how + // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR + // interpretation today will allow skipped arguments to simply not be passed at a call-site. + if !fn_abi.c_variadic { + let mut pushed_arg = false; + for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { + pushed_arg = true; + let ty = arg.layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + if !pushed_arg { + // Empty parameter lists, whether declared as () or conventionally as (void), are + // encoded with a void parameter specifier "v". + typeid.push('v'); + } + } else { + for n in 0..fn_abi.fixed_count as usize { + if fn_abi.args[n].mode == PassMode::Ignore { + continue; + } + let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + + typeid.push('z'); + } + + // Close the "F..E" pair + typeid.push('E'); + + // Add encoding suffixes + if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + typeid.push_str(".normalized"); + } + + if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) { + typeid.push_str(".generalized"); + } + + typeid +} + +/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with +/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. +#[instrument(level = "trace", skip(tcx))] +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + options: TypeIdOptions, +) -> String { + let transform_ty_options = TransformTyOptions::from_bits(options.bits()) + .unwrap_or_else(|| bug!("typeid_for_instance: invalid option(s) `{:?}`", options.bits())); + let instance = transform_instance(tcx, instance, transform_ty_options); + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) + .unwrap_or_else(|error| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}") + }); + typeid_for_fnabi(tcx, fn_abi, options) +} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs new file mode 100644 index 0000000000000..7141c6c9bb0c3 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -0,0 +1,450 @@ +//! Transforms instances and types for LLVM CFI and cross-language LLVM CFI support using Itanium +//! C++ ABI mangling. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use rustc_hir as hir; +use rustc_hir::LangItem; +use rustc_middle::bug; +use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{ + self, Instance, IntTy, List, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, UintTy, +}; +use rustc_span::sym; +use rustc_trait_selection::traits; +use std::iter; +use tracing::{debug, instrument}; + +use crate::cfi::typeid::itanium_cxx_abi::encode::EncodeTyOptions; +use crate::cfi::typeid::TypeIdOptions; + +/// Options for transform_ty. +pub type TransformTyOptions = TypeIdOptions; + +pub struct TransformTy<'tcx> { + tcx: TyCtxt<'tcx>, + options: TransformTyOptions, + parents: Vec>, +} + +impl<'tcx> TransformTy<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self { + TransformTy { tcx, options, parents: Vec::new() } + } +} + +/// Transforms a ty:Ty for being encoded and used in the substitution dictionary. +/// +/// * Transforms all c_void types into unit types. +/// * Generalizes pointers if TransformTyOptions::GENERALIZE_POINTERS option is set. +/// * Normalizes integers if TransformTyOptions::NORMALIZE_INTEGERS option is set. +/// * Generalizes any repr(transparent) user-defined type that is either a pointer or reference, and +/// either references itself or any other type that contains or references itself, to avoid a +/// reference cycle. +/// * Transforms repr(transparent) types without non-ZST field into (). +/// +impl<'tcx> TypeFolder> for TransformTy<'tcx> { + // Transforms a ty:Ty for being encoded and used in the substitution dictionary. + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind() { + ty::Array(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::Float(..) + | ty::FnDef(..) + | ty::Foreign(..) + | ty::Never + | ty::Pat(..) + | ty::Slice(..) + | ty::Str + | ty::Tuple(..) => t.super_fold_with(self), + + ty::Bool => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Note: on all platforms that Rust's currently supports, its size and alignment + // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) + // + // Clang represents bool as an 8-bit unsigned integer. + self.tcx.types.u8 + } else { + t + } + } + + ty::Char => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Since #118032, char is guaranteed to have the same size, alignment, and + // function call ABI as u32 on all platforms. + self.tcx.types.u32 + } else { + t + } + } + + ty::Int(..) | ty::Uint(..) => { + if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { + // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit + // wide. All platforms we currently support have a C platform, and as a + // consequence, isize/usize are at least 16-bit wide for all of them. + // + // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) + match t.kind() { + ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.i16, + 32 => self.tcx.types.i32, + 64 => self.tcx.types.i64, + 128 => self.tcx.types.i128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width { + 16 => self.tcx.types.u16, + 32 => self.tcx.types.u32, + 64 => self.tcx.types.u64, + 128 => self.tcx.types.u128, + _ => bug!( + "fold_ty: unexpected pointer width `{}`", + self.tcx.sess.target.pointer_width + ), + }, + _ => t, + } + } else { + t + } + } + + ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit, + + ty::Adt(adt_def, args) => { + if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t) + { + // Don't transform repr(transparent) types with an user-defined CFI encoding to + // preserve the user-defined CFI encoding. + if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) { + return t; + } + let variant = adt_def.non_enum_variant(); + let param_env = self.tcx.param_env(variant.def_id); + let field = variant.fields.iter().find(|field| { + let ty = self.tcx.type_of(field.did).instantiate_identity(); + let is_zst = self + .tcx + .layout_of(param_env.and(ty)) + .is_ok_and(|layout| layout.is_zst()); + !is_zst + }); + if let Some(field) = field { + let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args); + // Generalize any repr(transparent) user-defined type that is either a + // pointer or reference, and either references itself or any other type that + // contains or references itself, to avoid a reference cycle. + + // If the self reference is not through a pointer, for example, due + // to using `PhantomData`, need to skip normalizing it if we hit it again. + self.parents.push(t); + let ty = if ty0.is_any_ptr() && ty0.contains(t) { + let options = self.options; + self.options |= TransformTyOptions::GENERALIZE_POINTERS; + let ty = ty0.fold_with(self); + self.options = options; + ty + } else { + ty0.fold_with(self) + }; + self.parents.pop(); + ty + } else { + // Transform repr(transparent) types without non-ZST field into () + self.tcx.types.unit + } + } else { + t.super_fold_with(self) + } + } + + ty::Ref(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } else { + Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit) + } + } else { + t.super_fold_with(self) + } + } + + ty::RawPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if t.is_mutable_ptr() { + Ty::new_mut_ptr(self.tcx, self.tcx.types.unit) + } else { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } + } else { + t.super_fold_with(self) + } + } + + ty::FnPtr(..) => { + if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + Ty::new_imm_ptr(self.tcx, self.tcx.types.unit) + } else { + t.super_fold_with(self) + } + } + + ty::Alias(..) => { + self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t)) + } + + ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { + bug!("fold_ty: unexpected `{:?}`", t.kind()); + } + } + } + + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } +} + +#[instrument(skip(tcx), ret)] +fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> { + assert!(!poly_trait_ref.has_non_region_param()); + let principal_pred = poly_trait_ref.map_bound(|trait_ref| { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) + }); + let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref) + .flat_map(|super_poly_trait_ref| { + tcx.associated_items(super_poly_trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .map(move |assoc_ty| { + super_poly_trait_ref.map_bound(|super_trait_ref| { + let alias_ty = ty::AliasTy::new(tcx, assoc_ty.def_id, super_trait_ref.args); + let resolved = tcx.normalize_erasing_regions( + ty::ParamEnv::reveal_all(), + alias_ty.to_ty(tcx), + ); + debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + def_id: assoc_ty.def_id, + args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args, + term: resolved.into(), + }) + }) + }) + }) + .collect(); + assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + let preds = tcx.mk_poly_existential_predicates_from_iter( + iter::once(principal_pred).chain(assoc_preds.into_iter()), + ); + Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) +} + +/// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI +/// mangling. +/// +/// typeid_for_instance is called at two locations, initially when declaring/defining functions and +/// methods, and later during code generation at call sites, after type erasure might have ocurred. +/// +/// In the first call (i.e., when declaring/defining functions and methods), it encodes type ids for +/// an FnAbi or Instance, and these type ids are attached to functions and methods. (These type ids +/// are used later by the LowerTypeTests LLVM pass to aggregate functions in groups derived from +/// these type ids.) +/// +/// In the second call (i.e., during code generation at call sites), it encodes a type id for an +/// FnAbi or Instance, after type erasure might have occured, and this type id is used for testing +/// if a function is member of the group derived from this type id. Therefore, in the first call to +/// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at +/// most as much information that would be available in the second call (i.e., during code +/// generation at call sites); otherwise, the type ids would not not match. +/// +/// For this, it: +/// +/// * Adjust the type ids of DropGlues (see below). +/// * Adjusts the type ids of VTableShims to the type id expected in the call sites for the +/// entry in the vtable (i.e., by using the signature of the closure passed as an argument to the +/// shim, or by just removing self). +/// * Performs type erasure for calls on trait objects by transforming self into a trait object of +/// the trait that defines the method. +/// * Performs type erasure for closures call methods by transforming self into a trait object of +/// the Fn trait that defines the method (for being attached as a secondary type id). +/// +#[instrument(level = "trace", skip(tcx))] +pub fn transform_instance<'tcx>( + tcx: TyCtxt<'tcx>, + mut instance: Instance<'tcx>, + options: TransformTyOptions, +) -> Instance<'tcx> { + if (matches!(instance.def, ty::InstanceDef::Virtual(..)) + && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn()) + || matches!(instance.def, ty::InstanceDef::DropGlue(..)) + { + // Adjust the type ids of DropGlues + // + // DropGlues may have indirect calls to one or more given types drop function. Rust allows + // for types to be erased to any trait object and retains the drop function for the original + // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is + // called a second time, it only has information after type erasure and it could be a call + // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on + // declaration/definition, and during code generation at call sites so they have the same + // type id and match. + // + // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of + // any other type. + // + let def_id = tcx + .lang_items() + .drop_trait() + .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item")); + let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { + def_id: def_id, + args: List::empty(), + }); + let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); + let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); + instance.args = tcx.mk_args_trait(self_ty, List::empty()); + } else if let ty::InstanceDef::Virtual(def_id, _) = instance.def { + // Transform self into a trait object of the trait that defines the method for virtual + // functions to match the type erasure done below. + let upcast_ty = match tcx.trait_of_item(def_id) { + Some(trait_id) => trait_object_ty( + tcx, + ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), + ), + // drop_in_place won't have a defining trait, skip the upcast + None => instance.args.type_at(0), + }; + let ty::Dynamic(preds, lifetime, kind) = upcast_ty.kind() else { + bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}"); + }; + let self_ty = if preds.principal().is_some() { + let filtered_preds = + tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { + !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) + })); + Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) + } else { + // If there's no principal type, re-encode it as a unit, since we don't know anything + // about it. This technically discards the knowledge that it was a type that was made + // into a trait object at some point, but that's not a lot. + tcx.types.unit + }; + instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1)); + } else if let ty::InstanceDef::VTableShim(def_id) = instance.def + && let Some(trait_id) = tcx.trait_of_item(def_id) + { + // Adjust the type ids of VTableShims to the type id expected in the call sites for the + // entry in the vtable (i.e., by using the signature of the closure passed as an argument + // to the shim, or by just removing self). + let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + } + + if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) { + // Perform type erasure for calls on trait objects by transforming self into a trait object + // of the trait that defines the method. + if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) + && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) + { + let impl_method = tcx.associated_item(instance.def_id()); + let method_id = impl_method + .trait_item_def_id + .expect("Part of a trait implementation, but not linked to the def_id?"); + let trait_method = tcx.associated_item(method_id); + let trait_id = trait_ref.skip_binder().def_id; + if traits::is_vtable_safe_method(tcx, trait_id, trait_method) + && tcx.object_safety_violations(trait_id).is_empty() + { + // Trait methods will have a Self polymorphic parameter, where the concreteized + // implementatation will not. We need to walk back to the more general trait method + let trait_ref = tcx.instantiate_and_normalize_erasing_regions( + instance.args, + ty::ParamEnv::reveal_all(), + trait_ref, + ); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + + // At the call site, any call to this concrete function through a vtable will be + // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the + // original method id, and we've recovered the trait arguments, we can make the callee + // instance we're computing the alias set for match the caller instance. + // + // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder. + // If we ever *do* start encoding the vtable index, we will need to generate an alias set + // based on which vtables we are putting this method into, as there will be more than one + // index value when supertraits are involved. + instance.def = ty::InstanceDef::Virtual(method_id, 0); + let abstract_trait_args = + tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args); + } + } else if tcx.is_closure_like(instance.def_id()) { + // We're either a closure or a coroutine. Our goal is to find the trait we're defined on, + // instantiate it, and take the type of its only method as our own. + let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + let (trait_id, inputs) = match closure_ty.kind() { + ty::Closure(..) => { + let closure_args = instance.args.as_closure(); + let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap(); + let tuple_args = + tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0]; + (trait_id, Some(tuple_args)) + } + ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() { + hir::CoroutineKind::Coroutine(..) => ( + tcx.require_lang_item(LangItem::Coroutine, None), + Some(instance.args.as_coroutine().resume_ty()), + ), + hir::CoroutineKind::Desugared(desugaring, _) => { + let lang_item = match desugaring { + hir::CoroutineDesugaring::Async => LangItem::Future, + hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator, + hir::CoroutineDesugaring::Gen => LangItem::Iterator, + }; + (tcx.require_lang_item(lang_item, None), None) + } + }, + ty::CoroutineClosure(..) => ( + tcx.require_lang_item(LangItem::FnOnce, None), + Some( + tcx.instantiate_bound_regions_with_erased( + instance.args.as_coroutine_closure().coroutine_closure_sig(), + ) + .tupled_inputs_ty, + ), + ), + x => bug!("Unexpected type kind for closure-like: {x:?}"), + }; + let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into)); + let trait_ref = ty::TraitRef::new(tcx, trait_id, concrete_args); + let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref)); + let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1)); + // There should be exactly one method on this trait, and it should be the one we're + // defining. + let call = tcx + .associated_items(trait_id) + .in_definition_order() + .find(|it| it.kind == ty::AssocKind::Fn) + .expect("No call-family function on closure-like Fn trait?") + .def_id; + + instance.def = ty::InstanceDef::Virtual(call, 0); + instance.args = abstract_args; + } + } + + instance +} diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs new file mode 100644 index 0000000000000..ad8b180443999 --- /dev/null +++ b/compiler/rustc_sanitizers/src/cfi/typeid/mod.rs @@ -0,0 +1,54 @@ +//! Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI +//! support for the Rust compiler. +//! +//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, +//! see design document in the tracking issue #89653. +use bitflags::bitflags; +use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; + +bitflags! { + /// Options for typeid_for_fnabi. + #[derive(Clone, Copy, Debug)] + pub struct TypeIdOptions: u32 { + /// Generalizes pointers for compatibility with Clang + /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI + /// support. + const GENERALIZE_POINTERS = 1; + /// Generalizes repr(C) user-defined type for extern function types with the "C" calling + /// convention (or extern types) for cross-language LLVM CFI and KCFI support. + const GENERALIZE_REPR_C = 2; + /// Normalizes integers for compatibility with Clang + /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM + /// CFI and KCFI support. + const NORMALIZE_INTEGERS = 4; + /// Do not perform self type erasure for attaching a secondary type id to methods with their + /// concrete self so they can be used as function pointers. + /// + /// (This applies to typeid_for_instance only and should be used to attach a secondary type + /// id to methods during their declaration/definition so they match the type ids returned by + /// either typeid_for_instance or typeid_for_fnabi at call sites during code generation for + /// type membership tests when methods are used as function pointers.) + const USE_CONCRETE_SELF = 8; + } +} + +pub mod itanium_cxx_abi; + +/// Returns a type metadata identifier for the specified FnAbi. +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) +} + +/// Returns a type metadata identifier for the specified Instance. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + options: TypeIdOptions, +) -> String { + itanium_cxx_abi::typeid_for_instance(tcx, instance, options) +} diff --git a/compiler/rustc_sanitizers/src/kcfi/mod.rs b/compiler/rustc_sanitizers/src/kcfi/mod.rs new file mode 100644 index 0000000000000..a8b632940b040 --- /dev/null +++ b/compiler/rustc_sanitizers/src/kcfi/mod.rs @@ -0,0 +1,7 @@ +//! LLVM Kernel Control Flow Integrity (KCFI) and cross-language LLVM KCFI support for the Rust +//! compiler. +//! +//! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler, +//! see the tracking issue #123479. +pub mod typeid; +pub use crate::kcfi::typeid::{typeid_for_fnabi, typeid_for_instance, TypeIdOptions}; diff --git a/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs new file mode 100644 index 0000000000000..436c398e39b06 --- /dev/null +++ b/compiler/rustc_sanitizers/src/kcfi/typeid/mod.rs @@ -0,0 +1,55 @@ +//! Type metadata identifiers for LLVM Kernel Control Flow Integrity (KCFI) and cross-language LLVM +//! KCFI support for the Rust compiler. +//! +//! For more information about LLVM KCFI and cross-language LLVM KCFI support for the Rust compiler, +//! see the tracking issue #123479. +use rustc_middle::ty::{Instance, InstanceDef, ReifyReason, Ty, TyCtxt}; +use rustc_target::abi::call::FnAbi; +use std::hash::Hasher; +use twox_hash::XxHash64; + +pub use crate::cfi::typeid::{itanium_cxx_abi, TypeIdOptions}; + +/// Returns a KCFI type metadata identifier for the specified FnAbi. +pub fn typeid_for_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> u32 { + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); + hash.finish() as u32 +} + +/// Returns a KCFI type metadata identifier for the specified Instance. +pub fn typeid_for_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + mut options: TypeIdOptions, +) -> u32 { + // KCFI support for Rust shares most of its implementation with the CFI support, with some key + // differences: + // + // 1. KCFI performs type tests differently and are implemented as different LLVM passes than CFI + // to not require LTO. + // 2. KCFI has the limitation that a function or method may have one type id assigned only. + // + // Because of the limitation listed above (2), the current KCFI implementation (not CFI) does + // reifying of types (i.e., adds shims/trampolines for indirect calls in these cases) for: + // + // * Supporting casting between function items, closures, and Fn trait objects. + // * Supporting methods being cast as function pointers. + // + // This was implemented for KCFI support in #123106 and #123052 (which introduced the + // ReifyReason). The tracking issue for KCFI support for Rust is #123479. + if matches!(instance.def, InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr))) { + options.insert(TypeIdOptions::USE_CONCRETE_SELF); + } + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.finish() as u32 +} diff --git a/compiler/rustc_sanitizers/src/lib.rs b/compiler/rustc_sanitizers/src/lib.rs new file mode 100644 index 0000000000000..1f73e255490b2 --- /dev/null +++ b/compiler/rustc_sanitizers/src/lib.rs @@ -0,0 +1,7 @@ +#![feature(let_chains)] +//! Sanitizers support for the Rust compiler. +//! +//! This crate contains the source code for providing support for the sanitizers to the Rust +//! compiler. +pub mod cfi; +pub mod kcfi; diff --git a/compiler/rustc_serialize/src/int_overflow.rs b/compiler/rustc_serialize/src/int_overflow.rs new file mode 100644 index 0000000000000..f2aac2ef711c1 --- /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 ca661bac78c7d..4432480446822 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() -> 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 bb822c611a170..532b749f9136b 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -8,11 +8,8 @@ #![doc(rust_logo)] #![allow(internal_features)] #![feature(rustdoc_internals)] -#![feature(associated_type_bounds)] #![feature(const_option)] #![feature(core_intrinsics)] -#![feature(generic_nonzero)] -#![feature(inline_const)] #![feature(min_specialization)] #![feature(never_type)] #![feature(ptr_sub_ptr)] @@ -23,5 +20,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 cc8d1c25092eb..eec83c02d3537 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::(written); } - self.buffered += written; + self.buffered = self.buffered.debug_strict_add(written); } #[cold] diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 42c681e496174..b8dacc6968d30 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -24,6 +24,9 @@ session_feature_diagnostic_for_issue = session_feature_diagnostic_help = add `#![feature({$feature})]` to the crate attributes to enable +session_feature_diagnostic_suggestion = + add `#![feature({$feature})]` to the crate attributes to enable + session_feature_suggest_upgrade_compiler = this compiler was built on {$date}; consider upgrading it if it is out of date @@ -93,6 +96,8 @@ session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Cli session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1` +session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort` + session_sanitizer_not_supported = {$us} sanitizer is not supported for this target session_sanitizers_not_supported = {$us} sanitizers are not supported for this target diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index 2553df33cc765..93839fa1186af 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -44,6 +44,10 @@ pub struct FieldInfo { pub offset: u64, pub size: u64, pub align: u64, + /// Name of the type of this field. + /// Present only if the creator thought that this would be important for identifying the field, + /// typically because the field name is uninformative. + pub type_name: Option, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -192,7 +196,7 @@ impl CodeStats { fields.sort_by_key(|f| (f.offset, f.size)); for field in fields { - let FieldInfo { kind, ref name, offset, size, align } = field; + let FieldInfo { kind, ref name, offset, size, align, type_name } = field; if offset > min_offset { let pad = offset - min_offset; @@ -201,21 +205,27 @@ impl CodeStats { if offset < min_offset { // If this happens it's probably a union. - println!( + print!( "print-type-size {indent}{kind} `.{name}`: {size} bytes, \ offset: {offset} bytes, \ alignment: {align} bytes" ); } else if info.packed || offset == min_offset { - println!("print-type-size {indent}{kind} `.{name}`: {size} bytes"); + print!("print-type-size {indent}{kind} `.{name}`: {size} bytes"); } else { // Include field alignment in output only if it caused padding injection - println!( + print!( "print-type-size {indent}{kind} `.{name}`: {size} bytes, \ alignment: {align} bytes" ); } + if let Some(type_name) = type_name { + println!(", type: {type_name}"); + } else { + println!(); + } + min_offset = offset + size; } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b7ee2c9802541..ad66e5e1c2b4d 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -10,19 +10,17 @@ use crate::search_paths::SearchPath; use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use crate::{filesearch, lint, HashStableContext}; use crate::{EarlyDiagCtxt, Session}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagArgValue, DiagCtxtFlags, IntoDiagArg}; use rustc_feature::UnstableFeatures; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION}; use rustc_span::source_map::FilePathMapping; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::{FileName, FileNameDisplayPreference, RealFileName, SourceFileHashAlgorithm}; -use rustc_target::abi::Align; -use rustc_target::spec::LinkSelfContainedComponents; -use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, SplitDebuginfo}; -use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS}; +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, }; @@ -35,9 +33,13 @@ use std::iter; use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; use std::sync::LazyLock; +use tracing::debug; +mod cfg; pub mod sigpipe; +pub use cfg::{Cfg, CheckCfg, ExpectedValues}; + /// The different settings that the `-C strip` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum Strip { @@ -144,15 +146,27 @@ pub enum InstrumentCoverage { } /// Individual flag values controlled by `-Z coverage-options`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub struct CoverageOptions { - /// Add branch coverage instrumentation. - pub branch: bool, + pub level: CoverageLevel, + // Other boolean or enum-valued options might be added here. +} + +/// Controls whether branch coverage or MC/DC coverage is enabled. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum CoverageLevel { + /// Instrument for coverage at the MIR block level. + Block, + /// Also instrument branch points (includes block coverage). + Branch, + /// Instrument for MC/DC. Mostly a superset of branch coverage, but might + /// differ in some corner cases. + Mcdc, } -impl Default for CoverageOptions { +impl Default for CoverageLevel { fn default() -> Self { - Self { branch: false } + Self::Block } } @@ -298,6 +312,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 { @@ -319,7 +375,7 @@ pub struct LocationDetail { } impl LocationDetail { - pub fn all() -> Self { + pub(crate) fn all() -> Self { Self { file: true, line: true, column: true } } } @@ -555,7 +611,7 @@ impl OutputTypes { OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) } - pub fn get(&self, key: &OutputType) -> Option<&Option> { + pub(crate) fn get(&self, key: &OutputType) -> Option<&Option> { self.0.get(key) } @@ -563,6 +619,11 @@ impl OutputTypes { self.0.contains_key(key) } + /// Returns `true` if user specified a name and not just produced type + pub fn contains_explicit_name(&self, key: &OutputType) -> bool { + self.0.get(key).map_or(false, |f| f.is_some()) + } + pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option> { self.0.iter() } @@ -668,10 +729,6 @@ impl Externs { pub fn iter(&self) -> BTreeMapIter<'_, String, ExternEntry> { self.0.iter() } - - pub fn len(&self) -> usize { - self.0.len() - } } impl ExternEntry { @@ -860,13 +917,13 @@ impl OutFileName { #[derive(Clone, Hash, Debug, HashStable_Generic, Encodable, Decodable)] pub struct OutputFilenames { - pub out_directory: PathBuf, + pub(crate) out_directory: PathBuf, /// Crate name. Never contains '-'. crate_stem: String, /// Typically based on `.rs` input file name. Any '-' is preserved. filestem: String, pub single_output_file: Option, - pub temps_directory: Option, + temps_directory: Option, pub outputs: OutputTypes, } @@ -904,7 +961,7 @@ impl OutputFilenames { /// Gets the output path where a compilation artifact of the given type /// should be placed on disk. - pub fn output_path(&self, flavor: OutputType) -> PathBuf { + fn output_path(&self, flavor: OutputType) -> PathBuf { let extension = flavor.extension(); match flavor { OutputType::Metadata => { @@ -995,22 +1052,12 @@ bitflags::bitflags! { const MACRO = 1 << 0; /// Apply remappings to printed compiler diagnostics const DIAGNOSTICS = 1 << 1; - /// Apply remappings to debug information only when they are written to - /// compiled executables or libraries, but not when they are in split - /// debuginfo files - const UNSPLIT_DEBUGINFO = 1 << 2; - /// Apply remappings to debug information only when they are written to - /// split debug information files, but not in compiled executables or - /// libraries - const SPLIT_DEBUGINFO = 1 << 3; - /// Apply remappings to the paths pointing to split debug information - /// files. Does nothing when these files are not generated. - const SPLIT_DEBUGINFO_PATH = 1 << 4; + /// Apply remappings to debug informations + const DEBUGINFO = 1 << 3; - /// An alias for macro,unsplit-debuginfo,split-debuginfo-path. This - /// ensures all paths in compiled executables or libraries are remapped - /// but not elsewhere. - const OBJECT = Self::MACRO.bits() | Self::UNSPLIT_DEBUGINFO.bits() | Self::SPLIT_DEBUGINFO_PATH.bits(); + /// An alias for `macro` and `debuginfo`. This ensures all paths in compiled + /// executables or libraries are remapped but not elsewhere. + const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits(); } } @@ -1135,6 +1182,16 @@ impl UnstableOptions { track_diagnostics: self.track_diagnostics, } } + + pub fn src_hash_algorithm(&self, target: &Target) -> SourceFileHashAlgorithm { + self.src_hash_algorithm.unwrap_or_else(|| { + if target.is_like_msvc { + SourceFileHashAlgorithm::Sha256 + } else { + SourceFileHashAlgorithm::Md5 + } + }) + } } // The type of entry function, so users can have their own entry functions @@ -1179,14 +1236,14 @@ pub enum Passes { } impl Passes { - pub fn is_empty(&self) -> bool { + fn is_empty(&self) -> bool { match *self { Passes::Some(ref v) => v.is_empty(), Passes::All => false, } } - pub fn extend(&mut self, passes: impl IntoIterator) { + pub(crate) fn extend(&mut self, passes: impl IntoIterator) { match *self { Passes::Some(ref mut v) => v.extend(passes), Passes::All => {} @@ -1212,393 +1269,48 @@ pub struct BranchProtection { pub pac_ret: Option, } -pub const fn default_lib_output() -> CrateType { +pub(crate) const fn default_lib_output() -> CrateType { CrateType::Rlib } -fn default_configuration(sess: &Session) -> Cfg { - let mut ret = Cfg::default(); - - macro_rules! ins_none { - ($key:expr) => { - ret.insert(($key, None)); - }; - } - macro_rules! ins_str { - ($key:expr, $val_str:expr) => { - ret.insert(($key, Some(Symbol::intern($val_str)))); - }; - } - macro_rules! ins_sym { - ($key:expr, $val_sym:expr) => { - ret.insert(($key, Some($val_sym))); - }; - } - - // Symbols are inserted in alphabetical order as much as possible. - // The exceptions are where control flow forces things out of order. - // - // Run `rustc --print cfg` to see the configuration in practice. - // - // NOTE: These insertions should be kept in sync with - // `CheckCfg::fill_well_known` below. - - if sess.opts.debug_assertions { - ins_none!(sym::debug_assertions); - } - - if sess.overflow_checks() { - ins_none!(sym::overflow_checks); - } - - ins_sym!(sym::panic, sess.panic_strategy().desc_symbol()); - - // JUSTIFICATION: before wrapper fn is available - #[allow(rustc::bad_opt_access)] - if sess.opts.crate_types.contains(&CrateType::ProcMacro) { - ins_none!(sym::proc_macro); - } - - if sess.is_nightly_build() { - ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol()); - } - - for mut s in sess.opts.unstable_opts.sanitizer { - // KASAN is still ASAN under the hood, so it uses the same attribute. - if s == SanitizerSet::KERNELADDRESS { - s = SanitizerSet::ADDRESS; - } - ins_str!(sym::sanitize, &s.to_string()); - } - - if sess.is_sanitizer_cfi_generalize_pointers_enabled() { - ins_none!(sym::sanitizer_cfi_generalize_pointers); - } - if sess.is_sanitizer_cfi_normalize_integers_enabled() { - ins_none!(sym::sanitizer_cfi_normalize_integers); - } - - ins_str!(sym::target_abi, &sess.target.abi); - ins_str!(sym::target_arch, &sess.target.arch); - ins_str!(sym::target_endian, sess.target.endian.as_str()); - ins_str!(sym::target_env, &sess.target.env); - - for family in sess.target.families.as_ref() { - ins_str!(sym::target_family, family); - if family == "windows" { - ins_none!(sym::windows); - } else if family == "unix" { - ins_none!(sym::unix); - } - } - - // `target_has_atomic*` - let layout = sess.target.parse_data_layout().unwrap_or_else(|err| { - sess.dcx().emit_fatal(err); - }); - let mut has_atomic = false; - for (i, align) in [ - (8, layout.i8_align.abi), - (16, layout.i16_align.abi), - (32, layout.i32_align.abi), - (64, layout.i64_align.abi), - (128, layout.i128_align.abi), - ] { - if i >= sess.target.min_atomic_width() && i <= sess.target.max_atomic_width() { - if !has_atomic { - has_atomic = true; - if sess.is_nightly_build() { - if sess.target.atomic_cas { - ins_none!(sym::target_has_atomic); - } - ins_none!(sym::target_has_atomic_load_store); - } - } - let mut insert_atomic = |sym, align: Align| { - if sess.target.atomic_cas { - ins_sym!(sym::target_has_atomic, sym); - } - if align.bits() == i { - ins_sym!(sym::target_has_atomic_equal_alignment, sym); - } - ins_sym!(sym::target_has_atomic_load_store, sym); - }; - insert_atomic(sym::integer(i), align); - if sess.target.pointer_width as u64 == i { - insert_atomic(sym::ptr, layout.pointer_align.abi); - } - } - } - - ins_str!(sym::target_os, &sess.target.os); - ins_sym!(sym::target_pointer_width, sym::integer(sess.target.pointer_width)); - - if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) { - ins_none!(sym::target_thread_local); - } - - ins_str!(sym::target_vendor, &sess.target.vendor); - - // If the user wants a test runner, then add the test cfg. - if sess.is_test_crate() { - ins_none!(sym::test); - } - - ret -} - -/// The parsed `--cfg` options that define the compilation environment of the -/// crate, used to drive conditional compilation. -/// -/// An `FxIndexSet` is used to ensure deterministic ordering of error messages -/// relating to `--cfg`. -pub type Cfg = FxIndexSet<(Symbol, Option)>; - -/// The parsed `--check-cfg` options. -#[derive(Default)] -pub struct CheckCfg { - /// Is well known names activated - pub exhaustive_names: bool, - /// Is well known values activated - pub exhaustive_values: bool, - /// All the expected values for a config name - pub expecteds: FxHashMap>, - /// Well known names (only used for diagnostics purposes) - pub well_known_names: FxHashSet, -} - -pub enum ExpectedValues { - Some(FxHashSet>), - Any, -} - -impl ExpectedValues { - fn insert(&mut self, value: T) -> bool { - match self { - ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)), - ExpectedValues::Any => false, - } - } -} - -impl Extend for ExpectedValues { - fn extend>(&mut self, iter: I) { - match self { - ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(Some)), - ExpectedValues::Any => {} - } - } -} - -impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues { - fn extend>(&mut self, iter: I) { - match self { - ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))), - ExpectedValues::Any => {} - } - } -} - -impl CheckCfg { - pub fn fill_well_known(&mut self, current_target: &Target) { - if !self.exhaustive_values && !self.exhaustive_names { - return; - } - - let no_values = || { - let mut values = FxHashSet::default(); - values.insert(None); - ExpectedValues::Some(values) - }; - - let empty_values = || { - let values = FxHashSet::default(); - ExpectedValues::Some(values) - }; - - macro_rules! ins { - ($name:expr, $values:expr) => {{ - self.well_known_names.insert($name); - self.expecteds.entry($name).or_insert_with($values) - }}; - } - - // Symbols are inserted in alphabetical order as much as possible. - // The exceptions are where control flow forces things out of order. - // - // NOTE: This should be kept in sync with `default_configuration`. - // Note that symbols inserted conditionally in `default_configuration` - // are inserted unconditionally here. - // - // When adding a new config here you should also update - // `tests/ui/check-cfg/well-known-values.rs`. - // - // Don't forget to update `src/doc/unstable-book/src/compiler-flags/check-cfg.md` - // in the unstable book as well! - - ins!(sym::debug_assertions, no_values); - - // These four are never set by rustc, but we set them anyway: they - // should not trigger a lint because `cargo clippy`, `cargo doc`, - // `cargo test` and `cargo miri run` (respectively) can set them. - ins!(sym::clippy, no_values); - ins!(sym::doc, no_values); - ins!(sym::doctest, no_values); - ins!(sym::miri, no_values); - - ins!(sym::overflow_checks, no_values); - - ins!(sym::panic, empty_values).extend(&PanicStrategy::all()); - - ins!(sym::proc_macro, no_values); - - ins!(sym::relocation_model, empty_values).extend(RelocModel::all()); - - let sanitize_values = SanitizerSet::all() - .into_iter() - .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap())); - ins!(sym::sanitize, empty_values).extend(sanitize_values); - - ins!(sym::sanitizer_cfi_generalize_pointers, no_values); - ins!(sym::sanitizer_cfi_normalize_integers, no_values); - - ins!(sym::target_feature, empty_values).extend( - rustc_target::target_features::all_known_features() - .map(|(f, _sb)| f) - .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned()) - .map(Symbol::intern), - ); - - // sym::target_* - { - const VALUES: [&Symbol; 8] = [ - &sym::target_abi, - &sym::target_arch, - &sym::target_endian, - &sym::target_env, - &sym::target_family, - &sym::target_os, - &sym::target_pointer_width, - &sym::target_vendor, - ]; - - // Initialize (if not already initialized) - for &e in VALUES { - if !self.exhaustive_values { - ins!(e, || ExpectedValues::Any); - } else { - ins!(e, empty_values); - } - } - - if self.exhaustive_values { - // Get all values map at once otherwise it would be costly. - // (8 values * 220 targets ~= 1760 times, at the time of writing this comment). - let [ - values_target_abi, - values_target_arch, - values_target_endian, - values_target_env, - values_target_family, - values_target_os, - values_target_pointer_width, - values_target_vendor, - ] = self - .expecteds - .get_many_mut(VALUES) - .expect("unable to get all the check-cfg values buckets"); - - for target in TARGETS - .iter() - .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target))) - .chain(iter::once(current_target.clone())) - { - values_target_abi.insert(Symbol::intern(&target.options.abi)); - values_target_arch.insert(Symbol::intern(&target.arch)); - values_target_endian.insert(Symbol::intern(target.options.endian.as_str())); - values_target_env.insert(Symbol::intern(&target.options.env)); - values_target_family.extend( - target.options.families.iter().map(|family| Symbol::intern(family)), - ); - values_target_os.insert(Symbol::intern(&target.options.os)); - values_target_pointer_width.insert(sym::integer(target.pointer_width)); - values_target_vendor.insert(Symbol::intern(&target.options.vendor)); - } - } - } - - let atomic_values = &[ - sym::ptr, - sym::integer(8usize), - sym::integer(16usize), - sym::integer(32usize), - sym::integer(64usize), - sym::integer(128usize), - ]; - for sym in [ - sym::target_has_atomic, - sym::target_has_atomic_equal_alignment, - sym::target_has_atomic_load_store, - ] { - ins!(sym, no_values).extend(atomic_values); - } - - ins!(sym::target_thread_local, no_values); - - ins!(sym::test, no_values); - - ins!(sym::unix, no_values); - ins!(sym::windows, no_values); - } -} - pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { // Combine the configuration requested by the session (command line) with // some default and generated configuration items. - user_cfg.extend(default_configuration(sess)); + user_cfg.extend(cfg::default_configuration(sess)); user_cfg } -pub fn build_target_config( - early_dcx: &EarlyDiagCtxt, - opts: &Options, - target_override: Option, - sysroot: &Path, -) -> Target { - let target_result = target_override.map_or_else( - || Target::search(&opts.target_triple, sysroot), - |t| Ok((t, TargetWarnings::empty())), - ); - let (target, target_warnings) = target_result.unwrap_or_else(|e| { - early_dcx.early_fatal(format!( +pub fn build_target_config(early_dcx: &EarlyDiagCtxt, opts: &Options, sysroot: &Path) -> Target { + match Target::search(&opts.target_triple, sysroot) { + Ok((target, warnings)) => { + for warning in warnings.warning_messages() { + early_dcx.early_warn(warning) + } + if !matches!(target.pointer_width, 16 | 32 | 64) { + early_dcx.early_fatal(format!( + "target specification was invalid: unrecognized target-pointer-width {}", + target.pointer_width + )) + } + target + } + Err(e) => early_dcx.early_fatal(format!( "Error loading target specification: {e}. \ - Run `rustc --print target-list` for a list of built-in targets" - )) - }); - for warning in target_warnings.warning_messages() { - early_dcx.early_warn(warning) - } - - if !matches!(target.pointer_width, 16 | 32 | 64) { - early_dcx.early_fatal(format!( - "target specification was invalid: unrecognized target-pointer-width {}", - target.pointer_width - )) + Run `rustc --print target-list` for a list of built-in targets" + )), } - - target } #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum OptionStability { +enum OptionStability { Stable, Unstable, } pub struct RustcOptGroup { pub apply: Box &mut getopts::Options>, - pub name: &'static str, - pub stability: OptionStability, + name: &'static str, + stability: OptionStability, } impl RustcOptGroup { @@ -1634,8 +1346,8 @@ mod opt { use super::RustcOptGroup; - pub type R = RustcOptGroup; - pub type S = &'static str; + type R = RustcOptGroup; + type S = &'static str; fn stable(name: S, f: F) -> R where @@ -1655,32 +1367,34 @@ mod opt { if a.len() > b.len() { a } else { b } } - pub fn opt_s(a: S, b: S, c: S, d: S) -> R { + pub(crate) fn opt_s(a: S, b: S, c: S, d: S) -> R { stable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) } - pub fn multi_s(a: S, b: S, c: S, d: S) -> R { + pub(crate) fn multi_s(a: S, b: S, c: S, d: S) -> R { stable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) } - pub fn flag_s(a: S, b: S, c: S) -> R { + pub(crate) fn flag_s(a: S, b: S, c: S) -> R { stable(longer(a, b), move |opts| opts.optflag(a, b, c)) } - pub fn flagmulti_s(a: S, b: S, c: S) -> R { + pub(crate) fn flagmulti_s(a: S, b: S, c: S) -> R { stable(longer(a, b), move |opts| opts.optflagmulti(a, b, c)) } - pub fn opt(a: S, b: S, c: S, d: S) -> R { + fn opt(a: S, b: S, c: S, d: S) -> R { unstable(longer(a, b), move |opts| opts.optopt(a, b, c, d)) } - pub fn multi(a: S, b: S, c: S, d: S) -> R { + pub(crate) fn multi(a: S, b: S, c: S, d: S) -> R { unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) } } + static EDITION_STRING: LazyLock = LazyLock::new(|| { format!( "Specify which edition of the compiler to use when compiling code. \ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE_EDITION}." ) }); + /// Returns the "short" subset of the rustc command line options, /// including metadata for each option, such as whether the option is /// part of the stable long-term interface for rustc. @@ -1689,7 +1403,7 @@ pub fn rustc_short_optgroups() -> Vec { opt::flag_s("h", "help", "Display this message"), opt::multi_s("", "cfg", "Configure the compilation environment. SPEC supports the syntax `NAME[=\"VALUE\"]`.", "SPEC"), - opt::multi("", "check-cfg", "Provide list of valid cfg options for checking", "SPEC"), + opt::multi_s("", "check-cfg", "Provide list of expected cfgs for checking", "SPEC"), opt::multi_s( "L", "", @@ -1870,9 +1584,9 @@ pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Col /// Possible json config files pub struct JsonConfig { pub json_rendered: HumanReadableErrorType, - pub json_artifact_notifications: bool, + json_artifact_notifications: bool, pub json_unused_externs: JsonUnusedExterns, - pub json_future_incompat: bool, + json_future_incompat: bool, } /// Report unused externs in event stream @@ -2022,7 +1736,7 @@ pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches } fn check_error_format_stability( - early_dcx: &mut EarlyDiagCtxt, + early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions, error_format: ErrorOutputType, ) { @@ -2120,7 +1834,7 @@ fn should_override_cgus_and_disable_thinlto( fn collect_print_requests( early_dcx: &EarlyDiagCtxt, cg: &mut CodegenOptions, - unstable_opts: &mut UnstableOptions, + unstable_opts: &UnstableOptions, matches: &getopts::Matches, ) -> Vec { let mut prints = Vec::::new(); @@ -2192,9 +1906,12 @@ fn collect_print_requests( let prints = PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::>(); let prints = prints.join(", "); - early_dcx.early_fatal(format!( - "unknown print request `{req}`. Valid print requests are: {prints}" - )); + + let mut diag = + early_dcx.early_struct_fatal(format!("unknown print request: `{req}`")); + #[allow(rustc::diagnostic_outside_of_impl)] + diag.help(format!("valid print requests are: {prints}")); + diag.emit() } }; @@ -2586,7 +2303,7 @@ fn parse_remap_path_prefix( } fn parse_logical_env( - early_dcx: &mut EarlyDiagCtxt, + early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, ) -> FxIndexMap { let mut vars = FxIndexMap::default(); @@ -2633,13 +2350,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M check_error_format_stability(early_dcx, &unstable_opts, error_format); - if !unstable_opts.unstable_options && json_unused_externs.is_enabled() { - early_dcx.early_fatal( - "the `-Z unstable-options` flag must also be passed to enable \ - the flag `--json=unused-externs`", - ); - } - let output_types = parse_output_types(early_dcx, &unstable_opts, matches); let mut cg = CodegenOptions::build(early_dcx, matches); @@ -2744,6 +2454,8 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } if let Ok(graphviz_font) = std::env::var("RUSTC_GRAPHVIZ_FONT") { + // FIXME: this is only mutation of UnstableOptions here, move into + // UnstableOptions::build? unstable_opts.graphviz_font = graphviz_font; } @@ -2793,7 +2505,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M )); } - let prints = collect_print_requests(early_dcx, &mut cg, &mut unstable_opts, matches); + let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); let cg = cg; @@ -2807,11 +2519,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let debuginfo = select_debuginfo(matches, &cg); let debuginfo_compression = unstable_opts.debuginfo_compression; - let mut search_paths = vec![]; - for s in &matches.opt_strs("L") { - search_paths.push(SearchPath::from_cli_opt(early_dcx, s)); - } - let libs = parse_libs(early_dcx, matches); let test = matches.opt_present("test"); @@ -2860,17 +2567,23 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M candidate.join("library/std/src/lib.rs").is_file().then_some(candidate) }; + let mut search_paths = vec![]; + for s in &matches.opt_strs("L") { + search_paths.push(SearchPath::from_cli_opt( + &sysroot, + &target_triple, + early_dcx, + s, + unstable_opts.unstable_options, + )); + } + let working_dir = std::env::current_dir().unwrap_or_else(|e| { early_dcx.early_fatal(format!("Current directory is invalid: {e}")); }); - let remap = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); - let (path, remapped) = remap.map_prefix(&working_dir); - let working_dir = if remapped { - RealFileName::Remapped { virtual_name: path.into_owned(), local_path: Some(working_dir) } - } else { - RealFileName::LocalPath(path.into_owned()) - }; + let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); + let working_dir = file_mapping.to_real_filename(&working_dir); let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals; @@ -2998,7 +2711,7 @@ pub mod nightly_options { is_nightly_build(matches.opt_str("crate-name").as_deref()) } - pub fn is_nightly_build(krate: Option<&str>) -> bool { + fn is_nightly_build(krate: Option<&str>) -> bool { UnstableFeatures::from_environment(krate).is_nightly_build() } @@ -3196,7 +2909,7 @@ pub(crate) mod dep_tracking { use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; use rustc_span::RealFileName; - use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; + use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel, WasmCAbi}; use rustc_target::spec::{ RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, }; @@ -3205,7 +2918,7 @@ pub(crate) mod dep_tracking { use std::num::NonZero; use std::path::PathBuf; - pub trait DepTrackingHash { + pub(crate) trait DepTrackingHash { fn hash( &self, hasher: &mut DefaultHasher, @@ -3294,6 +3007,7 @@ pub(crate) mod dep_tracking { Polonius, InliningThreshold, FunctionReturn, + WasmCAbi, ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs new file mode 100644 index 0000000000000..31badbd86927d --- /dev/null +++ b/compiler/rustc_session/src/config/cfg.rs @@ -0,0 +1,379 @@ +//! cfg and check-cfg configuration +//! +//! This module contains the definition of [`Cfg`] and [`CheckCfg`] +//! as well as the logic for creating the default configuration for a +//! given [`Session`]. +//! +//! It also contains the filling of the well known configs, which should +//! ALWAYS be in sync with the default_configuration. +//! +//! ## Adding a new cfg +//! +//! Adding a new feature requires two new symbols one for the cfg it-self +//! and the second one for the unstable feature gate, those are defined in +//! `rustc_span::symbol`. +//! +//! As well as the following points, +//! - Add the activation logic in [`default_configuration`] +//! - Add the cfg to [`CheckCfg::fill_well_known`] (and related files), +//! so that the compiler can know the cfg is expected +//! - Add the feature gating in `compiler/rustc_feature/src/builtin_attrs.rs` + +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_target::abi::Align; +use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet}; +use rustc_target::spec::{Target, TargetTriple, TARGETS}; + +use crate::config::CrateType; +use crate::Session; + +use std::hash::Hash; +use std::iter; + +/// The parsed `--cfg` options that define the compilation environment of the +/// crate, used to drive conditional compilation. +/// +/// An `FxIndexSet` is used to ensure deterministic ordering of error messages +/// relating to `--cfg`. +pub type Cfg = FxIndexSet<(Symbol, Option)>; + +/// The parsed `--check-cfg` options. +#[derive(Default)] +pub struct CheckCfg { + /// Is well known names activated + pub exhaustive_names: bool, + /// Is well known values activated + pub exhaustive_values: bool, + /// All the expected values for a config name + pub expecteds: FxHashMap>, + /// Well known names (only used for diagnostics purposes) + pub well_known_names: FxHashSet, +} + +pub enum ExpectedValues { + Some(FxHashSet>), + Any, +} + +impl ExpectedValues { + fn insert(&mut self, value: T) -> bool { + match self { + ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)), + ExpectedValues::Any => false, + } + } +} + +impl Extend for ExpectedValues { + fn extend>(&mut self, iter: I) { + match self { + ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(Some)), + ExpectedValues::Any => {} + } + } +} + +impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues { + fn extend>(&mut self, iter: I) { + match self { + ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))), + ExpectedValues::Any => {} + } + } +} + +/// Generate the default configs for a given session +pub(crate) fn default_configuration(sess: &Session) -> Cfg { + let mut ret = Cfg::default(); + + macro_rules! ins_none { + ($key:expr) => { + ret.insert(($key, None)); + }; + } + macro_rules! ins_str { + ($key:expr, $val_str:expr) => { + ret.insert(($key, Some(Symbol::intern($val_str)))); + }; + } + macro_rules! ins_sym { + ($key:expr, $val_sym:expr) => { + ret.insert(($key, Some($val_sym))); + }; + } + + // Symbols are inserted in alphabetical order as much as possible. + // The exceptions are where control flow forces things out of order. + // + // Run `rustc --print cfg` to see the configuration in practice. + // + // NOTE: These insertions should be kept in sync with + // `CheckCfg::fill_well_known` below. + + if sess.opts.debug_assertions { + ins_none!(sym::debug_assertions); + } + + if sess.overflow_checks() { + ins_none!(sym::overflow_checks); + } + + ins_sym!(sym::panic, sess.panic_strategy().desc_symbol()); + + // JUSTIFICATION: before wrapper fn is available + #[allow(rustc::bad_opt_access)] + if sess.opts.crate_types.contains(&CrateType::ProcMacro) { + ins_none!(sym::proc_macro); + } + + if sess.is_nightly_build() { + ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol()); + } + + for mut s in sess.opts.unstable_opts.sanitizer { + // KASAN is still ASAN under the hood, so it uses the same attribute. + if s == SanitizerSet::KERNELADDRESS { + s = SanitizerSet::ADDRESS; + } + ins_str!(sym::sanitize, &s.to_string()); + } + + if sess.is_sanitizer_cfi_generalize_pointers_enabled() { + ins_none!(sym::sanitizer_cfi_generalize_pointers); + } + if sess.is_sanitizer_cfi_normalize_integers_enabled() { + ins_none!(sym::sanitizer_cfi_normalize_integers); + } + + ins_str!(sym::target_abi, &sess.target.abi); + ins_str!(sym::target_arch, &sess.target.arch); + ins_str!(sym::target_endian, sess.target.endian.as_str()); + ins_str!(sym::target_env, &sess.target.env); + + for family in sess.target.families.as_ref() { + ins_str!(sym::target_family, family); + if family == "windows" { + ins_none!(sym::windows); + } else if family == "unix" { + ins_none!(sym::unix); + } + } + + // `target_has_atomic*` + let layout = sess.target.parse_data_layout().unwrap_or_else(|err| { + sess.dcx().emit_fatal(err); + }); + let mut has_atomic = false; + for (i, align) in [ + (8, layout.i8_align.abi), + (16, layout.i16_align.abi), + (32, layout.i32_align.abi), + (64, layout.i64_align.abi), + (128, layout.i128_align.abi), + ] { + if i >= sess.target.min_atomic_width() && i <= sess.target.max_atomic_width() { + if !has_atomic { + has_atomic = true; + if sess.is_nightly_build() { + if sess.target.atomic_cas { + ins_none!(sym::target_has_atomic); + } + ins_none!(sym::target_has_atomic_load_store); + } + } + let mut insert_atomic = |sym, align: Align| { + if sess.target.atomic_cas { + ins_sym!(sym::target_has_atomic, sym); + } + if align.bits() == i { + ins_sym!(sym::target_has_atomic_equal_alignment, sym); + } + ins_sym!(sym::target_has_atomic_load_store, sym); + }; + insert_atomic(sym::integer(i), align); + if sess.target.pointer_width as u64 == i { + insert_atomic(sym::ptr, layout.pointer_align.abi); + } + } + } + + ins_str!(sym::target_os, &sess.target.os); + ins_sym!(sym::target_pointer_width, sym::integer(sess.target.pointer_width)); + + if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) { + ins_none!(sym::target_thread_local); + } + + ins_str!(sym::target_vendor, &sess.target.vendor); + + // If the user wants a test runner, then add the test cfg. + if sess.is_test_crate() { + ins_none!(sym::test); + } + + if sess.ub_checks() { + ins_none!(sym::ub_checks); + } + + ret +} + +impl CheckCfg { + /// Fill the current [`CheckCfg`] with all the well known cfgs + pub fn fill_well_known(&mut self, current_target: &Target) { + if !self.exhaustive_values && !self.exhaustive_names { + return; + } + + // for `#[cfg(foo)]` (ie. cfg value is none) + let no_values = || { + let mut values = FxHashSet::default(); + values.insert(None); + ExpectedValues::Some(values) + }; + + // preparation for inserting some values + let empty_values = || { + let values = FxHashSet::default(); + ExpectedValues::Some(values) + }; + + macro_rules! ins { + ($name:expr, $values:expr) => {{ + self.well_known_names.insert($name); + self.expecteds.entry($name).or_insert_with($values) + }}; + } + + // Symbols are inserted in alphabetical order as much as possible. + // The exceptions are where control flow forces things out of order. + // + // NOTE: This should be kept in sync with `default_configuration`. + // Note that symbols inserted conditionally in `default_configuration` + // are inserted unconditionally here. + // + // When adding a new config here you should also update + // `tests/ui/check-cfg/well-known-values.rs` (in order to test the + // expected values of the new config) and bless the all directory. + // + // Don't forget to update `src/doc/rustc/src/check-cfg.md` + // in the unstable book as well! + + ins!(sym::debug_assertions, no_values); + + // These four are never set by rustc, but we set them anyway: they + // should not trigger a lint because `cargo clippy`, `cargo doc`, + // `cargo test` and `cargo miri run` (respectively) can set them. + ins!(sym::clippy, no_values); + ins!(sym::doc, no_values); + ins!(sym::doctest, no_values); + ins!(sym::miri, no_values); + + ins!(sym::overflow_checks, no_values); + + ins!(sym::panic, empty_values).extend(&PanicStrategy::all()); + + ins!(sym::proc_macro, no_values); + + ins!(sym::relocation_model, empty_values).extend(RelocModel::all()); + + let sanitize_values = SanitizerSet::all() + .into_iter() + .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap())); + ins!(sym::sanitize, empty_values).extend(sanitize_values); + + ins!(sym::sanitizer_cfi_generalize_pointers, no_values); + ins!(sym::sanitizer_cfi_normalize_integers, no_values); + + ins!(sym::target_feature, empty_values).extend( + rustc_target::target_features::all_known_features() + .map(|(f, _sb)| f) + .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned()) + .map(Symbol::intern), + ); + + // sym::target_* + { + const VALUES: [&Symbol; 8] = [ + &sym::target_abi, + &sym::target_arch, + &sym::target_endian, + &sym::target_env, + &sym::target_family, + &sym::target_os, + &sym::target_pointer_width, + &sym::target_vendor, + ]; + + // Initialize (if not already initialized) + for &e in VALUES { + if !self.exhaustive_values { + ins!(e, || ExpectedValues::Any); + } else { + ins!(e, empty_values); + } + } + + if self.exhaustive_values { + // Get all values map at once otherwise it would be costly. + // (8 values * 220 targets ~= 1760 times, at the time of writing this comment). + let [ + values_target_abi, + values_target_arch, + values_target_endian, + values_target_env, + values_target_family, + values_target_os, + values_target_pointer_width, + values_target_vendor, + ] = self + .expecteds + .get_many_mut(VALUES) + .expect("unable to get all the check-cfg values buckets"); + + for target in TARGETS + .iter() + .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target))) + .chain(iter::once(current_target.clone())) + { + values_target_abi.insert(Symbol::intern(&target.options.abi)); + values_target_arch.insert(Symbol::intern(&target.arch)); + values_target_endian.insert(Symbol::intern(target.options.endian.as_str())); + values_target_env.insert(Symbol::intern(&target.options.env)); + values_target_family.extend( + target.options.families.iter().map(|family| Symbol::intern(family)), + ); + values_target_os.insert(Symbol::intern(&target.options.os)); + values_target_pointer_width.insert(sym::integer(target.pointer_width)); + values_target_vendor.insert(Symbol::intern(&target.options.vendor)); + } + } + } + + let atomic_values = &[ + sym::ptr, + sym::integer(8usize), + sym::integer(16usize), + sym::integer(32usize), + sym::integer(64usize), + sym::integer(128usize), + ]; + for sym in [ + sym::target_has_atomic, + sym::target_has_atomic_equal_alignment, + sym::target_has_atomic_load_store, + ] { + ins!(sym, no_values).extend(atomic_values); + } + + ins!(sym::target_thread_local, no_values); + + ins!(sym::test, no_values); + + ins!(sym::ub_checks, no_values); + + ins!(sym::unix, no_values); + ins!(sym::windows, no_values); + } +} diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index a0f5eb59b6a25..2c20c3f0e1a34 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -4,12 +4,13 @@ use crate::search_paths::PathKind; use crate::utils::NativeLibKind; -use crate::Session; use rustc_ast as ast; use rustc_data_structures::sync::{self, AppendOnlyIndexVec, FreezeLock}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, StableCrateId, LOCAL_CRATE}; +use rustc_hir::def_id::{ + CrateNum, DefId, LocalDefId, StableCrateId, StableCrateIdMap, LOCAL_CRATE, +}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, Definitions}; -use rustc_span::hygiene::{ExpnHash, ExpnId}; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::spec::abi::Abi; @@ -219,23 +220,6 @@ pub trait CrateStore: std::fmt::Debug { // incr. comp. uses to identify a CrateNum. fn crate_name(&self, cnum: CrateNum) -> Symbol; fn stable_crate_id(&self, cnum: CrateNum) -> StableCrateId; - fn stable_crate_id_to_crate_num(&self, stable_crate_id: StableCrateId) -> CrateNum; - - /// Fetch a DefId from a DefPathHash for a foreign crate. - fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId; - fn expn_hash_to_expn_id( - &self, - sess: &Session, - cnum: CrateNum, - index_guess: u32, - hash: ExpnHash, - ) -> ExpnId; - - /// Imports all `SourceFile`s from the given crate into the current session. - /// This normally happens automatically when we decode a `Span` from - /// that crate's metadata - however, the incr comp cache needs - /// to trigger this manually when decoding a foreign `Span` - fn import_source_files(&self, sess: &Session, cnum: CrateNum); } pub type CrateStoreDyn = dyn CrateStore + sync::DynSync + sync::DynSend; @@ -245,4 +229,6 @@ pub struct Untracked { /// Reference span for definitions. pub source_span: AppendOnlyIndexVec, pub definitions: FreezeLock, + /// The interned [StableCrateId]s. + pub stable_crate_ids: FreezeLock, } diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index d523da1ad7e38..dce56382a53a0 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -6,15 +6,15 @@ use rustc_errors::{ codes::*, Diag, DiagCtxt, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, Level, MultiSpan, }; -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple}; use crate::{config::CrateType, parse::ParseSess}; -pub struct FeatureGateError { - pub span: MultiSpan, - pub explain: DiagMessage, +pub(crate) struct FeatureGateError { + pub(crate) span: MultiSpan, + pub(crate) explain: DiagMessage, } impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for FeatureGateError { @@ -26,22 +26,22 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for FeatureGateError { #[derive(Subdiagnostic)] #[note(session_feature_diagnostic_for_issue)] -pub struct FeatureDiagnosticForIssue { - pub n: NonZero, +pub(crate) struct FeatureDiagnosticForIssue { + pub(crate) n: NonZero, } #[derive(Subdiagnostic)] #[note(session_feature_suggest_upgrade_compiler)] -pub struct SuggestUpgradeCompiler { +pub(crate) struct SuggestUpgradeCompiler { date: &'static str, } impl SuggestUpgradeCompiler { - pub fn ui_testing() -> Self { + pub(crate) fn ui_testing() -> Self { Self { date: "YYYY-MM-DD" } } - pub fn new() -> Option { + pub(crate) fn new() -> Option { let date = option_env!("CFG_VER_DATE")?; Some(Self { date }) @@ -50,108 +50,124 @@ impl SuggestUpgradeCompiler { #[derive(Subdiagnostic)] #[help(session_feature_diagnostic_help)] -pub struct FeatureDiagnosticHelp { +pub(crate) struct FeatureDiagnosticHelp { + pub(crate) feature: Symbol, +} + +#[derive(Subdiagnostic)] +#[suggestion( + session_feature_diagnostic_suggestion, + applicability = "maybe-incorrect", + code = "#![feature({feature})]\n" +)] +pub struct FeatureDiagnosticSuggestion { pub feature: Symbol, + #[primary_span] + pub span: Span, } #[derive(Subdiagnostic)] #[help(session_cli_feature_diagnostic_help)] -pub struct CliFeatureDiagnosticHelp { - pub feature: Symbol, +pub(crate) struct CliFeatureDiagnosticHelp { + pub(crate) feature: Symbol, } #[derive(Diagnostic)] #[diag(session_not_circumvent_feature)] -pub struct NotCircumventFeature; +pub(crate) struct NotCircumventFeature; #[derive(Diagnostic)] #[diag(session_linker_plugin_lto_windows_not_supported)] -pub struct LinkerPluginToWindowsNotSupported; +pub(crate) struct LinkerPluginToWindowsNotSupported; #[derive(Diagnostic)] #[diag(session_profile_use_file_does_not_exist)] -pub struct ProfileUseFileDoesNotExist<'a> { - pub path: &'a std::path::Path, +pub(crate) struct ProfileUseFileDoesNotExist<'a> { + pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] #[diag(session_profile_sample_use_file_does_not_exist)] -pub struct ProfileSampleUseFileDoesNotExist<'a> { - pub path: &'a std::path::Path, +pub(crate) struct ProfileSampleUseFileDoesNotExist<'a> { + pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] #[diag(session_target_requires_unwind_tables)] -pub struct TargetRequiresUnwindTables; +pub(crate) struct TargetRequiresUnwindTables; #[derive(Diagnostic)] #[diag(session_instrumentation_not_supported)] -pub struct InstrumentationNotSupported { - pub us: String, +pub(crate) struct InstrumentationNotSupported { + pub(crate) us: String, } #[derive(Diagnostic)] #[diag(session_sanitizer_not_supported)] -pub struct SanitizerNotSupported { - pub us: String, +pub(crate) struct SanitizerNotSupported { + pub(crate) us: String, } #[derive(Diagnostic)] #[diag(session_sanitizers_not_supported)] -pub struct SanitizersNotSupported { - pub us: String, +pub(crate) struct SanitizersNotSupported { + pub(crate) us: String, } #[derive(Diagnostic)] #[diag(session_cannot_mix_and_match_sanitizers)] -pub struct CannotMixAndMatchSanitizers { - pub first: String, - pub second: String, +pub(crate) struct CannotMixAndMatchSanitizers { + pub(crate) first: String, + pub(crate) second: String, } #[derive(Diagnostic)] #[diag(session_cannot_enable_crt_static_linux)] -pub struct CannotEnableCrtStaticLinux; +pub(crate) struct CannotEnableCrtStaticLinux; #[derive(Diagnostic)] #[diag(session_sanitizer_cfi_requires_lto)] -pub struct SanitizerCfiRequiresLto; +pub(crate) struct SanitizerCfiRequiresLto; #[derive(Diagnostic)] #[diag(session_sanitizer_cfi_requires_single_codegen_unit)] -pub struct SanitizerCfiRequiresSingleCodegenUnit; +pub(crate) struct SanitizerCfiRequiresSingleCodegenUnit; #[derive(Diagnostic)] #[diag(session_sanitizer_cfi_canonical_jump_tables_requires_cfi)] -pub struct SanitizerCfiCanonicalJumpTablesRequiresCfi; +pub(crate) struct SanitizerCfiCanonicalJumpTablesRequiresCfi; #[derive(Diagnostic)] #[diag(session_sanitizer_cfi_generalize_pointers_requires_cfi)] -pub struct SanitizerCfiGeneralizePointersRequiresCfi; +pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi; #[derive(Diagnostic)] #[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)] -pub struct SanitizerCfiNormalizeIntegersRequiresCfi; +pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi; + +#[derive(Diagnostic)] +#[diag(session_sanitizer_kcfi_requires_panic_abort)] +pub(crate) struct SanitizerKcfiRequiresPanicAbort; #[derive(Diagnostic)] #[diag(session_split_lto_unit_requires_lto)] -pub struct SplitLtoUnitRequiresLto; +pub(crate) struct SplitLtoUnitRequiresLto; #[derive(Diagnostic)] #[diag(session_unstable_virtual_function_elimination)] -pub struct UnstableVirtualFunctionElimination; +pub(crate) struct UnstableVirtualFunctionElimination; #[derive(Diagnostic)] #[diag(session_unsupported_dwarf_version)] -pub struct UnsupportedDwarfVersion { - pub dwarf_version: u32, +pub(crate) struct UnsupportedDwarfVersion { + pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] #[diag(session_target_stack_protector_not_supported)] -pub struct StackProtectorNotSupportedForTarget<'a> { - pub stack_protector: StackProtector, - pub target_triple: &'a TargetTriple, +pub(crate) struct StackProtectorNotSupportedForTarget<'a> { + pub(crate) stack_protector: StackProtector, + pub(crate) target_triple: &'a TargetTriple, } #[derive(Diagnostic)] @@ -160,58 +176,58 @@ pub(crate) struct BranchProtectionRequiresAArch64; #[derive(Diagnostic)] #[diag(session_split_debuginfo_unstable_platform)] -pub struct SplitDebugInfoUnstablePlatform { - pub debuginfo: SplitDebuginfo, +pub(crate) struct SplitDebugInfoUnstablePlatform { + pub(crate) debuginfo: SplitDebuginfo, } #[derive(Diagnostic)] #[diag(session_file_is_not_writeable)] -pub struct FileIsNotWriteable<'a> { - pub file: &'a std::path::Path, +pub(crate) struct FileIsNotWriteable<'a> { + pub(crate) file: &'a std::path::Path, } #[derive(Diagnostic)] #[diag(session_file_write_fail)] pub(crate) struct FileWriteFail<'a> { - pub path: &'a std::path::Path, - pub err: String, + pub(crate) path: &'a std::path::Path, + pub(crate) err: String, } #[derive(Diagnostic)] #[diag(session_crate_name_does_not_match)] -pub struct CrateNameDoesNotMatch { +pub(crate) struct CrateNameDoesNotMatch { #[primary_span] - pub span: Span, - pub s: Symbol, - pub name: Symbol, + pub(crate) span: Span, + pub(crate) s: Symbol, + pub(crate) name: Symbol, } #[derive(Diagnostic)] #[diag(session_crate_name_invalid)] -pub struct CrateNameInvalid<'a> { - pub s: &'a str, +pub(crate) struct CrateNameInvalid<'a> { + pub(crate) s: &'a str, } #[derive(Diagnostic)] #[diag(session_crate_name_empty)] -pub struct CrateNameEmpty { +pub(crate) struct CrateNameEmpty { #[primary_span] - pub span: Option, + pub(crate) span: Option, } #[derive(Diagnostic)] #[diag(session_invalid_character_in_create_name)] -pub struct InvalidCharacterInCrateName { +pub(crate) struct InvalidCharacterInCrateName { #[primary_span] - pub span: Option, - pub character: char, - pub crate_name: Symbol, + pub(crate) span: Option, + pub(crate) character: char, + pub(crate) crate_name: Symbol, #[subdiagnostic] - pub crate_name_help: Option, + pub(crate) crate_name_help: Option, } #[derive(Subdiagnostic)] -pub enum InvalidCrateNameHelp { +pub(crate) enum InvalidCrateNameHelp { #[help(session_invalid_character_in_create_name_help)] AddCrateName, } @@ -220,9 +236,9 @@ pub enum InvalidCrateNameHelp { #[multipart_suggestion(session_expr_parentheses_needed, applicability = "machine-applicable")] pub struct ExprParenthesesNeeded { #[suggestion_part(code = "(")] - pub left: Span, + left: Span, #[suggestion_part(code = ")")] - pub right: Span, + right: Span, } impl ExprParenthesesNeeded { @@ -233,13 +249,13 @@ impl ExprParenthesesNeeded { #[derive(Diagnostic)] #[diag(session_skipping_const_checks)] -pub struct SkippingConstChecks { +pub(crate) struct SkippingConstChecks { #[subdiagnostic] - pub unleashed_features: Vec, + pub(crate) unleashed_features: Vec, } #[derive(Subdiagnostic)] -pub enum UnleashedFeatureHelp { +pub(crate) enum UnleashedFeatureHelp { #[help(session_unleashed_feature_help_named)] Named { #[primary_span] @@ -255,101 +271,101 @@ pub enum UnleashedFeatureHelp { #[derive(Diagnostic)] #[diag(session_invalid_literal_suffix)] -pub(crate) struct InvalidLiteralSuffix<'a> { +struct InvalidLiteralSuffix<'a> { #[primary_span] #[label] - pub span: Span, + span: Span, // FIXME(#100717) - pub kind: &'a str, - pub suffix: Symbol, + kind: &'a str, + suffix: Symbol, } #[derive(Diagnostic)] #[diag(session_invalid_int_literal_width)] #[help] -pub(crate) struct InvalidIntLiteralWidth { +struct InvalidIntLiteralWidth { #[primary_span] - pub span: Span, - pub width: String, + span: Span, + width: String, } #[derive(Diagnostic)] #[diag(session_invalid_num_literal_base_prefix)] #[note] -pub(crate) struct InvalidNumLiteralBasePrefix { +struct InvalidNumLiteralBasePrefix { #[primary_span] #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")] - pub span: Span, - pub fixed: String, + span: Span, + fixed: String, } #[derive(Diagnostic)] #[diag(session_invalid_num_literal_suffix)] #[help] -pub(crate) struct InvalidNumLiteralSuffix { +struct InvalidNumLiteralSuffix { #[primary_span] #[label] - pub span: Span, - pub suffix: String, + span: Span, + suffix: String, } #[derive(Diagnostic)] #[diag(session_invalid_float_literal_width)] #[help] -pub(crate) struct InvalidFloatLiteralWidth { +struct InvalidFloatLiteralWidth { #[primary_span] - pub span: Span, - pub width: String, + span: Span, + width: String, } #[derive(Diagnostic)] #[diag(session_invalid_float_literal_suffix)] #[help] -pub(crate) struct InvalidFloatLiteralSuffix { +struct InvalidFloatLiteralSuffix { #[primary_span] #[label] - pub span: Span, - pub suffix: String, + span: Span, + suffix: String, } #[derive(Diagnostic)] #[diag(session_int_literal_too_large)] #[note] -pub(crate) struct IntLiteralTooLarge { +struct IntLiteralTooLarge { #[primary_span] - pub span: Span, - pub limit: String, + span: Span, + limit: String, } #[derive(Diagnostic)] #[diag(session_hexadecimal_float_literal_not_supported)] -pub(crate) struct HexadecimalFloatLiteralNotSupported { +struct HexadecimalFloatLiteralNotSupported { #[primary_span] #[label(session_not_supported)] - pub span: Span, + span: Span, } #[derive(Diagnostic)] #[diag(session_octal_float_literal_not_supported)] -pub(crate) struct OctalFloatLiteralNotSupported { +struct OctalFloatLiteralNotSupported { #[primary_span] #[label(session_not_supported)] - pub span: Span, + span: Span, } #[derive(Diagnostic)] #[diag(session_binary_float_literal_not_supported)] -pub(crate) struct BinaryFloatLiteralNotSupported { +struct BinaryFloatLiteralNotSupported { #[primary_span] #[label(session_not_supported)] - pub span: Span, + span: Span, } #[derive(Diagnostic)] #[diag(session_unsupported_crate_type_for_target)] -pub struct UnsupportedCrateTypeForTarget<'a> { - pub crate_type: CrateType, - pub target_triple: &'a TargetTriple, +pub(crate) struct UnsupportedCrateTypeForTarget<'a> { + pub(crate) crate_type: CrateType, + pub(crate) target_triple: &'a TargetTriple, } pub fn report_lit_error( @@ -431,16 +447,16 @@ pub fn report_lit_error( #[derive(Diagnostic)] #[diag(session_optimization_fuel_exhausted)] -pub struct OptimisationFuelExhausted { - pub msg: String, +pub(crate) struct OptimisationFuelExhausted { + pub(crate) msg: String, } #[derive(Diagnostic)] #[diag(session_incompatible_linker_flavor)] #[note] -pub struct IncompatibleLinkerFlavor { - pub flavor: &'static str, - pub compatible_list: String, +pub(crate) struct IncompatibleLinkerFlavor { + pub(crate) flavor: &'static str, + pub(crate) compatible_list: String, } #[derive(Diagnostic)] @@ -453,6 +469,6 @@ pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel; #[derive(Diagnostic)] #[diag(session_failed_to_create_profiler)] -pub struct FailedToCreateProfiler { - pub err: String, +pub(crate) struct FailedToCreateProfiler { + pub(crate) err: String, } diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 4f0e3354680ac..f4e2436efb9fb 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -1,13 +1,12 @@ //! A module for searching for libraries -use rustc_fs_util::try_canonicalize; +use crate::search_paths::{PathKind, SearchPath}; +use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use smallvec::{smallvec, SmallVec}; use std::env; use std::fs; use std::path::{Path, PathBuf}; - -use crate::search_paths::{PathKind, SearchPath}; -use rustc_fs_util::fix_windows_verbatim_for_gcc; +use tracing::debug; #[derive(Clone)] pub struct FileSearch<'a> { @@ -45,11 +44,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 { - 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/lib.rs b/compiler/rustc_session/src/lib.rs index c63af90a7f312..ce866906e1e69 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(lazy_cell)] #![feature(option_get_or_insert_default)] @@ -7,13 +6,8 @@ #![feature(iter_intersperse)] #![allow(internal_features)] -#[macro_use] -extern crate rustc_macros; pub mod errors; -#[macro_use] -extern crate tracing; - pub mod utils; pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass}; pub use rustc_lint_defs as lint; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b0bd5e757352d..7355e5b695347 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1,5 +1,4 @@ use crate::config::*; - use crate::search_paths::SearchPath; use crate::utils::NativeLib; use crate::{lint, EarlyDiagCtxt}; @@ -8,18 +7,17 @@ use rustc_data_structures::profiling::TimePassesFormat; use rustc_data_structures::stable_hasher::Hash64; use rustc_errors::ColorConfig; use rustc_errors::{LanguageIdentifier, TerminalUrl}; -use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet}; -use rustc_target::spec::{ - RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, -}; - use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; use rustc_span::RealFileName; use rustc_span::SourceFileHashAlgorithm; - +use rustc_target::spec::{ + CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet, WasmCAbi, +}; +use rustc_target::spec::{ + RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, +}; use std::collections::BTreeMap; - use std::hash::{DefaultHasher, Hasher}; use std::num::{IntErrorKind, NonZero}; use std::path::PathBuf; @@ -116,8 +114,8 @@ top_level_options!( /// incremental compilation cache before proceeding. /// /// - `[TRACKED_NO_CRATE_HASH]` - /// Same as `[TRACKED]`, but will not affect the crate hash. This is useful for options that only - /// affect the incremental cache. + /// Same as `[TRACKED]`, but will not affect the crate hash. This is useful for options that + /// only affect the incremental cache. /// /// - `[UNTRACKED]` /// Incremental compilation is not influenced by this option. @@ -396,7 +394,7 @@ mod desc { pub const parse_optimization_fuel: &str = "crate=integer"; pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub const parse_instrument_coverage: &str = parse_bool; - pub const parse_coverage_options: &str = "`branch` or `no-branch`"; + pub const parse_coverage_options: &str = "`block` | `branch` | `mcdc`"; pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; @@ -426,6 +424,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`)"; @@ -433,11 +433,13 @@ mod desc { "a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`"; pub const parse_proc_macro_execution_strategy: &str = "one of supported execution strategies (`same-thread`, or `cross-thread`)"; - pub const parse_remap_path_scope: &str = "comma separated list of scopes: `macro`, `diagnostics`, `unsplit-debuginfo`, `split-debuginfo`, `split-debuginfo-path`, `object`, `all`"; + pub const parse_remap_path_scope: &str = + "comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `object`, `all`"; pub const parse_inlining_threshold: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number"; pub const parse_llvm_module_flag: &str = ":::. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; pub const parse_function_return: &str = "`keep` or `thunk-extern`"; + pub const parse_wasm_c_abi: &str = "`legacy` or `spec`"; } mod parse { @@ -943,17 +945,13 @@ mod parse { let Some(v) = v else { return true }; for option in v.split(',') { - let (option, enabled) = match option.strip_prefix("no-") { - Some(without_no) => (without_no, false), - None => (option, true), - }; - let slot = match option { - "branch" => &mut slot.branch, + match option { + "block" => slot.level = CoverageLevel::Block, + "branch" => slot.level = CoverageLevel::Branch, + "mcdc" => slot.level = CoverageLevel::Mcdc, _ => return false, - }; - *slot = enabled; + } } - true } @@ -1156,9 +1154,7 @@ mod parse { *slot |= match s { "macro" => RemapPathScopeComponents::MACRO, "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS, - "unsplit-debuginfo" => RemapPathScopeComponents::UNSPLIT_DEBUGINFO, - "split-debuginfo" => RemapPathScopeComponents::SPLIT_DEBUGINFO, - "split-debuginfo-path" => RemapPathScopeComponents::SPLIT_DEBUGINFO_PATH, + "debuginfo" => RemapPathScopeComponents::DEBUGINFO, "object" => RemapPathScopeComponents::OBJECT, "all" => RemapPathScopeComponents::all(), _ => return false, @@ -1270,6 +1266,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, v: Option<&str>) -> bool { match v { Some("command") => *slot = Some(WasiExecModel::Command), @@ -1340,10 +1352,20 @@ mod parse { slot: &mut CollapseMacroDebuginfo, v: Option<&str>, ) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + CollapseMacroDebuginfo::Yes + } else { + CollapseMacroDebuginfo::No + }; + return true; + } + } + *slot = match v { - Some("no") => CollapseMacroDebuginfo::No, Some("external") => CollapseMacroDebuginfo::External, - Some("yes") => CollapseMacroDebuginfo::Yes, _ => return false, }; true @@ -1416,6 +1438,15 @@ mod parse { } true } + + pub(crate) fn parse_wasm_c_abi(slot: &mut WasmCAbi, v: Option<&str>) -> bool { + match v { + Some("spec") => *slot = WasmCAbi::Spec, + Some("legacy") => *slot = WasmCAbi::Legacy, + _ => return false, + } + true + } } options! { @@ -1433,6 +1464,9 @@ options! { "choose the code model to use (`rustc --print code-models` for details)"), codegen_units: Option = (None, parse_opt_number, [UNTRACKED], "divide crate into N units to optimize in parallel"), + collapse_macro_debuginfo: CollapseMacroDebuginfo = (CollapseMacroDebuginfo::Unspecified, + parse_collapse_macro_debuginfo, [TRACKED], + "set option to collapse debuginfo for macros"), control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [TRACKED], "use Windows Control Flow Guard (default: no)"), debug_assertions: Option = (None, parse_opt_bool, [TRACKED], @@ -1517,6 +1551,8 @@ options! { relocation_model: Option = (None, parse_relocation_model, [TRACKED], "control generation of position-independent code (PIC) \ (`rustc --print relocation-models` for details)"), + relro_level: Option = (None, parse_relro_level, [TRACKED], + "choose which RELRO level to use"), remark: Passes = (Passes::Some(Vec::new()), parse_passes, [UNTRACKED], "output remarks for these optimization passes (space separated, or \"all\")"), rpath: bool = (false, parse_bool, [UNTRACKED], @@ -1578,9 +1614,6 @@ options! { "show all expected values in check-cfg diagnostics (default: no)"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), - collapse_macro_debuginfo: CollapseMacroDebuginfo = (CollapseMacroDebuginfo::Unspecified, - parse_collapse_macro_debuginfo, [TRACKED], - "set option to collapse debuginfo for macros"), combine_cgu: bool = (false, parse_bool, [TRACKED], "combine CGUs into a single one"), coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED], @@ -1591,8 +1624,6 @@ options! { "threshold to allow cross crate inlining of functions"), debug_info_for_profiling: bool = (false, parse_bool, [TRACKED], "emit discriminators and other data necessary for AutoFDO"), - debug_macros: bool = (false, parse_bool, [TRACKED], - "emit line numbers debug info inside macros (default: no)"), debuginfo_compression: DebugInfoCompression = (DebugInfoCompression::None, parse_debuginfo_compression, [TRACKED], "compress debug info sections (none, zlib, zstd, default: none)"), deduplicate_diagnostics: bool = (true, parse_bool, [UNTRACKED], @@ -1698,6 +1729,9 @@ options! { "enable MIR inlining (default: no)"), inline_mir_hint_threshold: Option = (None, parse_opt_number, [TRACKED], "inlining threshold for functions with inline hint (default: 100)"), + inline_mir_preserve_debug: Option = (None, parse_opt_bool, [TRACKED], + "when MIR inlining, whether to preserve debug info for callee variables \ + (default: preserve for debuginfo != None, otherwise remove)"), inline_mir_threshold: Option = (None, parse_opt_number, [TRACKED], "a default MIR inlining threshold (default: 50)"), input_stats: bool = (false, parse_bool, [UNTRACKED], @@ -1722,6 +1756,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], @@ -1862,8 +1898,6 @@ options! { "randomize the layout of types (default: no)"), relax_elf_relocations: Option = (None, parse_opt_bool, [TRACKED], "whether ELF relocations can be relaxed"), - relro_level: Option = (None, parse_relro_level, [TRACKED], - "choose which RELRO level to use"), remap_cwd_prefix: Option = (None, parse_opt_pathbuf, [TRACKED], "remap paths under the current working directory to this path prefix"), remap_path_scope: RemapPathScopeComponents = (RemapPathScopeComponents::all(), parse_remap_path_scope, [TRACKED], @@ -1951,8 +1985,6 @@ written to standard error output)"), #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), - thir_unsafeck: bool = (true, parse_bool, [TRACKED], - "use the THIR unsafety checker (default: yes)"), /// We default to 1 here since we want to behave like /// a sequential compiler for now. This'll likely be adjusted /// in the future. Note that -Zthreads=0 is the way to get @@ -1995,6 +2027,9 @@ written to standard error output)"), "in diagnostics, use heuristics to shorten paths referring to items"), tune_cpu: Option = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), + #[rustc_lint_opt_deny_field_access("use `Session::ub_checks` instead of this field")] + ub_checks: Option = (None, parse_opt_bool, [TRACKED], + "emit runtime checks for Undefined Behavior (default: -Cdebug-assertions)"), ui_testing: bool = (false, parse_bool, [UNTRACKED], "emit compiler diagnostics in a form suitable for UI testing (default: no)"), uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED], @@ -2038,6 +2073,8 @@ written to standard error output)"), Requires `-Clto[=[fat,yes]]`"), wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED], "whether to build a wasi command or reactor"), + wasm_c_abi: WasmCAbi = (WasmCAbi::Legacy, parse_wasm_c_abi, [TRACKED], + "use spec-compliant C ABI for `wasm32-unknown-unknown` (default: legacy)"), write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], "whether long type names should be written to files instead of being printed in errors"), // tidy-alphabetical-end diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 398138d7e1ff3..f6053f43fbd19 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -3,8 +3,8 @@ use crate::config::{Cfg, CheckCfg}; use crate::errors::{ - CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError, - SuggestUpgradeCompiler, + CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, + FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler, }; use crate::lint::{ builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiag, Lint, LintId, @@ -112,7 +112,7 @@ pub fn feature_err_issue( } let mut err = sess.psess.dcx.create_err(FeatureGateError { span, explain: explain.into() }); - add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); err } @@ -141,7 +141,7 @@ pub fn feature_warn_issue( explain: &'static str, ) { let mut err = sess.psess.dcx.struct_span_warn(span, explain); - add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; @@ -160,7 +160,7 @@ pub fn add_feature_diagnostics( sess: &Session, feature: Symbol, ) { - add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false); + add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None); } /// Adds the diagnostics for a feature to an existing error. @@ -175,6 +175,7 @@ pub fn add_feature_diagnostics_for_issue( feature: Symbol, issue: GateIssue, feature_from_cli: bool, + inject_span: Option, ) { if let Some(n) = find_feature_issue(feature, issue) { err.subdiagnostic(sess.dcx(), FeatureDiagnosticForIssue { n }); @@ -184,6 +185,8 @@ pub fn add_feature_diagnostics_for_issue( if sess.psess.unstable_features.is_nightly_build() { if feature_from_cli { err.subdiagnostic(sess.dcx(), CliFeatureDiagnosticHelp { feature }); + } else if let Some(span) = inject_span { + err.subdiagnostic(sess.dcx(), FeatureDiagnosticSuggestion { feature, span }); } else { err.subdiagnostic(sess.dcx(), FeatureDiagnosticHelp { feature }); } @@ -266,7 +269,11 @@ impl ParseSess { } } - pub fn with_silent_emitter(locale_resources: Vec<&'static str>, fatal_note: String) -> Self { + pub fn with_silent_emitter( + locale_resources: Vec<&'static str>, + fatal_note: String, + emit_fatal_diagnostic: bool, + ) -> Self { let fallback_bundle = fallback_fluent_bundle(locale_resources, false); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let emitter = Box::new(HumanEmitter::new( @@ -278,6 +285,7 @@ impl ParseSess { fallback_bundle, fatal_dcx, fatal_note: Some(fatal_note), + emit_fatal_diagnostic, })) .disable_warnings(); ParseSess::with_dcx(dcx, sm) diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index 32d5e430717bd..5e8adffc249e6 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -1,5 +1,7 @@ use crate::filesearch::make_target_lib_path; use crate::EarlyDiagCtxt; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_target::spec::TargetTriple; use std::path::{Path, PathBuf}; #[derive(Clone, Debug)] @@ -46,7 +48,13 @@ impl PathKind { } impl SearchPath { - pub fn from_cli_opt(early_dcx: &EarlyDiagCtxt, path: &str) -> Self { + pub fn from_cli_opt( + sysroot: &Path, + triple: &TargetTriple, + early_dcx: &EarlyDiagCtxt, + path: &str, + is_unstable_enabled: bool, + ) -> Self { let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") { (PathKind::Native, stripped) } else if let Some(stripped) = path.strip_prefix("crate=") { @@ -60,12 +68,25 @@ impl SearchPath { } else { (PathKind::All, path) }; - if path.is_empty() { + let dir = match path.strip_prefix("@RUSTC_BUILTIN") { + Some(stripped) => { + if !is_unstable_enabled { + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable + early_dcx.early_fatal( + "the `-Z unstable-options` flag must also be passed to \ + enable the use of `@RUSTC_BUILTIN`", + ); + } + + make_target_lib_path(sysroot, triple.triple()).join("builtin").join(stripped) + } + None => PathBuf::from(path), + }; + if dir.as_os_str().is_empty() { #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable early_dcx.early_fatal("empty search path given via `-L`"); } - let dir = PathBuf::from(path); Self::new(kind, dir) } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9c94fd7027fd7..db01bb90d6fac 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,8 +1,8 @@ use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use crate::config::{ - self, CrateType, FunctionReturn, InstrumentCoverage, OptLevel, OutFileName, OutputType, - RemapPathScopeComponents, SwitchWithOptPath, + self, CoverageLevel, CrateType, FunctionReturn, InstrumentCoverage, OptLevel, OutFileName, + OutputType, RemapPathScopeComponents, SwitchWithOptPath, }; use crate::config::{ErrorOutputType, Input}; use crate::errors; @@ -28,8 +28,9 @@ use rustc_errors::{ use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; use rustc_span::edition::Edition; -use rustc_span::source_map::{FileLoader, FilePathMapping, RealFileLoader, SourceMap}; -use rustc_span::{SourceFileHashAlgorithm, Span, Symbol}; +use rustc_span::source_map::{FilePathMapping, SourceMap}; +use rustc_span::{FileNameDisplayPreference, RealFileName}; +use rustc_span::{Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; use rustc_target::spec::{ @@ -250,13 +251,8 @@ impl Session { self.miri_unleashed_features.lock().push((span, feature_gate)); } - pub fn local_crate_source_file(&self) -> Option { - let path = self.io.input.opt_path()?; - if self.should_prefer_remapped_for_codegen() { - Some(self.opts.file_path_mapping().map_prefix(path).0.into_owned()) - } else { - Some(path.to_path_buf()) - } + pub fn local_crate_source_file(&self) -> Option { + Some(self.source_map().path_mapping().to_real_filename(self.io.input.opt_path()?)) } fn check_miri_unleashed_features(&self) -> Option { @@ -353,7 +349,13 @@ impl Session { } pub fn instrument_coverage_branch(&self) -> bool { - self.instrument_coverage() && self.opts.unstable_opts.coverage_options.branch + self.instrument_coverage() + && self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Branch + } + + pub fn instrument_coverage_mcdc(&self) -> bool { + self.instrument_coverage() + && self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Mcdc } pub fn is_sanitizer_cfi_enabled(&self) -> bool { @@ -577,7 +579,7 @@ impl Session { let dbg_opts = &self.opts.unstable_opts; - let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); + let relro_level = self.opts.cg.relro_level.unwrap_or(self.target.relro_level); // Only enable this optimization by default if full relro is also enabled. // In this case, lazy binding was already unavailable, so nothing is lost. @@ -739,6 +741,10 @@ impl Session { self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) } + pub fn ub_checks(&self) -> bool { + self.opts.unstable_opts.ub_checks.unwrap_or(self.opts.debug_assertions) + } + pub fn relocation_model(&self) -> RelocModel { self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) } @@ -886,38 +892,19 @@ impl Session { self.opts.cg.link_dead_code.unwrap_or(false) } - pub fn should_prefer_remapped_for_codegen(&self) -> bool { - let has_split_debuginfo = match self.split_debuginfo() { - SplitDebuginfo::Off => false, - SplitDebuginfo::Packed => true, - SplitDebuginfo::Unpacked => true, - }; - - let remap_path_scopes = &self.opts.unstable_opts.remap_path_scope; - let mut prefer_remapped = false; - - if remap_path_scopes.contains(RemapPathScopeComponents::UNSPLIT_DEBUGINFO) { - prefer_remapped |= !has_split_debuginfo; - } - - if remap_path_scopes.contains(RemapPathScopeComponents::SPLIT_DEBUGINFO) { - prefer_remapped |= has_split_debuginfo; + pub fn filename_display_preference( + &self, + scope: RemapPathScopeComponents, + ) -> FileNameDisplayPreference { + assert!( + scope.bits().count_ones() == 1, + "one and only one scope should be passed to `Session::filename_display_preference`" + ); + if self.opts.unstable_opts.remap_path_scope.contains(scope) { + FileNameDisplayPreference::Remapped + } else { + FileNameDisplayPreference::Local } - - prefer_remapped - } - - pub fn should_prefer_remapped_for_split_debuginfo_paths(&self) -> bool { - let has_split_debuginfo = match self.split_debuginfo() { - SplitDebuginfo::Off => false, - SplitDebuginfo::Packed | SplitDebuginfo::Unpacked => true, - }; - - self.opts - .unstable_opts - .remap_path_scope - .contains(RemapPathScopeComponents::SPLIT_DEBUGINFO_PATH) - && has_split_debuginfo } } @@ -1007,8 +994,7 @@ pub fn build_session( registry: rustc_errors::registry::Registry, fluent_resources: Vec<&'static str>, driver_lint_caps: FxHashMap, - file_loader: Option>, - target_cfg: Target, + target: Target, sysroot: PathBuf, cfg_version: &'static str, ice_file: Option, @@ -1034,24 +1020,11 @@ pub fn build_session( early_dcx.early_warn(warning) } - let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); - let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| { - if target_cfg.is_like_msvc { - SourceFileHashAlgorithm::Sha256 - } else { - SourceFileHashAlgorithm::Md5 - } - }); - let source_map = Lrc::new(SourceMap::with_file_loader_and_hash_kind( - loader, - sopts.file_path_mapping(), - hash_kind, - )); - let fallback_bundle = fallback_fluent_bundle( fluent_resources, sopts.unstable_opts.translate_directionality_markers, ); + let source_map = rustc_span::source_map::get_source_map().unwrap(); let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle); let mut dcx = @@ -1117,11 +1090,10 @@ pub fn build_session( _ => CtfeBacktrace::Disabled, }); - let asm_arch = - if target_cfg.allow_asm { InlineAsmArch::from_str(&target_cfg.arch).ok() } else { None }; + let asm_arch = if target.allow_asm { InlineAsmArch::from_str(&target.arch).ok() } else { None }; let sess = Session { - target: target_cfg, + target, host, opts: sopts, host_tlib_path, @@ -1235,6 +1207,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::SanitizerCfiRequiresLto); } + // KCFI requires panic=abort + if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy() != PanicStrategy::Abort { + sess.dcx().emit_err(errors::SanitizerKcfiRequiresPanicAbort); + } + // LLVM CFI using rustc LTO requires a single codegen unit. if sess.is_sanitizer_cfi_enabled() && sess.lto() == config::Lto::Fat @@ -1426,16 +1403,10 @@ impl EarlyDiagCtxt { self.dcx.warn(msg) } - pub fn initialize_checked_jobserver(&self) { - // initialize jobserver before getting `jobserver::client` and `build_session`. - jobserver::initialize_checked(|err| { - #[allow(rustc::untranslatable_diagnostic)] - #[allow(rustc::diagnostic_outside_of_impl)] - self.dcx - .struct_warn(err) - .with_note("the build environment is likely misconfigured") - .emit() - }); + #[allow(rustc::untranslatable_diagnostic)] + #[allow(rustc::diagnostic_outside_of_impl)] + pub fn early_struct_warn(&self, msg: impl Into) -> Diag<'_, ()> { + self.dcx.struct_warn(msg) } } @@ -1470,12 +1441,8 @@ pub trait RemapFileNameExt { /// Returns a possibly remapped filename based on the passed scope and remap cli options. /// - /// One and only one scope should be passed to this method. For anything related to - /// "codegen" see the [`RemapFileNameExt::for_codegen`] method. + /// One and only one scope should be passed to this method, it will panic otherwise. fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_>; - - /// Return a possibly remapped filename, to be used in "codegen" related parts. - fn for_codegen(&self, sess: &Session) -> Self::Output<'_>; } impl RemapFileNameExt for rustc_span::FileName { @@ -1492,14 +1459,6 @@ impl RemapFileNameExt for rustc_span::FileName { self.prefer_local() } } - - fn for_codegen(&self, sess: &Session) -> Self::Output<'_> { - if sess.should_prefer_remapped_for_codegen() { - self.prefer_remapped_unconditionaly() - } else { - self.prefer_local() - } - } } impl RemapFileNameExt for rustc_span::RealFileName { @@ -1516,12 +1475,4 @@ impl RemapFileNameExt for rustc_span::RealFileName { self.local_path_if_available() } } - - fn for_codegen(&self, sess: &Session) -> Self::Output<'_> { - if sess.should_prefer_remapped_for_codegen() { - self.remapped_path_if_available() - } else { - self.local_path_if_available() - } - } } diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 50ebbdccf6722..f70a53eeb4108 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,6 +1,7 @@ use crate::session::Session; use rustc_data_structures::profiling::VerboseTimingGuard; use rustc_fs_util::try_canonicalize; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use std::{ path::{Path, PathBuf}, sync::OnceLock, @@ -127,7 +128,7 @@ pub fn extra_compiler_flags() -> Option<(Vec, bool)> { const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"]; - let mut args = std::env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable(); + let mut args = std::env::args_os().map(|arg| arg.to_string_lossy().to_string()); let mut result = Vec::new(); let mut excluded_cargo_defaults = false; diff --git a/compiler/rustc_session/src/version.rs b/compiler/rustc_session/src/version.rs index c0c088bcef7dc..39e4541349ecd 100644 --- a/compiler/rustc_session/src/version.rs +++ b/compiler/rustc_session/src/version.rs @@ -1,3 +1,4 @@ +use rustc_macros::{current_rustc_version, Decodable, Encodable, HashStable_Generic}; use std::fmt::{self, Display}; #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 2cc9bc31873a4..0893bc31bfacd 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -10,12 +10,12 @@ use rustc_span::Symbol; use stable_mir::abi::Layout; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::mir::{Mutability, Safety}; +use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety}; use stable_mir::ty::{ Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, - GenericArgKind, GenericArgs, IndexedVal, IntTy, Movability, Region, RigidTy, Span, TermKind, - TraitRef, Ty, UintTy, VariantDef, VariantIdx, + GenericArgKind, GenericArgs, IndexedVal, IntTy, Movability, Pattern, Region, RigidTy, Span, + TermKind, TraitRef, Ty, UintTy, VariantDef, VariantIdx, }; use stable_mir::{CrateItem, CrateNum, DefId}; @@ -76,6 +76,19 @@ impl RustcInternal for Ty { } } +impl RustcInternal for Pattern { + type T<'tcx> = rustc_ty::Pattern<'tcx>; + fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + tcx.mk_pat(match self { + Pattern::Range { start, end, include_end } => rustc_ty::PatternKind::Range { + start: start.as_ref().map(|c| ty_const(c, tables, tcx)), + end: end.as_ref().map(|c| ty_const(c, tables, tcx)), + include_end: *include_end, + }, + }) + } +} + impl RustcInternal for RigidTy { type T<'tcx> = rustc_ty::TyKind<'tcx>; @@ -90,15 +103,17 @@ impl RustcInternal for RigidTy { RigidTy::Array(ty, cnst) => { rustc_ty::TyKind::Array(ty.internal(tables, tcx), ty_const(cnst, tables, tcx)) } + RigidTy::Pat(ty, pat) => { + rustc_ty::TyKind::Pat(ty.internal(tables, tcx), pat.internal(tables, tcx)) + } RigidTy::Adt(def, args) => { rustc_ty::TyKind::Adt(def.internal(tables, tcx), args.internal(tables, tcx)) } RigidTy::Str => rustc_ty::TyKind::Str, RigidTy::Slice(ty) => rustc_ty::TyKind::Slice(ty.internal(tables, tcx)), - RigidTy::RawPtr(ty, mutability) => rustc_ty::TyKind::RawPtr(rustc_ty::TypeAndMut { - ty: ty.internal(tables, tcx), - mutbl: mutability.internal(tables, tcx), - }), + RigidTy::RawPtr(ty, mutability) => { + rustc_ty::TyKind::RawPtr(ty.internal(tables, tcx), mutability.internal(tables, tcx)) + } RigidTy::Ref(region, ty, mutability) => rustc_ty::TyKind::Ref( region.internal(tables, tcx), ty.internal(tables, tcx), @@ -492,6 +507,82 @@ impl RustcInternal for Layout { } } +impl RustcInternal for Place { + type T<'tcx> = rustc_middle::mir::Place<'tcx>; + + fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + rustc_middle::mir::Place { + local: rustc_middle::mir::Local::from_usize(self.local), + projection: tcx.mk_place_elems(&self.projection.internal(tables, tcx)), + } + } +} + +impl RustcInternal for ProjectionElem { + type T<'tcx> = rustc_middle::mir::PlaceElem<'tcx>; + + fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + ProjectionElem::Deref => rustc_middle::mir::PlaceElem::Deref, + ProjectionElem::Field(idx, ty) => { + rustc_middle::mir::PlaceElem::Field((*idx).into(), ty.internal(tables, tcx)) + } + ProjectionElem::Index(idx) => rustc_middle::mir::PlaceElem::Index((*idx).into()), + ProjectionElem::ConstantIndex { offset, min_length, from_end } => { + rustc_middle::mir::PlaceElem::ConstantIndex { + offset: *offset, + min_length: *min_length, + from_end: *from_end, + } + } + ProjectionElem::Subslice { from, to, from_end } => { + rustc_middle::mir::PlaceElem::Subslice { from: *from, to: *to, from_end: *from_end } + } + ProjectionElem::Downcast(idx) => { + rustc_middle::mir::PlaceElem::Downcast(None, idx.internal(tables, tcx)) + } + ProjectionElem::OpaqueCast(ty) => { + rustc_middle::mir::PlaceElem::OpaqueCast(ty.internal(tables, tcx)) + } + ProjectionElem::Subtype(ty) => { + rustc_middle::mir::PlaceElem::Subtype(ty.internal(tables, tcx)) + } + } + } +} + +impl RustcInternal for BinOp { + type T<'tcx> = rustc_middle::mir::BinOp; + + fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + BinOp::Add => rustc_middle::mir::BinOp::Add, + BinOp::AddUnchecked => rustc_middle::mir::BinOp::AddUnchecked, + BinOp::Sub => rustc_middle::mir::BinOp::Sub, + BinOp::SubUnchecked => rustc_middle::mir::BinOp::SubUnchecked, + BinOp::Mul => rustc_middle::mir::BinOp::Mul, + BinOp::MulUnchecked => rustc_middle::mir::BinOp::MulUnchecked, + BinOp::Div => rustc_middle::mir::BinOp::Div, + BinOp::Rem => rustc_middle::mir::BinOp::Rem, + BinOp::BitXor => rustc_middle::mir::BinOp::BitXor, + BinOp::BitAnd => rustc_middle::mir::BinOp::BitAnd, + BinOp::BitOr => rustc_middle::mir::BinOp::BitOr, + BinOp::Shl => rustc_middle::mir::BinOp::Shl, + BinOp::ShlUnchecked => rustc_middle::mir::BinOp::ShlUnchecked, + BinOp::Shr => rustc_middle::mir::BinOp::Shr, + BinOp::ShrUnchecked => rustc_middle::mir::BinOp::ShrUnchecked, + BinOp::Eq => rustc_middle::mir::BinOp::Eq, + BinOp::Lt => rustc_middle::mir::BinOp::Lt, + BinOp::Le => rustc_middle::mir::BinOp::Le, + BinOp::Ne => rustc_middle::mir::BinOp::Ne, + BinOp::Ge => rustc_middle::mir::BinOp::Ge, + BinOp::Gt => rustc_middle::mir::BinOp::Gt, + BinOp::Cmp => rustc_middle::mir::BinOp::Cmp, + BinOp::Offset => rustc_middle::mir::BinOp::Offset, + } + } +} + impl RustcInternal for &T where T: RustcInternal, diff --git a/compiler/rustc_smir/src/rustc_internal/pretty.rs b/compiler/rustc_smir/src/rustc_internal/pretty.rs index 3ef2d28ea4734..c0dce08b0d33a 100644 --- a/compiler/rustc_smir/src/rustc_internal/pretty.rs +++ b/compiler/rustc_smir/src/rustc_internal/pretty.rs @@ -14,7 +14,7 @@ pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io )?; let _ = run(tcx, || { let items = stable_mir::all_local_items(); - let _ = items.iter().map(|item| -> io::Result<()> { item.dump(w) }).collect::>(); + let _ = items.iter().map(|item| -> io::Result<()> { item.emit_mir(w) }).collect::>(); }); Ok(()) } diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index a13262cdcc494..221224eed0170 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -4,9 +4,10 @@ //! monomorphic body using internal representation. //! After that, we convert the internal representation into a stable one. use crate::rustc_smir::{Stable, Tables}; +use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::visit::MutVisitor; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; /// Builds a monomorphic body for a given instance. pub struct BodyBuilder<'tcx> { @@ -16,47 +17,44 @@ pub struct BodyBuilder<'tcx> { impl<'tcx> BodyBuilder<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>) -> Self { + let instance = match instance.def { + // To get the fallback body of an intrinsic, we need to convert it to an item. + ty::InstanceDef::Intrinsic(def_id) => ty::Instance::new(def_id, instance.args), + _ => instance, + }; BodyBuilder { tcx, instance } } /// Build a stable monomorphic body for a given instance based on the MIR body. /// - /// Note that we skip instantiation for static and constants. Trying to do so can cause ICE. - /// - /// We do monomorphize non-generic functions to eval unevaluated constants. + /// All constants are also evaluated. pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body { - let mut body = self.tcx.instance_mir(self.instance.def).clone(); - if self.tcx.def_kind(self.instance.def_id()).is_fn_like() || !self.instance.args.is_empty() + let body = tables.tcx.instance_mir(self.instance.def).clone(); + let mono_body = if !self.instance.args.is_empty() + // Without the `generic_const_exprs` feature gate, anon consts in signatures do not + // get generic parameters. Which is wrong, but also not a problem without + // generic_const_exprs + || self.tcx.def_kind(self.instance.def_id()) != DefKind::AnonConst { - self.visit_body(&mut body); - } - body.stable(tables) - } - - fn monomorphize(&self, value: T) -> T - where - T: ty::TypeFoldable>, - { - self.instance.instantiate_mir_and_normalize_erasing_regions( - self.tcx, - ty::ParamEnv::reveal_all(), - ty::EarlyBinder::bind(value), - ) + let mut mono_body = self.instance.instantiate_mir_and_normalize_erasing_regions( + tables.tcx, + ty::ParamEnv::reveal_all(), + ty::EarlyBinder::bind(body), + ); + self.visit_body(&mut mono_body); + mono_body + } else { + // Already monomorphic. + body + }; + mono_body.stable(tables) } } impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { - fn visit_ty_const(&mut self, ct: &mut ty::Const<'tcx>, _location: mir::Location) { - *ct = self.monomorphize(*ct); - } - - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: mir::visit::TyContext) { - *ty = self.monomorphize(*ty); - } - fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) { - let const_ = self.monomorphize(constant.const_); - let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), Some(constant.span)) { + let const_ = constant.const_; + let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), constant.span) { Ok(v) => v, Err(mir::interpret::ErrorHandled::Reported(..)) => return, Err(mir::interpret::ErrorHandled::TooGeneric(..)) => { @@ -68,10 +66,6 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { self.super_constant(constant, location); } - fn visit_args(&mut self, args: &mut GenericArgsRef<'tcx>, _: mir::Location) { - *args = self.monomorphize(*args); - } - fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 509f0def2568e..fd31c020f899a 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -19,7 +19,7 @@ use stable_mir::abi::{FnAbi, Layout, LayoutShape}; use stable_mir::compiler_interface::Context; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; -use stable_mir::mir::Body; +use stable_mir::mir::{BinOp, Body, Place}; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -420,10 +420,13 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; let args = args.internal(&mut *tables, tcx); let def_ty = tables.tcx.type_of(item.internal(&mut *tables, tcx)); - def_ty.instantiate(tables.tcx, args).stable(&mut *tables) + tables + .tcx + .instantiate_and_normalize_erasing_regions(args, ty::ParamEnv::reveal_all(), def_ty) + .stable(&mut *tables) } - fn const_literal(&self, cnst: &stable_mir::ty::Const) -> String { + fn const_pretty(&self, cnst: &stable_mir::ty::Const) -> String { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; cnst.internal(&mut *tables, tcx).to_string() @@ -434,6 +437,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { tables.tcx.def_span(tables[def_id]).stable(&mut *tables) } + fn ty_pretty(&self, ty: stable_mir::ty::Ty) -> String { + let tables = self.0.borrow_mut(); + tables.types[ty].to_string() + } + fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind { let mut tables = self.0.borrow_mut(); tables.types[ty].kind().stable(&mut *tables) @@ -492,6 +500,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { matches!(instance.def, ty::InstanceDef::DropGlue(_, None)) } + fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { + let tables = self.0.borrow_mut(); + let instance = tables.instances[def]; + matches!(instance.def, ty::InstanceDef::AsyncDropGlueCtorShim(_, None)) + } + fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; @@ -566,7 +580,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let result = tcx.const_eval_instance( ParamEnv::reveal_all(), instance, - Some(tcx.def_span(instance.def_id())), + tcx.def_span(instance.def_id()), ); result .map(|const_val| { @@ -654,6 +668,21 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; id.internal(&mut *tables, tcx).0.stable(&mut *tables) } + + fn place_pretty(&self, place: &Place) -> String { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + format!("{:?}", place.internal(&mut *tables, tcx)) + } + + fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let rhs_internal = rhs.internal(&mut *tables, tcx); + let lhs_internal = lhs.internal(&mut *tables, tcx); + let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal); + ty.stable(&mut *tables) + } } pub struct TablesWrapper<'tcx>(pub RefCell>); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index c0876adf90509..452ab04c44c54 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -229,7 +229,7 @@ impl<'tcx> Stable<'tcx> for mir::BorrowKind { use rustc_middle::mir::BorrowKind::*; match *self { Shared => stable_mir::mir::BorrowKind::Shared, - Fake => stable_mir::mir::BorrowKind::Fake, + Fake(kind) => stable_mir::mir::BorrowKind::Fake(kind.stable(tables)), Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) }, } } @@ -247,23 +247,28 @@ impl<'tcx> Stable<'tcx> for mir::MutBorrowKind { } } +impl<'tcx> Stable<'tcx> for mir::FakeBorrowKind { + type T = stable_mir::mir::FakeBorrowKind; + fn stable(&self, _: &mut Tables<'_>) -> Self::T { + use rustc_middle::mir::FakeBorrowKind::*; + match *self { + Deep => stable_mir::mir::FakeBorrowKind::Deep, + Shallow => stable_mir::mir::FakeBorrowKind::Shallow, + } + } +} + impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { type T = stable_mir::mir::NullOp; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { use rustc_middle::mir::NullOp::*; - use rustc_middle::mir::UbKind; match self { SizeOf => stable_mir::mir::NullOp::SizeOf, AlignOf => stable_mir::mir::NullOp::AlignOf, OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( indices.iter().map(|idx| idx.stable(tables)).collect(), ), - UbCheck(UbKind::LanguageUb) => { - stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb) - } - UbCheck(UbKind::LibraryUb) => { - stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb) - } + UbChecks => stable_mir::mir::NullOp::UbChecks, } } } @@ -273,8 +278,8 @@ impl<'tcx> Stable<'tcx> for mir::CastKind { fn stable(&self, tables: &mut Tables<'_>) -> Self::T { use rustc_middle::mir::CastKind::*; match self { - PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress, - PointerFromExposedAddress => stable_mir::mir::CastKind::PointerFromExposedAddress, + PointerExposeProvenance => stable_mir::mir::CastKind::PointerExposeAddress, + PointerWithExposedProvenance => stable_mir::mir::CastKind::PointerWithExposedProvenance, PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)), DynStar => stable_mir::mir::CastKind::DynStar, IntToInt => stable_mir::mir::CastKind::IntToInt, @@ -499,6 +504,7 @@ impl<'tcx> Stable<'tcx> for mir::BinOp { BinOp::Ne => stable_mir::mir::BinOp::Ne, BinOp::Ge => stable_mir::mir::BinOp::Ge, BinOp::Gt => stable_mir::mir::BinOp::Gt, + BinOp::Cmp => stable_mir::mir::BinOp::Cmp, BinOp::Offset => stable_mir::mir::BinOp::Offset, } } @@ -548,6 +554,9 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { mir::AggregateKind::CoroutineClosure(..) => { todo!("FIXME(async_closures): Lower these to SMIR") } + mir::AggregateKind::RawPtr(ty, mutability) => { + stable_mir::mir::AggregateKind::RawPtr(ty.stable(tables), mutability.stable(tables)) + } } } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 84c6cbf178bcb..4abf991fba25d 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -330,8 +330,11 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { ty::Array(ty, constant) => { TyKind::RigidTy(RigidTy::Array(ty.stable(tables), constant.stable(tables))) } + ty::Pat(ty, pat) => { + TyKind::RigidTy(RigidTy::Pat(ty.stable(tables), pat.stable(tables))) + } ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables))), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + ty::RawPtr(ty, mutbl) => { TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables), mutbl.stable(tables))) } ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref( @@ -385,6 +388,20 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { } } +impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { + type T = stable_mir::ty::Pattern; + + fn stable(&self, tables: &mut Tables<'_>) -> Self::T { + match **self { + ty::PatternKind::Range { start, end, include_end } => stable_mir::ty::Pattern::Range { + start: start.stable(tables), + end: end.stable(tables), + include_end, + }, + } + } +} + impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { type T = stable_mir::ty::Const; @@ -719,6 +736,18 @@ impl<'tcx> Stable<'tcx> for ty::ImplPolarity { } } +impl<'tcx> Stable<'tcx> for ty::PredicatePolarity { + type T = stable_mir::ty::PredicatePolarity; + + fn stable(&self, _: &mut Tables<'_>) -> Self::T { + use rustc_middle::ty::PredicatePolarity::*; + match self { + Positive => stable_mir::ty::PredicatePolarity::Positive, + Negative => stable_mir::ty::PredicatePolarity::Negative, + } + } +} + impl<'tcx> Stable<'tcx> for ty::Region<'tcx> { type T = stable_mir::ty::Region; @@ -778,7 +807,10 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { | ty::InstanceDef::ThreadLocalShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::CloneShim(..) - | ty::InstanceDef::FnPtrShim(..) => stable_mir::mir::mono::InstanceKind::Shim, + | ty::InstanceDef::FnPtrShim(..) + | ty::InstanceDef::AsyncDropGlueCtorShim(..) => { + stable_mir::mir::mono::InstanceKind::Shim + } }; stable_mir::mir::mono::Instance { def, kind } } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 0c811d7dff1d7..1ac3a817bba56 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -6,7 +6,7 @@ use rustc_data_structures::stable_hasher::{ use rustc_data_structures::unhash::Unhasher; use rustc_data_structures::AtomicRef; use rustc_index::Idx; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Encodable}; use std::fmt; use std::hash::{BuildHasherDefault, Hash, Hasher}; @@ -22,7 +22,7 @@ rustc_index::newtype_index! { /// Item definitions in the currently-compiled crate would have the `CrateNum` /// `LOCAL_CRATE` in their `DefId`. -pub const LOCAL_CRATE: CrateNum = CrateNum::from_u32(0); +pub const LOCAL_CRATE: CrateNum = CrateNum::ZERO; impl CrateNum { #[inline] @@ -218,8 +218,6 @@ rustc_index::newtype_index! { /// /// You can create a `DefId` from a `LocalDefId` using `local_def_id.to_def_id()`. #[derive(Clone, PartialEq, Eq, Copy)] -// Don't derive order on 64-bit big-endian, so we can be consistent regardless of field order. -#[cfg_attr(not(all(target_pointer_width = "64", target_endian = "big")), derive(PartialOrd, Ord))] // On below-64 bit systems we can simply use the derived `Hash` impl #[cfg_attr(not(target_pointer_width = "64"), derive(Hash))] #[repr(C)] @@ -236,6 +234,12 @@ pub struct DefId { pub index: DefIndex, } +// To ensure correctness of incremental compilation, +// `DefId` must not implement `Ord` or `PartialOrd`. +// See https://github.com/rust-lang/rust/issues/90317. +impl !Ord for DefId {} +impl !PartialOrd for DefId {} + // On 64-bit systems, we can hash the whole `DefId` as one `u64` instead of two `u32`s. This // improves performance without impairing `FxHash` quality. So the below code gets compiled to a // noop on little endian systems because the memory layout of `DefId` is as follows: @@ -261,22 +265,6 @@ impl Hash for DefId { } } -// Implement the same comparison as derived with the other field order. -#[cfg(all(target_pointer_width = "64", target_endian = "big"))] -impl Ord for DefId { - #[inline] - fn cmp(&self, other: &DefId) -> std::cmp::Ordering { - Ord::cmp(&(self.index, self.krate), &(other.index, other.krate)) - } -} -#[cfg(all(target_pointer_width = "64", target_endian = "big"))] -impl PartialOrd for DefId { - #[inline] - fn partial_cmp(&self, other: &DefId) -> Option { - Some(self.cmp(other)) - } -} - impl DefId { /// Makes a local `DefId` from the given `DefIndex`. #[inline] diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index 78ac61fa31db9..fe09daf522c1c 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; /// The edition of the compiler. (See [RFC 2052](https://github.com/rust-lang/rfcs/blob/master/text/2052-epochs.md).) #[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, Encodable, Decodable, Eq)] diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 527938daae410..aa4bcefab9391 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -34,7 +34,7 @@ use rustc_data_structures::stable_hasher::{Hash64, HashStable, HashingControls, use rustc_data_structures::sync::{Lock, Lrc, WorkerLocal}; use rustc_data_structures::unhash::UnhashMap; use rustc_index::IndexVec; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::cell::RefCell; use std::collections::hash_map::Entry; @@ -165,7 +165,7 @@ pub enum Transparency { impl LocalExpnId { /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. - pub const ROOT: LocalExpnId = LocalExpnId::from_u32(0); + pub const ROOT: LocalExpnId = LocalExpnId::ZERO; #[inline] fn from_raw(idx: ExpnIndex) -> LocalExpnId { @@ -242,7 +242,7 @@ impl ExpnId { /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. /// Invariant: we do not create any ExpnId with local_id == 0 and krate != 0. pub const fn root() -> ExpnId { - ExpnId { krate: LOCAL_CRATE, local_id: ExpnIndex::from_u32(0) } + ExpnId { krate: LOCAL_CRATE, local_id: ExpnIndex::ZERO } } #[inline] @@ -459,28 +459,21 @@ impl HygieneData { span } - // We need to walk up and update return span if we meet macro instantiation to be collapsed - fn walk_chain_collapsed( - &self, - mut span: Span, - to: Span, - collapse_debuginfo_feature_enabled: bool, - ) -> Span { + fn walk_chain_collapsed(&self, mut span: Span, to: Span) -> Span { let orig_span = span; let mut ret_span = span; - - debug!( - "walk_chain_collapsed({:?}, {:?}), feature_enable={}", - span, to, collapse_debuginfo_feature_enabled, - ); + debug!("walk_chain_collapsed({:?}, {:?})", span, to); debug!("walk_chain_collapsed: span ctxt = {:?}", span.ctxt()); - while !span.eq_ctxt(to) && span.from_expansion() { - let outer_expn = self.outer_expn(span.ctxt()); + while let ctxt = span.ctxt() + && !ctxt.is_root() + && ctxt != to.ctxt() + { + let outer_expn = self.outer_expn(ctxt); debug!("walk_chain_collapsed({:?}): outer_expn={:?}", span, outer_expn); let expn_data = self.expn_data(outer_expn); debug!("walk_chain_collapsed({:?}): expn_data={:?}", span, expn_data); span = expn_data.call_site; - if !collapse_debuginfo_feature_enabled || expn_data.collapse_debuginfo { + if expn_data.collapse_debuginfo { ret_span = span; } } @@ -604,14 +597,13 @@ pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { HygieneData::with(|data| data.walk_chain(span, to)) } -pub fn walk_chain_collapsed( - span: Span, - to: Span, - collapse_debuginfo_feature_enabled: bool, -) -> Span { - HygieneData::with(|hdata| { - hdata.walk_chain_collapsed(span, to, collapse_debuginfo_feature_enabled) - }) +/// In order to have good line stepping behavior in debugger, for the given span we return its +/// outermost macro call site that still has a `#[collapse_debuginfo(yes)]` property on it. +/// We also stop walking call sites at the function body level because no line stepping can occur +/// at the level above that. +/// The returned span can then be used in emitted debuginfo. +pub fn walk_chain_collapsed(span: Span, to: Span) -> Span { + HygieneData::with(|data| data.walk_chain_collapsed(span, to)) } pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { @@ -1062,7 +1054,7 @@ pub enum ExpnKind { Macro(MacroKind, Symbol), /// Transform done by the compiler on the AST. AstPass(AstPass), - /// Desugaring done by the compiler during HIR lowering. + /// Desugaring done by the compiler during AST lowering. Desugaring(DesugaringKind), } @@ -1251,7 +1243,7 @@ struct HygieneDecodeContextInner { // global `HygieneData`. When we deserialize a `SyntaxContext`, we need to create // a new id in the global `HygieneData`. This map tracks the ID we end up picking, // so that multiple occurrences of the same serialized id are decoded to the same - // `SyntaxContext`. This only stores `SyntaxContext`s which are completly decoded. + // `SyntaxContext`. This only stores `SyntaxContext`s which are completely decoded. remapped_ctxts: Vec>, /// Maps serialized `SyntaxContext` ids that are currently being decoded to a `SyntaxContext`. diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 616a7ccc7c64f..f83bacdcebe77 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -35,21 +35,18 @@ extern crate self as rustc_span; -#[macro_use] -extern crate rustc_macros; - #[macro_use] extern crate tracing; use rustc_data_structures::{outline, AtomicRef}; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; mod caching_source_map_view; pub mod source_map; pub use self::caching_source_map_view::CachingSourceMapView; -use source_map::SourceMap; +use source_map::{SourceMap, SourceMapInputs}; pub mod edition; use edition::Edition; @@ -104,35 +101,35 @@ pub struct SessionGlobals { metavar_spans: Lock>, hygiene_data: Lock, - /// A reference to the source map in the `Session`. It's an `Option` - /// because it can't be initialized until `Session` is created, which - /// happens after `SessionGlobals`. `set_source_map` does the - /// initialization. - /// - /// This field should only be used in places where the `Session` is truly - /// not available, such as `::fmt`. - source_map: Lock>>, + /// The session's source map, if there is one. This field should only be + /// used in places where the `Session` is truly not available, such as + /// `::fmt`. + source_map: Option>, } impl SessionGlobals { - pub fn new(edition: Edition) -> SessionGlobals { + pub fn new(edition: Edition, sm_inputs: Option) -> SessionGlobals { SessionGlobals { symbol_interner: symbol::Interner::fresh(), span_interner: Lock::new(span_encoding::SpanInterner::default()), metavar_spans: Default::default(), hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), - source_map: Lock::new(None), + source_map: sm_inputs.map(|inputs| Lrc::new(SourceMap::with_inputs(inputs))), } } } -pub fn create_session_globals_then(edition: Edition, f: impl FnOnce() -> R) -> R { +pub fn create_session_globals_then( + edition: Edition, + sm_inputs: Option, + f: impl FnOnce() -> R, +) -> R { assert!( !SESSION_GLOBALS.is_set(), "SESSION_GLOBALS should never be overwritten! \ Use another thread if you need another SessionGlobals" ); - let session_globals = SessionGlobals::new(edition); + let session_globals = SessionGlobals::new(edition, sm_inputs); SESSION_GLOBALS.set(&session_globals, f) } @@ -145,12 +142,13 @@ pub fn set_session_globals_then(session_globals: &SessionGlobals, f: impl FnO SESSION_GLOBALS.set(session_globals, f) } +/// No source map. pub fn create_session_if_not_set_then(edition: Edition, f: F) -> R where F: FnOnce(&SessionGlobals) -> R, { if !SESSION_GLOBALS.is_set() { - let session_globals = SessionGlobals::new(edition); + let session_globals = SessionGlobals::new(edition, None); SESSION_GLOBALS.set(&session_globals, || SESSION_GLOBALS.with(f)) } else { SESSION_GLOBALS.with(f) @@ -164,8 +162,9 @@ where SESSION_GLOBALS.with(f) } +/// Default edition, no source map. pub fn create_default_session_globals_then(f: impl FnOnce() -> R) -> R { - create_session_globals_then(edition::DEFAULT_EDITION, f) + create_session_globals_then(edition::DEFAULT_EDITION, None, f) } // If this ever becomes non thread-local, `decode_syntax_context` @@ -271,6 +270,18 @@ impl RealFileName { } } + /// Return the path remmapped or not depending on the [`FileNameDisplayPreference`]. + /// + /// For the purpose of this function, local and short preference are equal. + pub fn to_path(&self, display_pref: FileNameDisplayPreference) -> &Path { + match display_pref { + FileNameDisplayPreference::Local | FileNameDisplayPreference::Short => { + self.local_path_if_available() + } + FileNameDisplayPreference::Remapped => self.remapped_path_if_available(), + } + } + pub fn to_string_lossy(&self, display_pref: FileNameDisplayPreference) -> Cow<'_, str> { match display_pref { FileNameDisplayPreference::Local => self.local_path_if_available().to_string_lossy(), @@ -427,6 +438,17 @@ impl FileName { src.hash(&mut hasher); FileName::InlineAsm(hasher.finish()) } + + /// Returns the path suitable for reading from the file system on the local host, + /// if this information exists. + /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + pub fn into_local_path(self) -> Option { + match self { + FileName::Real(path) => path.into_local_path(), + FileName::DocTest(path, _) => Some(path), + _ => None, + } + } } /// Represents a span. @@ -720,6 +742,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> { + /// 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() @@ -1256,25 +1317,6 @@ impl Decodable for AttrId { } } -/// Insert `source_map` into the session globals for the duration of the -/// closure's execution. -pub fn set_source_map T>(source_map: Lrc, f: F) -> T { - with_session_globals(|session_globals| { - *session_globals.source_map.borrow_mut() = Some(source_map); - }); - struct ClearSourceMap; - impl Drop for ClearSourceMap { - fn drop(&mut self) { - with_session_globals(|session_globals| { - session_globals.source_map.borrow_mut().take(); - }); - } - } - - let _guard = ClearSourceMap; - f() -} - impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Use the global `SourceMap` to print the span. If that's not @@ -1290,7 +1332,7 @@ impl fmt::Debug for Span { if SESSION_GLOBALS.is_set() { with_session_globals(|session_globals| { - if let Some(source_map) = &*session_globals.source_map.borrow() { + if let Some(source_map) = &session_globals.source_map { write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(*self), self.ctxt()) } else { fallback(*self, f) diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index df7635e447da8..2093dcf0e8315 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -12,9 +12,10 @@ use crate::*; use rustc_data_structures::sync::{IntoDynSyncSend, MappedReadGuard, ReadGuard, RwLock}; use rustc_data_structures::unhash::UnhashMap; +use rustc_macros::{Decodable, Encodable}; use std::fs; use std::io::{self, BorrowedBuf, Read}; -use std::path::{self}; +use std::path; #[cfg(test)] mod tests; @@ -167,9 +168,17 @@ struct SourceMapFiles { stable_id_to_source_file: UnhashMap>, } +/// Used to construct a `SourceMap` with `SourceMap::with_inputs`. +pub struct SourceMapInputs { + pub file_loader: Box, + pub path_mapping: FilePathMapping, + pub hash_kind: SourceFileHashAlgorithm, +} + pub struct SourceMap { files: RwLock, file_loader: IntoDynSyncSend>, + // This is used to apply the file path remapping as specified via // `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`. path_mapping: FilePathMapping, @@ -180,17 +189,15 @@ pub struct SourceMap { impl SourceMap { pub fn new(path_mapping: FilePathMapping) -> SourceMap { - Self::with_file_loader_and_hash_kind( - Box::new(RealFileLoader), + Self::with_inputs(SourceMapInputs { + file_loader: Box::new(RealFileLoader), path_mapping, - SourceFileHashAlgorithm::Md5, - ) + hash_kind: SourceFileHashAlgorithm::Md5, + }) } - pub fn with_file_loader_and_hash_kind( - file_loader: Box, - path_mapping: FilePathMapping, - hash_kind: SourceFileHashAlgorithm, + pub fn with_inputs( + SourceMapInputs { file_loader, path_mapping, hash_kind }: SourceMapInputs, ) -> SourceMap { SourceMap { files: Default::default(), @@ -218,7 +225,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> { + 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 +234,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 @@ -654,6 +669,12 @@ impl SourceMap { }) } + /// Extends the span to include any trailing whitespace, or returns the original + /// span if a `SpanSnippetError` was encountered. + pub fn span_extend_while_whitespace(&self, span: Span) -> Span { + self.span_extend_while(span, char::is_whitespace).unwrap_or(span) + } + /// Extends the given `Span` to previous character while the previous character matches the predicate pub fn span_extend_prev_while( &self, @@ -1034,15 +1055,16 @@ impl SourceMap { /// // ^^^^^^ input /// ``` pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option { - let span = self.span_extend_while(mac_call, char::is_whitespace).ok()?; - let span = span.shrink_to_hi().with_hi(BytePos(span.hi().0.checked_add(1)?)); - if self.span_to_snippet(span).as_deref() != Ok(";") { - return None; - } - Some(span) + let span = self.span_extend_while_whitespace(mac_call); + let span = self.next_point(span); + if self.span_to_snippet(span).as_deref() == Ok(";") { Some(span) } else { None } } } +pub fn get_source_map() -> Option> { + with_session_globals(|session_globals| session_globals.source_map.clone()) +} + #[derive(Clone)] pub struct FilePathMapping { mapping: Vec<(PathBuf, PathBuf)>, @@ -1129,6 +1151,21 @@ impl FilePathMapping { } } + /// Applies any path prefix substitution as defined by the mapping. + /// The return value is the local path with a "virtual path" representing the remapped + /// part if any remapping was performed. + pub fn to_real_filename<'a>(&self, local_path: impl Into>) -> RealFileName { + let local_path = local_path.into(); + if let (remapped_path, true) = self.map_prefix(&*local_path) { + RealFileName::Remapped { + virtual_name: remapped_path.into_owned(), + local_path: Some(local_path.into_owned()), + } + } else { + RealFileName::LocalPath(local_path.into_owned()) + } + } + /// Expand a relative path to an absolute path with remapping taken into account. /// Use this when absolute paths are required (e.g. debuginfo or crate metadata). /// diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 81a9e4706883f..dcb02da371921 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -243,7 +243,7 @@ fn t10() { src_hash, stable_id, source_len.to_u32(), - CrateNum::new(0), + CrateNum::ZERO, FreezeLock::new(lines.read().clone()), multibyte_chars, non_narrow_chars, diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index e162695a13beb..788a52faf5688 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_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e43c9533382e1..7fe94c2e82ba6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -8,7 +8,7 @@ use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; use rustc_data_structures::sync::Lock; -use rustc_macros::HashStable_Generic; +use rustc_macros::{symbols, Decodable, Encodable, HashStable_Generic}; use std::fmt; use std::hash::{Hash, Hasher}; @@ -166,9 +166,8 @@ symbols! { Break, C, CStr, - CallFuture, - CallMutFuture, CallOnceFuture, + CallRefFuture, Capture, Center, Cleanup, @@ -193,6 +192,7 @@ symbols! { Duration, Encodable, Encoder, + Enumerate, Eq, Equal, Err, @@ -208,6 +208,7 @@ symbols! { FromResidual, FsOpenOptions, FsPermissions, + FusedIterator, Future, FutureOutput, GlobalAlloc, @@ -361,6 +362,7 @@ symbols! { adt_const_params, advanced_slice_patterns, adx_target_feature, + aggregate_raw_ptr, alias, align, align_offset, @@ -424,6 +426,16 @@ symbols! { async_call_mut, async_call_once, async_closure, + async_destruct, + async_drop, + async_drop_chain, + async_drop_defer, + async_drop_either, + async_drop_fuse, + async_drop_in_place, + async_drop_noop, + async_drop_slice, + async_drop_surface_drop_in_place, async_fn, async_fn_in_trait, async_fn_kind_helper, @@ -514,12 +526,11 @@ symbols! { cfg_target_has_atomic_equal_alignment, cfg_target_thread_local, cfg_target_vendor, + cfg_ub_checks, cfg_version, cfi, cfi_encoding, char, - check_language_ub, - check_library_ub, client, clippy, clobber_abi, @@ -533,8 +544,15 @@ symbols! { cmp, cmp_max, cmp_min, + cmp_ord_max, + cmp_ord_min, cmp_partialeq_eq, cmp_partialeq_ne, + cmp_partialord_cmp, + cmp_partialord_ge, + cmp_partialord_gt, + cmp_partialord_le, + cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, coerce_unsized, @@ -675,6 +693,8 @@ symbols! { deref_method, deref_mut, deref_mut_method, + deref_patterns, + deref_pure, deref_target, derive, derive_const, @@ -690,6 +710,7 @@ symbols! { dispatch_from_dyn, div, div_assign, + diverging_block_default, do_not_recommend, doc, doc_alias, @@ -733,6 +754,7 @@ symbols! { enable, encode, end, + enumerate_method, env, env_CFG_RELEASE: env!("CFG_RELEASE"), eprint_macro, @@ -815,9 +837,8 @@ symbols! { fadd_algebraic, fadd_fast, fake_variadic, - fallback_to_never, - fallback_to_niko, - fallback_to_unit, + fallback, + fallback_surface_drop, fdiv_algebraic, fdiv_fast, feature, @@ -886,6 +907,7 @@ symbols! { fsub_algebraic, fsub_fast, fundamental, + fused_iterator, future, future_trait, gdb_script_file, @@ -1001,6 +1023,7 @@ symbols! { io_stderr, io_stdout, irrefutable_let_patterns, + is, is_val_statically_known, isa_attribute, isize, @@ -1185,6 +1208,8 @@ symbols! { multiple_supertrait_upcastable, must_not_suspend, must_use, + mut_preserve_binding_mode_2024, + mut_ref, naked, naked_functions, name, @@ -1228,6 +1253,7 @@ symbols! { new_v1, new_v1_formatted, next, + niko, nll, no, no_builtins, @@ -1236,7 +1262,6 @@ symbols! { no_crate_inject, no_debug, no_default_passes, - no_fallback, no_implicit_prelude, no_inline, no_link, @@ -1297,6 +1322,24 @@ symbols! { panic_abort, panic_bounds_check, panic_cannot_unwind, + panic_const_add_overflow, + panic_const_async_fn_resumed, + panic_const_async_fn_resumed_panic, + panic_const_async_gen_fn_resumed, + panic_const_async_gen_fn_resumed_panic, + panic_const_coroutine_resumed, + panic_const_coroutine_resumed_panic, + panic_const_div_by_zero, + panic_const_div_overflow, + panic_const_gen_fn_none, + panic_const_gen_fn_none_panic, + panic_const_mul_overflow, + panic_const_neg_overflow, + panic_const_rem_by_zero, + panic_const_rem_overflow, + panic_const_shl_overflow, + panic_const_shr_overflow, + panic_const_sub_overflow, panic_fmt, panic_handler, panic_impl, @@ -1307,7 +1350,7 @@ symbols! { panic_misaligned_pointer_dereference, panic_nounwind, panic_runtime, - panic_str, + panic_str_2015, panic_unwind, panicking, param_attrs, @@ -1320,6 +1363,8 @@ symbols! { path, pattern_complexity, pattern_parentheses, + pattern_type, + pattern_types, phantom_data, pic, pie, @@ -1334,6 +1379,7 @@ symbols! { poll, poll_next, post_dash_lto: "post-lto", + postfix_match, powerpc_target_feature, powf128, powf16, @@ -1344,6 +1390,7 @@ symbols! { powif32, powif64, pre_dash_lto: "pre-lto", + precise_capturing, precise_pointer_size_matching, pref_align_of, prefetch_read_data, @@ -1429,6 +1476,8 @@ symbols! { receiver, recursion_limit, reexport_test_harness_main, + ref_pat_eat_one_layer_2024, + ref_pat_everywhere, ref_unwind_safe_trait, reference, reflect, @@ -1463,7 +1512,6 @@ symbols! { residual, result, resume, - retag_box_to_raw, return_position_impl_trait_in_trait, return_type_notation, rhs, @@ -1555,7 +1603,7 @@ symbols! { rustc_mir, rustc_must_implement_one_of, rustc_never_returns_null_ptr, - rustc_never_type_mode, + rustc_never_type_options, rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, @@ -1572,6 +1620,7 @@ symbols! { rustc_peek_maybe_init, rustc_peek_maybe_uninit, rustc_polymorphize_error, + rustc_preserve_ub_checks, rustc_private, rustc_proc_macro_decls, rustc_promotable, @@ -1632,7 +1681,7 @@ symbols! { simd_cttz, simd_div, simd_eq, - simd_expose_addr, + simd_expose_provenance, simd_extract, simd_fabs, simd_fcos, @@ -1648,7 +1697,6 @@ symbols! { simd_fmin, simd_fpow, simd_fpowi, - simd_from_exposed_addr, simd_fsin, simd_fsqrt, simd_gather, @@ -1687,6 +1735,7 @@ symbols! { simd_shuffle_generic, simd_sub, simd_trunc, + simd_with_exposed_provenance, simd_xor, since, sinf128, @@ -1753,6 +1802,7 @@ symbols! { sub_assign, sub_with_overflow, suggestion, + surface_async_drop_in_place, sym, sync, synthetic, @@ -1786,6 +1836,7 @@ symbols! { thread, thread_local, thread_local_macro, + three_way_compare, thumb2, thumb_mode: "thumb-mode", tmm_reg, @@ -1835,6 +1886,7 @@ symbols! { type_macros, type_name, type_privacy_lints, + typed_swap, u128, u128_legacy_const_max, u128_legacy_const_min, @@ -1865,6 +1917,7 @@ symbols! { u8_legacy_fn_max_value, u8_legacy_fn_min_value, u8_legacy_mod, + ub_checks, unaligned_volatile_load, unaligned_volatile_store, unboxed_closures, diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 0ce522c9cabca..65aa9e40c8ba3 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -bitflags = "2.4.1" punycode = "0.4.0" rustc-demangle = "0.1.21" rustc_data_structures = { path = "../rustc_data_structures" } @@ -16,5 +15,4 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } tracing = "0.1" -twox-hash = "1.6.3" # tidy-alphabetical-end diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 83e920e2f8ee4..0ed1f67bb8218 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -1,12 +1,13 @@ use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; use rustc_hir::def_id::CrateNum; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_middle::bug; use rustc_middle::ty::print::{PrettyPrinter, Print, PrintError, Printer}; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Instance, ReifyReason, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArg, GenericArgKind}; - use std::fmt::{self, Write}; use std::mem::{self, discriminant}; +use tracing::debug; pub(super) fn mangle<'tcx>( tcx: TyCtxt<'tcx>, @@ -55,7 +56,9 @@ pub(super) fn mangle<'tcx>( printer .print_def_path( def_id, - if let ty::InstanceDef::DropGlue(_, _) = instance.def { + if let ty::InstanceDef::DropGlue(_, _) | ty::InstanceDef::AsyncDropGlueCtorShim(_, _) = + instance.def + { // Add the name of the dropped type to the symbol name &*instance.args } else { @@ -71,21 +74,21 @@ pub(super) fn mangle<'tcx>( ty::InstanceDef::VTableShim(..) => { printer.write_str("{{vtable-shim}}").unwrap(); } - ty::InstanceDef::ReifyShim(..) => { - printer.write_str("{{reify-shim}}").unwrap(); + ty::InstanceDef::ReifyShim(_, reason) => { + printer.write_str("{{reify-shim").unwrap(); + match reason { + Some(ReifyReason::FnPtr) => printer.write_str("-fnptr").unwrap(), + Some(ReifyReason::Vtable) => printer.write_str("-vtable").unwrap(), + None => (), + } + printer.write_str("}}").unwrap(); } // FIXME(async_closures): This shouldn't be needed when we fix // `Instance::ty`/`Instance::def_id`. - ty::InstanceDef::ConstructCoroutineInClosureShim { target_kind, .. } - | ty::InstanceDef::CoroutineKindShim { target_kind, .. } => match target_kind { - ty::ClosureKind::Fn => unreachable!(), - ty::ClosureKind::FnMut => { - printer.write_str("{{fn-mut-shim}}").unwrap(); - } - ty::ClosureKind::FnOnce => { - printer.write_str("{{fn-once-shim}}").unwrap(); - } - }, + ty::InstanceDef::ConstructCoroutineInClosureShim { .. } + | ty::InstanceDef::CoroutineKindShim { .. } => { + printer.write_str("{{fn-once-shim}}").unwrap(); + } _ => {} } diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 02bb1fde75c1b..745ae41085b29 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -90,14 +90,9 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] +#![feature(let_chains)] #![allow(internal_features)] -#[macro_use] -extern crate rustc_middle; - -#[macro_use] -extern crate tracing; - use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -106,6 +101,7 @@ use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::SymbolManglingVersion; +use tracing::debug; mod hashed; mod legacy; @@ -113,7 +109,6 @@ mod v0; pub mod errors; pub mod test; -pub mod typeid; /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 6766080a54f51..f0fb87fe83c86 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -25,7 +25,7 @@ pub fn report_symbol_names(tcx: TyCtxt<'_>) { let mut symbol_names = SymbolNamesTest { tcx }; let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { symbol_names.process_attrs(id.owner_id.def_id); } diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs deleted file mode 100644 index e8763e49e624b..0000000000000 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ /dev/null @@ -1,66 +0,0 @@ -/// Type metadata identifiers for LLVM Control Flow Integrity (CFI) and cross-language LLVM CFI -/// support. -/// -/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, -/// see design document in the tracking issue #89653. -use bitflags::bitflags; -use rustc_middle::ty::{Instance, Ty, TyCtxt}; -use rustc_target::abi::call::FnAbi; -use std::hash::Hasher; -use twox_hash::XxHash64; - -bitflags! { - /// Options for typeid_for_fnabi. - #[derive(Clone, Copy, Debug)] - pub struct TypeIdOptions: u32 { - const GENERALIZE_POINTERS = 1; - const GENERALIZE_REPR_C = 2; - const NORMALIZE_INTEGERS = 4; - } -} - -mod typeid_itanium_cxx_abi; - -/// Returns a type metadata identifier for the specified FnAbi. -pub fn typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> String { - typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) -} - -/// Returns a type metadata identifier for the specified Instance. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: &Instance<'tcx>, - options: TypeIdOptions, -) -> String { - typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) -} - -/// Returns a KCFI type metadata identifier for the specified FnAbi. -pub fn kcfi_typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> u32 { - // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the - // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) - let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); - hash.finish() as u32 -} - -/// Returns a KCFI type metadata identifier for the specified Instance. -pub fn kcfi_typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: &Instance<'tcx>, - options: TypeIdOptions, -) -> u32 { - // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the - // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) - let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); - hash.finish() as u32 -} diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs deleted file mode 100644 index 51e2c96120caa..0000000000000 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ /dev/null @@ -1,1131 +0,0 @@ -/// Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow -/// Integrity (CFI) and cross-language LLVM CFI support. -/// -/// Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium -/// C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that -/// are not used across the FFI boundary. -/// -/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, -/// see design document in the tracking issue #89653. -use rustc_data_structures::base_n; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; -use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{ - self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, - TermKind, Ty, TyCtxt, UintTy, -}; -use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; -use rustc_span::def_id::DefId; -use rustc_span::sym; -use rustc_target::abi::call::{Conv, FnAbi}; -use rustc_target::abi::Integer; -use rustc_target::spec::abi::Abi; -use std::fmt::Write as _; - -use crate::typeid::TypeIdOptions; - -/// Type and extended type qualifiers. -#[derive(Eq, Hash, PartialEq)] -enum TyQ { - None, - Const, - Mut, -} - -/// Substitution dictionary key. -#[derive(Eq, Hash, PartialEq)] -enum DictKey<'tcx> { - Ty(Ty<'tcx>, TyQ), - Region(Region<'tcx>), - Const(Const<'tcx>), - Predicate(ExistentialPredicate<'tcx>), -} - -/// Options for encode_ty. -type EncodeTyOptions = TypeIdOptions; - -/// Options for transform_ty. -type TransformTyOptions = TypeIdOptions; - -/// Converts a number to a disambiguator (see -/// ). -fn to_disambiguator(num: u64) -> String { - if let Some(num) = num.checked_sub(1) { - format!("s{}_", base_n::encode(num as u128, 62)) - } else { - "s_".to_string() - } -} - -/// Converts a number to a sequence number (see -/// ). -fn to_seq_id(num: usize) -> String { - if let Some(num) = num.checked_sub(1) { - base_n::encode(num as u128, 36).to_uppercase() - } else { - "".to_string() - } -} - -/// Substitutes a component if found in the substitution dictionary (see -/// ). -fn compress<'tcx>( - dict: &mut FxHashMap, usize>, - key: DictKey<'tcx>, - comp: &mut String, -) { - match dict.get(&key) { - Some(num) => { - comp.clear(); - let _ = write!(comp, "S{}_", to_seq_id(*num)); - } - None => { - dict.insert(key, dict.len()); - } - } -} - -/// Encodes a const using the Itanium C++ ABI as a literal argument (see -/// ). -fn encode_const<'tcx>( - tcx: TyCtxt<'tcx>, - c: Const<'tcx>, - dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, -) -> String { - // L[n][]E as literal argument - let mut s = String::from('L'); - - match c.kind() { - // Const parameters - ty::ConstKind::Param(..) => { - // LE as literal argument - - // Element type - s.push_str(&encode_ty(tcx, c.ty(), dict, options)); - } - - // Literal arguments - ty::ConstKind::Value(..) => { - // L[n]E as literal argument - - // Element type - s.push_str(&encode_ty(tcx, c.ty(), dict, options)); - - // The only allowed types of const values are bool, u8, u16, u32, - // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The - // bool value false is encoded as 0 and true as 1. - match c.ty().kind() { - ty::Int(ity) => { - let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); - let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; - if val < 0 { - s.push('n'); - } - let _ = write!(s, "{val}"); - } - ty::Uint(_) => { - let val = c.eval_bits(tcx, ty::ParamEnv::reveal_all()); - let _ = write!(s, "{val}"); - } - ty::Bool => { - let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); - let _ = write!(s, "{val}"); - } - _ => { - bug!("encode_const: unexpected type `{:?}`", c.ty()); - } - } - } - - _ => { - bug!("encode_const: unexpected kind `{:?}`", c.kind()); - } - } - - // Close the "L..E" pair - s.push('E'); - - compress(dict, DictKey::Const(c), &mut s); - - s -} - -/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for -/// Rust types that are not used at the FFI boundary. -#[instrument(level = "trace", skip(tcx, dict))] -fn encode_fnsig<'tcx>( - tcx: TyCtxt<'tcx>, - fn_sig: &FnSig<'tcx>, - dict: &mut FxHashMap, usize>, - options: TypeIdOptions, -) -> String { - // Function types are delimited by an "F..E" pair - let mut s = String::from("F"); - - let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); - match fn_sig.abi { - Abi::C { .. } => { - encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); - } - _ => { - encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); - } - } - - // Encode the return type - let transform_ty_options = TransformTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); - let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options); - s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); - - // Encode the parameter types - let tys = fn_sig.inputs(); - if !tys.is_empty() { - for ty in tys { - let ty = transform_ty(tcx, *ty, transform_ty_options); - s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); - } - - if fn_sig.c_variadic { - s.push('z'); - } - } else { - if fn_sig.c_variadic { - s.push('z'); - } else { - // Empty parameter lists, whether declared as () or conventionally as (void), are - // encoded with a void parameter specifier "v". - s.push('v') - } - } - - // Close the "F..E" pair - s.push('E'); - - s -} - -/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for -/// Rust types that are not used at the FFI boundary. -fn encode_predicate<'tcx>( - tcx: TyCtxt<'tcx>, - predicate: ty::PolyExistentialPredicate<'tcx>, - dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, -) -> String { - // u[IE], where is , as vendor - // extended type. - let mut s = String::new(); - match predicate.as_ref().skip_binder() { - ty::ExistentialPredicate::Trait(trait_ref) => { - let name = encode_ty_name(tcx, trait_ref.def_id); - let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, trait_ref.args, dict, options)); - } - ty::ExistentialPredicate::Projection(projection) => { - let name = encode_ty_name(tcx, projection.def_id); - let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, projection.args, dict, options)); - match projection.term.unpack() { - TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)), - TermKind::Const(c) => s.push_str(&encode_const(tcx, c, dict, options)), - } - } - ty::ExistentialPredicate::AutoTrait(def_id) => { - let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); - } - }; - compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s); - s -} - -/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for -/// Rust types that are not used at the FFI boundary. -fn encode_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: &List>, - dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, -) -> String { - // E as part of vendor extended type - let mut s = String::new(); - let predicates: Vec> = predicates.iter().collect(); - for predicate in predicates { - s.push_str(&encode_predicate(tcx, predicate, dict, options)); - } - s -} - -/// Encodes a region using the Itanium C++ ABI as a vendor extended type. -fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap, usize>) -> String { - // u6region[I[][]E] as vendor extended type - let mut s = String::new(); - match region.kind() { - RegionKind::ReBound(debruijn, r) => { - s.push_str("u6regionI"); - // Debruijn index, which identifies the binder, as region disambiguator - let num = debruijn.index() as u64; - if num > 0 { - s.push_str(&to_disambiguator(num)); - } - // Index within the binder - let _ = write!(s, "{}", r.var.index() as u64); - s.push('E'); - compress(dict, DictKey::Region(region), &mut s); - } - // FIXME(@lcnr): Why is `ReEarlyParam` reachable here. - RegionKind::ReEarlyParam(..) | RegionKind::ReErased => { - s.push_str("u6region"); - compress(dict, DictKey::Region(region), &mut s); - } - RegionKind::ReLateParam(..) - | RegionKind::ReStatic - | RegionKind::ReError(_) - | RegionKind::ReVar(..) - | RegionKind::RePlaceholder(..) => { - bug!("encode_region: unexpected `{:?}`", region.kind()); - } - } - s -} - -/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust -/// types that are not used at the FFI boundary. -fn encode_args<'tcx>( - tcx: TyCtxt<'tcx>, - args: GenericArgsRef<'tcx>, - dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, -) -> String { - // [IE] as part of vendor extended type - let mut s = String::new(); - let args: Vec> = args.iter().collect(); - if !args.is_empty() { - s.push('I'); - for arg in args { - match arg.unpack() { - GenericArgKind::Lifetime(region) => { - s.push_str(&encode_region(region, dict)); - } - GenericArgKind::Type(ty) => { - s.push_str(&encode_ty(tcx, ty, dict, options)); - } - GenericArgKind::Const(c) => { - s.push_str(&encode_const(tcx, c, dict, options)); - } - } - } - s.push('E'); - } - s -} - -/// Encodes a ty:Ty name, including its crate and path disambiguators and names. -fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { - // Encode for use in u[IE], where - // is , using v0's without v0's extended form of paths: - // - // N..N - // C - // .. - // - // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: - // - // pub type Type1 = impl Send; - // let _: Type1 = >::foo; - // fn foo1(_: Type1) { } - // - // pub type Type2 = impl Send; - // let _: Type2 = >::foo; - // fn foo2(_: Type2) { } - // - // pub type Type3 = impl Send; - // let _: Type3 = >::foo; - // fn foo3(_: Type3) { } - // - // pub type Type4 = impl Send; - // let _: Type4 = as Trait1>::foo; - // fn foo3(_: Type4) { } - // - // Are encoded as: - // - // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE - // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE - // - // The reason for not using v0's extended form of paths is to use a consistent and simpler - // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e., - // keep symbol names close to how methods are represented in error messages). See - // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. - let mut s = String::new(); - - // Start and namespace tags - let mut def_path = tcx.def_path(def_id); - def_path.data.reverse(); - for disambiguated_data in &def_path.data { - s.push('N'); - s.push_str(match disambiguated_data.data { - hir::definitions::DefPathData::Impl => "I", // Not specified in v0's - hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's - hir::definitions::DefPathData::TypeNs(..) => "t", - hir::definitions::DefPathData::ValueNs(..) => "v", - hir::definitions::DefPathData::Closure => "C", - hir::definitions::DefPathData::Ctor => "c", - hir::definitions::DefPathData::AnonConst => "k", - hir::definitions::DefPathData::OpaqueTy => "i", - hir::definitions::DefPathData::CrateRoot - | hir::definitions::DefPathData::Use - | hir::definitions::DefPathData::GlobalAsm - | hir::definitions::DefPathData::MacroNs(..) - | hir::definitions::DefPathData::LifetimeNs(..) - | hir::definitions::DefPathData::AnonAdt => { - bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); - } - }); - } - - // Crate disambiguator and name - s.push('C'); - s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64())); - let crate_name = tcx.crate_name(def_path.krate).to_string(); - let _ = write!(s, "{}{}", crate_name.len(), &crate_name); - - // Disambiguators and names - def_path.data.reverse(); - for disambiguated_data in &def_path.data { - let num = disambiguated_data.disambiguator as u64; - if num > 0 { - s.push_str(&to_disambiguator(num)); - } - - let name = disambiguated_data.data.to_string(); - let _ = write!(s, "{}", name.len()); - - // Prepend a '_' if name starts with a digit or '_' - if let Some(first) = name.as_bytes().first() { - if first.is_ascii_digit() || *first == b'_' { - s.push('_'); - } - } else { - bug!("encode_ty_name: invalid name `{:?}`", name); - } - - s.push_str(&name); - } - - s -} - -/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for -/// Rust types that are not used at the FFI boundary. -fn encode_ty<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - dict: &mut FxHashMap, usize>, - options: EncodeTyOptions, -) -> String { - let mut typeid = String::new(); - - match ty.kind() { - // Primitive types - - // Rust's bool has the same layout as C17's _Bool, that is, its size and alignment are - // implementation-defined. Any bool can be cast into an integer, taking on the values 1 - // (true) or 0 (false). - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) - ty::Bool => { - typeid.push('b'); - } - - ty::Int(..) | ty::Uint(..) => { - // u as vendor extended type - let mut s = String::from(match ty.kind() { - ty::Int(IntTy::I8) => "u2i8", - ty::Int(IntTy::I16) => "u3i16", - ty::Int(IntTy::I32) => "u3i32", - ty::Int(IntTy::I64) => "u3i64", - ty::Int(IntTy::I128) => "u4i128", - ty::Int(IntTy::Isize) => "u5isize", - ty::Uint(UintTy::U8) => "u2u8", - ty::Uint(UintTy::U16) => "u3u16", - ty::Uint(UintTy::U32) => "u3u32", - ty::Uint(UintTy::U64) => "u3u64", - ty::Uint(UintTy::U128) => "u4u128", - ty::Uint(UintTy::Usize) => "u5usize", - _ => bug!("encode_ty: unexpected `{:?}`", ty.kind()), - }); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - // Rust's f16, f32, f64, and f126 half (16-bit), single (32-bit), double (64-bit), and - // quad (128-bit) precision floating-point types have IEEE-754 binary16, binary32, - // binary64, and binary128 floating-point layouts, respectively. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#fixed-width-floating-point-types.) - ty::Float(float_ty) => { - typeid.push_str(match float_ty { - FloatTy::F16 => "Dh", - FloatTy::F32 => "f", - FloatTy::F64 => "d", - FloatTy::F128 => "g", - }); - } - - ty::Char => { - // u4char as vendor extended type - let mut s = String::from("u4char"); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - ty::Str => { - // u3str as vendor extended type - let mut s = String::from("u3str"); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - ty::Never => { - // u5never as vendor extended type - let mut s = String::from("u5never"); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - // Compound types - // () in Rust is equivalent to void return type in C - _ if ty.is_unit() => { - typeid.push('v'); - } - - // Sequence types - ty::Tuple(tys) => { - // u5tupleIE as vendor extended type - let mut s = String::from("u5tupleI"); - for ty in tys.iter() { - s.push_str(&encode_ty(tcx, ty, dict, options)); - } - s.push('E'); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - ty::Array(ty0, len) => { - // A - let mut s = String::from("A"); - let _ = write!( - s, - "{}", - &len.try_to_scalar() - .unwrap() - .to_u64() - .unwrap_or_else(|_| panic!("failed to convert length to u64")) - ); - s.push_str(&encode_ty(tcx, *ty0, dict, options)); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - ty::Slice(ty0) => { - // u5sliceIE as vendor extended type - let mut s = String::from("u5sliceI"); - s.push_str(&encode_ty(tcx, *ty0, dict, options)); - s.push('E'); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - // User-defined types - ty::Adt(adt_def, args) => { - let mut s = String::new(); - let def_id = adt_def.did(); - if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) { - // Use user-defined CFI encoding for type - if let Some(value_str) = cfi_encoding.value_str() { - let value_str = value_str.to_string(); - let str = value_str.trim(); - if !str.is_empty() { - s.push_str(str); - // Don't compress user-defined builtin types (see - // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and - // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). - let builtin_types = [ - "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y", - "n", "o", "f", "d", "e", "g", "z", "Dh", - ]; - if !builtin_types.contains(&str) { - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - } - } else { - #[allow( - rustc::diagnostic_outside_of_impl, - rustc::untranslatable_diagnostic - )] - tcx.dcx() - .struct_span_err( - cfi_encoding.span, - format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), - ) - .emit(); - } - } else { - bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind()); - } - } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() { - // For cross-language LLVM CFI support, the encoding must be compatible at the FFI - // boundary. For instance: - // - // struct type1 {}; - // void foo(struct type1* bar) {} - // - // Is encoded as: - // - // _ZTSFvP5type1E - // - // So, encode any repr(C) user-defined type for extern function types with the "C" - // calling convention (or extern types [i.e., ty::Foreign]) as , where - // is . - let name = tcx.item_name(def_id).to_string(); - let _ = write!(s, "{}{}", name.len(), &name); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - } else { - // u[IE], where is - // , as vendor extended type. - let name = encode_ty_name(tcx, def_id); - let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, args, dict, options)); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - } - typeid.push_str(&s); - } - - ty::Foreign(def_id) => { - // , where is - let mut s = String::new(); - if let Some(cfi_encoding) = tcx.get_attr(*def_id, sym::cfi_encoding) { - // Use user-defined CFI encoding for type - if let Some(value_str) = cfi_encoding.value_str() { - if !value_str.to_string().trim().is_empty() { - s.push_str(value_str.to_string().trim()); - } else { - #[allow( - rustc::diagnostic_outside_of_impl, - rustc::untranslatable_diagnostic - )] - tcx.dcx() - .struct_span_err( - cfi_encoding.span, - format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), - ) - .emit(); - } - } else { - bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind()); - } - } else { - let name = tcx.item_name(*def_id).to_string(); - let _ = write!(s, "{}{}", name.len(), &name); - } - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - // Function types - ty::FnDef(def_id, args) - | ty::Closure(def_id, args) - | ty::CoroutineClosure(def_id, args) => { - // u[IE], where is , - // as vendor extended type. - let mut s = String::new(); - let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, args, dict, options)); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - ty::Coroutine(def_id, args, ..) => { - // u[IE], where is , - // as vendor extended type. - let mut s = String::new(); - let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); - // Encode parent args only - s.push_str(&encode_args( - tcx, - tcx.mk_args(args.as_coroutine().parent_args()), - dict, - options, - )); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - // Pointer types - ty::Ref(region, ty0, ..) => { - // [U3mut]u3refIE as vendor extended type qualifier and type - let mut s = String::new(); - s.push_str("u3refI"); - s.push_str(&encode_ty(tcx, *ty0, dict, options)); - s.push('E'); - compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s); - if ty.is_mutable_ptr() { - s = format!("{}{}", "U3mut", &s); - compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s); - } - typeid.push_str(&s); - } - - ty::RawPtr(tm) => { - // P[K] - let mut s = String::new(); - s.push_str(&encode_ty(tcx, tm.ty, dict, options)); - if !ty.is_mutable_ptr() { - s = format!("{}{}", "K", &s); - compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s); - }; - s = format!("{}{}", "P", &s); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - ty::FnPtr(fn_sig) => { - // PFE - let mut s = String::from("P"); - s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::empty())); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - // Trait types - ty::Dynamic(predicates, region, kind) => { - // u3dynIE, where is , as - // vendor extended type. - let mut s = String::from(match kind { - ty::Dyn => "u3dynI", - ty::DynStar => "u7dynstarI", - }); - s.push_str(&encode_predicates(tcx, predicates, dict, options)); - s.push_str(&encode_region(*region, dict)); - s.push('E'); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - // Type parameters - ty::Param(..) => { - // u5param as vendor extended type - let mut s = String::from("u5param"); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - - // Unexpected types - ty::Alias(..) - | ty::Bound(..) - | ty::Error(..) - | ty::CoroutineWitness(..) - | ty::Infer(..) - | ty::Placeholder(..) => { - bug!("encode_ty: unexpected `{:?}`", ty.kind()); - } - }; - - typeid -} - -/// Transforms predicates for being encoded and used in the substitution dictionary. -fn transform_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: &List>, -) -> &'tcx List> { - let predicates: Vec> = predicates - .iter() - .filter_map(|predicate| match predicate.skip_binder() { - ty::ExistentialPredicate::Trait(trait_ref) => { - let trait_ref = ty::TraitRef::identity(tcx, trait_ref.def_id); - Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), - ))) - } - ty::ExistentialPredicate::Projection(..) => None, - ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), - }) - .collect(); - tcx.mk_poly_existential_predicates(&predicates) -} - -/// Transforms args for being encoded and used in the substitution dictionary. -fn transform_args<'tcx>( - tcx: TyCtxt<'tcx>, - args: GenericArgsRef<'tcx>, - options: TransformTyOptions, -) -> GenericArgsRef<'tcx> { - let args = args.iter().map(|arg| match arg.unpack() { - GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(), - GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(), - _ => arg, - }); - tcx.mk_args_from_iter(args) -} - -// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all -// c_void types into unit types unconditionally, generalizes pointers if -// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if -// TransformTyOptions::NORMALIZE_INTEGERS option is set. -fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> { - let mut ty = ty; - - match ty.kind() { - ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {} - - ty::Bool => { - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: on all platforms that Rust's currently supports, its size and alignment are - // 1, and its ABI class is INTEGER - see Rust Layout and ABIs. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.) - // - // Clang represents bool as an 8-bit unsigned integer. - ty = tcx.types.u8; - } - } - - ty::Char => { - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Since #118032, char is guaranteed to have the same size, alignment, and function - // call ABI as u32 on all platforms. - ty = tcx.types.u32; - } - } - - ty::Int(..) | ty::Uint(..) => { - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit wide. - // All platforms we currently support have a C platform, and as a consequence, - // isize/usize are at least 16-bit wide for all of them. - // - // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.) - match ty.kind() { - ty::Int(IntTy::Isize) => match tcx.sess.target.pointer_width { - 16 => ty = tcx.types.i16, - 32 => ty = tcx.types.i32, - 64 => ty = tcx.types.i64, - 128 => ty = tcx.types.i128, - _ => bug!( - "transform_ty: unexpected pointer width `{}`", - tcx.sess.target.pointer_width - ), - }, - ty::Uint(UintTy::Usize) => match tcx.sess.target.pointer_width { - 16 => ty = tcx.types.u16, - 32 => ty = tcx.types.u32, - 64 => ty = tcx.types.u64, - 128 => ty = tcx.types.u128, - _ => bug!( - "transform_ty: unexpected pointer width `{}`", - tcx.sess.target.pointer_width - ), - }, - _ => (), - } - } - } - - _ if ty.is_unit() => {} - - ty::Tuple(tys) => { - ty = Ty::new_tup_from_iter(tcx, tys.iter().map(|ty| transform_ty(tcx, ty, options))); - } - - ty::Array(ty0, len) => { - let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); - - ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len); - } - - ty::Slice(ty0) => { - ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, options)); - } - - ty::Adt(adt_def, args) => { - if ty.is_c_void(tcx) { - ty = Ty::new_unit(tcx); - } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() - { - ty = Ty::new_adt(tcx, *adt_def, ty::List::empty()); - } else if adt_def.repr().transparent() && adt_def.is_struct() { - // Don't transform repr(transparent) types with an user-defined CFI encoding to - // preserve the user-defined CFI encoding. - if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) { - return ty; - } - let variant = adt_def.non_enum_variant(); - let param_env = tcx.param_env(variant.def_id); - let field = variant.fields.iter().find(|field| { - let ty = tcx.type_of(field.did).instantiate_identity(); - let is_zst = - tcx.layout_of(param_env.and(ty)).is_ok_and(|layout| layout.is_zst()); - !is_zst - }); - if let Some(field) = field { - let ty0 = tcx.type_of(field.did).instantiate(tcx, args); - // Generalize any repr(transparent) user-defined type that is either a pointer - // or reference, and either references itself or any other type that contains or - // references itself, to avoid a reference cycle. - if ty0.is_any_ptr() && ty0.contains(ty) { - ty = transform_ty( - tcx, - ty0, - options | TransformTyOptions::GENERALIZE_POINTERS, - ); - } else { - ty = transform_ty(tcx, ty0, options); - } - } else { - // Transform repr(transparent) types without non-ZST field into () - ty = Ty::new_unit(tcx); - } - } else { - ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, options)); - } - } - - ty::FnDef(def_id, args) => { - ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, options)); - } - - ty::Closure(def_id, args) => { - ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, options)); - } - - ty::CoroutineClosure(def_id, args) => { - ty = Ty::new_coroutine_closure(tcx, *def_id, transform_args(tcx, args, options)); - } - - ty::Coroutine(def_id, args) => { - ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, options)); - } - - ty::Ref(region, ty0, ..) => { - if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); - } else { - ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); - } - } else { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options)); - } else { - ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options)); - } - } - } - - ty::RawPtr(tm) => { - if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx)); - } else { - ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx)); - } - } else { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, tm.ty, options)); - } else { - ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, tm.ty, options)); - } - } - } - - ty::FnPtr(fn_sig) => { - if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx)); - } else { - let parameters: Vec> = fn_sig - .skip_binder() - .inputs() - .iter() - .map(|ty| transform_ty(tcx, *ty, options)) - .collect(); - let output = transform_ty(tcx, fn_sig.skip_binder().output(), options); - ty = Ty::new_fn_ptr( - tcx, - ty::Binder::bind_with_vars( - tcx.mk_fn_sig( - parameters, - output, - fn_sig.c_variadic(), - fn_sig.unsafety(), - fn_sig.abi(), - ), - fn_sig.bound_vars(), - ), - ); - } - } - - ty::Dynamic(predicates, _region, kind) => { - ty = Ty::new_dynamic( - tcx, - transform_predicates(tcx, predicates), - tcx.lifetimes.re_erased, - *kind, - ); - } - - ty::Alias(..) => { - ty = transform_ty( - tcx, - tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty), - options, - ); - } - - ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { - bug!("transform_ty: unexpected `{:?}`", ty.kind()); - } - } - - ty -} - -/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor -/// extended type qualifiers and types for Rust types that are not used at the FFI boundary. -#[instrument(level = "trace", skip(tcx))] -pub fn typeid_for_fnabi<'tcx>( - tcx: TyCtxt<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - options: TypeIdOptions, -) -> String { - // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions - // its type. - let mut typeid = String::from("_Z"); - - // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type - // metadata identifiers for function pointers. The typeinfo name encoding is a two-character - // code (i.e., 'TS') prefixed to the type encoding for the function. - typeid.push_str("TS"); - - // Function types are delimited by an "F..E" pair - typeid.push('F'); - - // A dictionary of substitution candidates used for compression (see - // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). - let mut dict: FxHashMap, usize> = FxHashMap::default(); - - let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - match fn_abi.conv { - Conv::C => { - encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); - } - _ => { - encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); - } - } - - // Encode the return type - let transform_ty_options = TransformTyOptions::from_bits(options.bits()) - .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - - // Encode the parameter types - if !fn_abi.c_variadic { - if !fn_abi.args.is_empty() { - for arg in fn_abi.args.iter() { - let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - } - } else { - // Empty parameter lists, whether declared as () or conventionally as (void), are - // encoded with a void parameter specifier "v". - typeid.push('v'); - } - } else { - for n in 0..fn_abi.fixed_count as usize { - let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - } - - typeid.push('z'); - } - - // Close the "F..E" pair - typeid.push('E'); - - // Add encoding suffixes - if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { - typeid.push_str(".normalized"); - } - - if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) { - typeid.push_str(".generalized"); - } - - typeid -} - -/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with -/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: &Instance<'tcx>, - options: TypeIdOptions, -) -> String { - let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty()))) - .unwrap_or_else(|instance| { - bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) - }); - - // If this instance is a method and self is a reference, get the impl it belongs to - let impl_def_id = tcx.impl_of_method(instance.def_id()); - if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() { - // If this impl is not an inherent impl, get the trait it implements - if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) { - // Transform the concrete self into a reference to a trait object - let existential_predicate = trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy( - existential_predicate.skip_binder(), - )]); - // Is the concrete self mutable? - let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { - Ty::new_mut_ref( - tcx, - tcx.lifetimes.re_erased, - Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), - ) - } else { - Ty::new_imm_ref( - tcx, - tcx.lifetimes.re_erased, - Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), - ) - }; - - // Replace the concrete self in an fn_abi clone by the reference to a trait object - let mut fn_abi = fn_abi.clone(); - // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the - // other fields are never used. - fn_abi.args[0].layout.ty = self_ty; - - return typeid_for_fnabi(tcx, &fn_abi, options); - } - } - - typeid_for_fnabi(tcx, fn_abi, options) -} diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index f1b1b4ed2bb84..fc2c3dcb2d952 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -5,11 +5,12 @@ use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_middle::bug; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::print::{Print, PrintError, Printer}; use rustc_middle::ty::{ - self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, TypeVisitableExt, - UintTy, + self, EarlyBinder, FloatTy, Instance, IntTy, ReifyReason, Ty, TyCtxt, TypeVisitable, + TypeVisitableExt, UintTy, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::kw; @@ -44,14 +45,12 @@ pub(super) fn mangle<'tcx>( let shim_kind = match instance.def { ty::InstanceDef::ThreadLocalShim(_) => Some("tls"), ty::InstanceDef::VTableShim(_) => Some("vtable"), - ty::InstanceDef::ReifyShim(_) => Some("reify"), + ty::InstanceDef::ReifyShim(_, None) => Some("reify"), + ty::InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"), + ty::InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"), - ty::InstanceDef::ConstructCoroutineInClosureShim { target_kind, .. } - | ty::InstanceDef::CoroutineKindShim { target_kind, .. } => match target_kind { - ty::ClosureKind::Fn => unreachable!(), - ty::ClosureKind::FnMut => Some("fn_mut"), - ty::ClosureKind::FnOnce => Some("fn_once"), - }, + ty::InstanceDef::ConstructCoroutineInClosureShim { .. } + | ty::InstanceDef::CoroutineKindShim { .. } => Some("fn_once"), _ => None, }; @@ -365,14 +364,33 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty.print(self)?; } - ty::RawPtr(mt) => { - self.push(match mt.mutbl { + ty::RawPtr(ty, mutbl) => { + self.push(match mutbl { hir::Mutability::Not => "P", hir::Mutability::Mut => "O", }); - mt.ty.print(self)?; + ty.print(self)?; } + ty::Pat(ty, pat) => match *pat { + ty::PatternKind::Range { start, end, include_end } => { + let consts = [ + start.unwrap_or(self.tcx.consts.unit), + end.unwrap_or(self.tcx.consts.unit), + ty::Const::from_bool(self.tcx, include_end).into(), + ]; + // HACK: Represent as tuple until we have something better. + // HACK: constants are used in arrays, even if the types don't match. + self.push("T"); + ty.print(self)?; + for ct in consts { + Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct) + .print(self)?; + } + self.push("E"); + } + }, + ty::Array(ty, len) => { self.push("A"); ty.print(self)?; @@ -814,7 +832,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { /// e.g. `1` becomes `"0_"`, `62` becomes `"Z_"`, etc. pub(crate) fn push_integer_62(x: u64, output: &mut String) { if let Some(x) = x.checked_sub(1) { - base_n::push_str(x as u128, 62, output); + base_n::push_str(x as u128, base_n::ALPHANUMERIC_ONLY, output); } output.push('_'); } diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/abi/call/aarch64.rs index f99f6a3b72164..04020d13f22f9 100644 --- a/compiler/rustc_target/src/abi/call/aarch64.rs +++ b/compiler/rustc_target/src/abi/call/aarch64.rs @@ -31,7 +31,7 @@ where RegKind::Vector => size.bits() == 64 || size.bits() == 128, }; - valid_unit.then_some(Uniform { unit, total: size }) + valid_unit.then_some(Uniform::consecutive(unit, size)) }) } @@ -60,7 +60,7 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { - ret.cast_to(Uniform { unit: Reg::i64(), total: size }); + ret.cast_to(Uniform::new(Reg::i64(), size)); return; } ret.make_indirect(); @@ -100,9 +100,9 @@ where }; if size.bits() <= 128 { if align.bits() == 128 { - arg.cast_to(Uniform { unit: Reg::i128(), total: size }); + arg.cast_to(Uniform::new(Reg::i128(), size)); } else { - arg.cast_to(Uniform { unit: Reg::i64(), total: size }); + arg.cast_to(Uniform::new(Reg::i64(), size)); } return; } diff --git a/compiler/rustc_target/src/abi/call/arm.rs b/compiler/rustc_target/src/abi/call/arm.rs index 95f6691d42aeb..9371e1b395865 100644 --- a/compiler/rustc_target/src/abi/call/arm.rs +++ b/compiler/rustc_target/src/abi/call/arm.rs @@ -21,7 +21,7 @@ where RegKind::Vector => size.bits() == 64 || size.bits() == 128, }; - valid_unit.then_some(Uniform { unit, total: size }) + valid_unit.then_some(Uniform::consecutive(unit, size)) }) } @@ -49,7 +49,7 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 32 { - ret.cast_to(Uniform { unit: Reg::i32(), total: size }); + ret.cast_to(Uniform::new(Reg::i32(), size)); return; } ret.make_indirect(); @@ -78,7 +78,7 @@ where let align = arg.layout.align.abi.bytes(); let total = arg.layout.size; - arg.cast_to(Uniform { unit: if align <= 4 { Reg::i32() } else { Reg::i64() }, total }); + arg.cast_to(Uniform::consecutive(if align <= 4 { Reg::i32() } else { Reg::i64() }, total)); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/compiler/rustc_target/src/abi/call/csky.rs b/compiler/rustc_target/src/abi/call/csky.rs index 8b4328db52ebb..7951f28beeaff 100644 --- a/compiler/rustc_target/src/abi/call/csky.rs +++ b/compiler/rustc_target/src/abi/call/csky.rs @@ -18,7 +18,7 @@ fn classify_ret(arg: &mut ArgAbi<'_, Ty>) { if total.bits() > 64 { arg.make_indirect(); } else if total.bits() > 32 { - arg.cast_to(Uniform { unit: Reg::i32(), total }); + arg.cast_to(Uniform::new(Reg::i32(), total)); } else { arg.cast_to(Reg::i32()); } @@ -38,7 +38,7 @@ fn classify_arg(arg: &mut ArgAbi<'_, Ty>) { if arg.layout.is_aggregate() { let total = arg.layout.size; if total.bits() > 32 { - arg.cast_to(Uniform { unit: Reg::i32(), total }); + arg.cast_to(Uniform::new(Reg::i32(), total)); } else { arg.cast_to(Reg::i32()); } diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs index 35d4b331cb4d3..943b12a9fbfcc 100644 --- a/compiler/rustc_target/src/abi/call/loongarch.rs +++ b/compiler/rustc_target/src/abi/call/loongarch.rs @@ -195,7 +195,7 @@ where if total.bits() <= xlen { arg.cast_to(xlen_reg); } else { - arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); + arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2))); } return false; } @@ -278,10 +278,10 @@ fn classify_arg<'a, Ty, C>( if total.bits() > xlen { let align_regs = align > xlen; if is_loongarch_aggregate(arg) { - arg.cast_to(Uniform { - unit: if align_regs { double_xlen_reg } else { xlen_reg }, - total: Size::from_bits(xlen * 2), - }); + arg.cast_to(Uniform::new( + if align_regs { double_xlen_reg } else { xlen_reg }, + Size::from_bits(xlen * 2), + )); } if align_regs && is_vararg { *avail_gprs -= *avail_gprs % 2; diff --git a/compiler/rustc_target/src/abi/call/mips.rs b/compiler/rustc_target/src/abi/call/mips.rs index 57ccfe2152bdf..0e5a7f37a094c 100644 --- a/compiler/rustc_target/src/abi/call/mips.rs +++ b/compiler/rustc_target/src/abi/call/mips.rs @@ -27,7 +27,7 @@ where if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); - arg.cast_to_and_pad_i32(Uniform { unit: Reg::i32(), total: size }, pad_i32); + arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_target/src/abi/call/mips64.rs b/compiler/rustc_target/src/abi/call/mips64.rs index 2700f67b2096b..b2a2c34b980f6 100644 --- a/compiler/rustc_target/src/abi/call/mips64.rs +++ b/compiler/rustc_target/src/abi/call/mips64.rs @@ -68,7 +68,7 @@ where } // Cast to a uniform int structure - ret.cast_to(Uniform { unit: Reg::i64(), total: size }); + ret.cast_to(Uniform::new(Reg::i64(), size)); } else { ret.make_indirect(); } @@ -139,7 +139,7 @@ where let rest_size = size - Size::from_bytes(8) * prefix_index as u64; arg.cast_to(CastTarget { prefix, - rest: Uniform { unit: Reg::i64(), total: rest_size }, + rest: Uniform::new(Reg::i64(), rest_size), attrs: ArgAttributes { regular: ArgAttribute::default(), arg_ext: ArgExtension::None, diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 486afc5f8f30a..3ddea42f67bbd 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -1,6 +1,7 @@ use crate::abi::{self, Abi, Align, FieldsShape, Size}; use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout}; -use crate::spec::{self, HasTargetSpec}; +use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt}; +use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; use std::str::FromStr; @@ -100,6 +101,8 @@ pub use attr_impl::ArgAttribute; #[allow(non_upper_case_globals)] #[allow(unused)] mod attr_impl { + use rustc_macros::HashStable_Generic; + // The subset of llvm::Attribute needed for arguments, packed into a bitfield. #[derive(Clone, Copy, Default, Hash, PartialEq, Eq, HashStable_Generic)] pub struct ArgAttribute(u8); @@ -251,15 +254,20 @@ pub struct Uniform { /// The total size of the argument, which can be: /// * equal to `unit.size` (one scalar/vector), /// * a multiple of `unit.size` (an array of scalar/vectors), - /// * if `unit.kind` is `Integer`, the last element - /// can be shorter, i.e., `{ i64, i64, i32 }` for - /// 64-bit integers with a total size of 20 bytes. + /// * if `unit.kind` is `Integer`, the last element can be shorter, i.e., `{ i64, i64, i32 }` + /// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed, + /// this size will be rounded up to the nearest multiple of `unit.size`. pub total: Size, + + /// Indicate that the argument is consecutive, in the sense that either all values need to be + /// passed in register, or all on the stack. If they are passed on the stack, there should be + /// no additional padding between elements. + pub is_consecutive: bool, } impl From for Uniform { fn from(unit: Reg) -> Uniform { - Uniform { unit, total: unit.size } + Uniform { unit, total: unit.size, is_consecutive: false } } } @@ -267,6 +275,18 @@ impl Uniform { pub fn align(&self, cx: &C) -> Align { self.unit.align(cx) } + + /// Pass using one or more values of the given type, without requiring them to be consecutive. + /// That is, some values may be passed in register and some on the stack. + pub fn new(unit: Reg, total: Size) -> Self { + Uniform { unit, total, is_consecutive: false } + } + + /// Pass using one or more consecutive values of the given type. Either all values will be + /// passed in registers, or all on the stack. + pub fn consecutive(unit: Reg, total: Size) -> Self { + Uniform { unit, total, is_consecutive: true } + } } /// Describes the type used for `PassMode::Cast`. @@ -319,14 +339,17 @@ impl CastTarget { } pub fn size(&self, _cx: &C) -> Size { - let mut size = self.rest.total; - for i in 0..self.prefix.iter().count() { - match self.prefix[i] { - Some(v) => size += v.size, - None => {} - } - } - return size; + // Prefix arguments are passed in specific designated registers + let prefix_size = self + .prefix + .iter() + .filter_map(|x| x.map(|reg| reg.size)) + .fold(Size::ZERO, |acc, size| acc + size); + // Remaining arguments are passed in chunks of the unit size + let rest_size = + self.rest.unit.size * self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes()); + + prefix_size + rest_size } pub fn align(&self, cx: &C) -> Align { @@ -809,7 +832,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { ) -> Result<(), AdjustForForeignAbiError> where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout + HasTargetSpec, + C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt, { if abi == spec::abi::Abi::X86Interrupt { if let Some(arg) = self.args.first_mut() { @@ -866,7 +889,9 @@ impl<'a, Ty> FnAbi<'a, Ty> { "sparc" => sparc::compute_abi_info(cx, self), "sparc64" => sparc64::compute_abi_info(cx, self), "nvptx64" => { - if cx.target_spec().adjust_abi(abi, self.c_variadic) == spec::abi::Abi::PtxKernel { + if cx.target_spec().adjust_abi(cx, abi, self.c_variadic) + == spec::abi::Abi::PtxKernel + { nvptx64::compute_ptx_kernel_abi_info(cx, self) } else { nvptx64::compute_abi_info(self) @@ -875,7 +900,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "hexagon" => hexagon::compute_abi_info(self), "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), "wasm32" | "wasm64" => { - if cx.target_spec().adjust_abi(abi, self.c_variadic) == spec::abi::Abi::Wasm { + if cx.target_spec().adjust_abi(cx, abi, self.c_variadic) == spec::abi::Abi::Wasm { wasm::compute_wasm_abi_info(self) } else { wasm::compute_c_abi_info(cx, self) @@ -927,7 +952,7 @@ impl FromStr for Conv { } // Some types are used a lot. Make sure they don't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] mod size_asserts { use super::*; use rustc_data_structures::static_assert_size; diff --git a/compiler/rustc_target/src/abi/call/nvptx64.rs b/compiler/rustc_target/src/abi/call/nvptx64.rs index 5c040ce9c3b10..f85fa2419f0f6 100644 --- a/compiler/rustc_target/src/abi/call/nvptx64.rs +++ b/compiler/rustc_target/src/abi/call/nvptx64.rs @@ -35,7 +35,7 @@ where 16 => Reg::i128(), _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"), }; - arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) }); + arg.cast_to(Uniform::new(unit, Size::from_bytes(2 * align_bytes))); } else { // FIXME: find a better way to do this. See https://github.com/rust-lang/rust/issues/117271. arg.make_direct_deprecated(); diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/abi/call/powerpc64.rs index 2d41f77e50e1e..11a6cb52babc9 100644 --- a/compiler/rustc_target/src/abi/call/powerpc64.rs +++ b/compiler/rustc_target/src/abi/call/powerpc64.rs @@ -2,7 +2,7 @@ // Alignment of 128 bit types is not currently handled, this will // need to be fixed when PowerPC vector support is added. -use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; +use crate::abi::call::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; use crate::abi::{Endian, HasDataLayout, TyAbiInterface}; use crate::spec::HasTargetSpec; @@ -37,7 +37,7 @@ where RegKind::Vector => arg.layout.size.bits() == 128, }; - valid_unit.then_some(Uniform { unit, total: arg.layout.size }) + valid_unit.then_some(Uniform::consecutive(unit, arg.layout.size)) }) } @@ -81,7 +81,7 @@ where Reg::i64() }; - ret.cast_to(Uniform { unit, total: size }); + ret.cast_to(Uniform::new(unit, size)); return; } @@ -108,18 +108,20 @@ where } let size = arg.layout.size; - let (unit, total) = if size.bits() <= 64 { + if size.bits() <= 64 { // Aggregates smaller than a doubleword should appear in // the least-significant bits of the parameter doubleword. - (Reg { kind: RegKind::Integer, size }, size) + arg.cast_to(Reg { kind: RegKind::Integer, size }) } else { - // Aggregates larger than a doubleword should be padded - // at the tail to fill out a whole number of doublewords. - let reg_i64 = Reg::i64(); - (reg_i64, size.align_to(reg_i64.align(cx))) + // Aggregates larger than i64 should be padded at the tail to fill out a whole number + // of i64s or i128s, depending on the aggregate alignment. Always use an array for + // this, even if there is only a single element. + let reg = if arg.layout.align.abi.bytes() > 8 { Reg::i128() } else { Reg::i64() }; + arg.cast_to(Uniform::consecutive( + reg, + size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()), + )) }; - - arg.cast_to(Uniform { unit, total }); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs index 6a38496dc5761..5d4b3a9d245a4 100644 --- a/compiler/rustc_target/src/abi/call/riscv.rs +++ b/compiler/rustc_target/src/abi/call/riscv.rs @@ -201,7 +201,7 @@ where if total.bits() <= xlen { arg.cast_to(xlen_reg); } else { - arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); + arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2))); } return false; } @@ -284,10 +284,10 @@ fn classify_arg<'a, Ty, C>( if total.bits() > xlen { let align_regs = align > xlen; if is_riscv_aggregate(arg) { - arg.cast_to(Uniform { - unit: if align_regs { double_xlen_reg } else { xlen_reg }, - total: Size::from_bits(xlen * 2), - }); + arg.cast_to(Uniform::new( + if align_regs { double_xlen_reg } else { xlen_reg }, + Size::from_bits(xlen * 2), + )); } if align_regs && is_vararg { *avail_gprs -= *avail_gprs % 2; diff --git a/compiler/rustc_target/src/abi/call/sparc.rs b/compiler/rustc_target/src/abi/call/sparc.rs index 57ccfe2152bdf..0e5a7f37a094c 100644 --- a/compiler/rustc_target/src/abi/call/sparc.rs +++ b/compiler/rustc_target/src/abi/call/sparc.rs @@ -27,7 +27,7 @@ where if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); - arg.cast_to_and_pad_i32(Uniform { unit: Reg::i32(), total: size }, pad_i32); + arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs index cbed5b4afc134..acdcd5cc0d482 100644 --- a/compiler/rustc_target/src/abi/call/sparc64.rs +++ b/compiler/rustc_target/src/abi/call/sparc64.rs @@ -192,7 +192,7 @@ where arg.cast_to(CastTarget { prefix: data.prefix, - rest: Uniform { unit: Reg::i64(), total: rest_size }, + rest: Uniform::new(Reg::i64(), rest_size), attrs: ArgAttributes { regular: data.arg_attribute, arg_ext: ArgExtension::None, @@ -205,7 +205,7 @@ where } } - arg.cast_to(Uniform { unit: Reg::i64(), total }); + arg.cast_to(Uniform::new(Reg::i64(), total)); } pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/abi/call/wasm.rs index a7a2b314a94f0..a773fb1e814e0 100644 --- a/compiler/rustc_target/src/abi/call/wasm.rs +++ b/compiler/rustc_target/src/abi/call/wasm.rs @@ -1,4 +1,4 @@ -use crate::abi::call::{ArgAbi, FnAbi, Uniform}; +use crate::abi::call::{ArgAbi, FnAbi}; use crate::abi::{HasDataLayout, TyAbiInterface}; fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool @@ -10,7 +10,7 @@ where if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) { let size = val.layout.size; if unit.size == size { - val.cast_to(Uniform { unit, total: size }); + val.cast_to(unit); return true; } } diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 97132311a5c9a..1a3218da1af04 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,7 +1,6 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; -use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -27,32 +26,28 @@ impl AArch64InlineAsmRegClass { None } - pub fn suggest_modifier( - self, - _arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, _arch: InlineAsmArch, ty: InlineAsmType) -> Option { match self { Self::reg => match ty.size().bits() { 64 => None, - _ => Some(('w', "w0")), + _ => Some(('w', "w0", 32).into()), }, Self::vreg | Self::vreg_low16 => match ty.size().bits() { - 8 => Some(('b', "b0")), - 16 => Some(('h', "h0")), - 32 => Some(('s', "s0")), - 64 => Some(('d', "d0")), - 128 => Some(('q', "q0")), + 8 => Some(('b', "b0", 8).into()), + 16 => Some(('h', "h0", 16).into()), + 32 => Some(('s', "s0", 32).into()), + 64 => Some(('d', "d0", 64).into()), + 128 => Some(('q', "q0", 128).into()), _ => None, }, Self::preg => None, } } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { match self { - Self::reg => Some(('x', "x0")), - Self::vreg | Self::vreg_low16 => Some(('v', "v0")), + Self::reg => Some(('x', "x0", 64).into()), + Self::vreg | Self::vreg_low16 => Some(('v', "v0", 128).into()), Self::preg => None, } } @@ -91,6 +86,20 @@ fn reserved_x18( } } +fn restricted_for_arm64ec( + arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxIndexSet, + _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"], @@ -106,8 +115,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"], @@ -115,12 +124,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"], @@ -138,22 +147,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/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 514e30ae0204d..37184393a730b 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,7 +1,6 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; -use rustc_macros::HashStable_Generic; use rustc_span::{sym, Symbol}; use std::fmt; @@ -35,11 +34,11 @@ impl ArmInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs index 9a96a61f5b2cd..6943fd9b5d72a 100644 --- a/compiler/rustc_target/src/asm/avr.rs +++ b/compiler/rustc_target/src/asm/avr.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -29,11 +28,11 @@ impl AvrInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs index 3b03766a089b2..faaeabb3c901b 100644 --- a/compiler/rustc_target/src/asm/bpf.rs +++ b/compiler/rustc_target/src/asm/bpf.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -23,11 +22,11 @@ impl BpfInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/csky.rs b/compiler/rustc_target/src/asm/csky.rs index db3d8106040b6..db6cdecfe1909 100644 --- a/compiler/rustc_target/src/asm/csky.rs +++ b/compiler/rustc_target/src/asm/csky.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -23,11 +22,11 @@ impl CSKYInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs index d20270ac9e95a..7a809efee6f4d 100644 --- a/compiler/rustc_target/src/asm/hexagon.rs +++ b/compiler/rustc_target/src/asm/hexagon.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -22,11 +21,11 @@ impl HexagonInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index 9d1a4f3eeeafa..534b696f7edf0 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -23,11 +22,11 @@ impl LoongArchInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/m68k.rs b/compiler/rustc_target/src/asm/m68k.rs index 8c857550cf21a..ea367e3d2f94e 100644 --- a/compiler/rustc_target/src/asm/m68k.rs +++ b/compiler/rustc_target/src/asm/m68k.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -24,11 +23,11 @@ impl M68kInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs index 4e7c2eb1bf88e..f0d659c9b9796 100644 --- a/compiler/rustc_target/src/asm/mips.rs +++ b/compiler/rustc_target/src/asm/mips.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -23,11 +22,11 @@ impl MipsInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index a11884bea268f..5f4ce5ed59974 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -1,18 +1,30 @@ use crate::spec::Target; use crate::{abi::Size, spec::RelocModel}; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::Symbol; use std::fmt; use std::str::FromStr; +pub struct ModifierInfo { + pub modifier: char, + pub result: &'static str, + pub size: u16, +} + +impl From<(char, &'static str, u16)> for ModifierInfo { + fn from((modifier, result, size): (char, &'static str, u16)) -> Self { + Self { modifier, result, size } + } +} + macro_rules! def_reg_class { ($arch:ident $arch_regclass:ident { $( $class:ident, )* }) => { - #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, HashStable_Generic)] + #[derive(Copy, Clone, rustc_macros::Encodable, rustc_macros::Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, rustc_macros::HashStable_Generic)] #[allow(non_camel_case_types)] pub enum $arch_regclass { $($class,)* @@ -61,7 +73,7 @@ macro_rules! def_regs { )* }) => { #[allow(unreachable_code)] - #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, HashStable_Generic)] + #[derive(Copy, Clone, rustc_macros::Encodable, rustc_macros::Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, rustc_macros::HashStable_Generic)] #[allow(non_camel_case_types)] pub enum $arch_reg { $($reg,)* @@ -205,6 +217,7 @@ pub enum InlineAsmArch { X86_64, Arm, AArch64, + Arm64EC, RiscV32, RiscV64, Nvptx64, @@ -234,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), @@ -329,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)?) } @@ -512,11 +528,7 @@ impl InlineAsmRegClass { /// Such suggestions are useful if a type smaller than the full register /// size is used and a modifier can be used to point to the subregister of /// the correct size. - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { match self { Self::X86(r) => r.suggest_modifier(arch, ty), Self::Arm(r) => r.suggest_modifier(arch, ty), @@ -545,7 +557,7 @@ impl InlineAsmRegClass { /// This is only needed when the register class can suggest a modifier, so /// that the user can be shown how to get the default behavior without a /// warning. - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, arch: InlineAsmArch) -> Option { match self { Self::X86(r) => r.default_modifier(arch), Self::Arm(r) => r.default_modifier(arch), @@ -602,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)?) } @@ -775,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 @@ -901,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/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs index a27d6390a72e8..14013cd8a7b41 100644 --- a/compiler/rustc_target/src/asm/msp430.rs +++ b/compiler/rustc_target/src/asm/msp430.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -22,11 +21,11 @@ impl Msp430InlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs index 8e1e91e7c5f1f..6c066ad7ac8fc 100644 --- a/compiler/rustc_target/src/asm/nvptx.rs +++ b/compiler/rustc_target/src/asm/nvptx.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; def_reg_class! { @@ -23,11 +22,11 @@ impl NvptxInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index d3ccb30350a27..45e9ace0f291b 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -26,11 +25,11 @@ impl PowerPCInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index dea6d50fe2ba8..3845a0e14af09 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -1,7 +1,6 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; -use rustc_macros::HashStable_Generic; use rustc_span::{sym, Symbol}; use std::fmt; @@ -26,11 +25,11 @@ impl RiscVInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs index b8afeb824d847..2bab41cd8a1b4 100644 --- a/compiler/rustc_target/src/asm/s390x.rs +++ b/compiler/rustc_target/src/asm/s390x.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; use std::fmt; @@ -24,11 +23,11 @@ impl S390xInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs index 31073da10b21b..f242faec0266d 100644 --- a/compiler/rustc_target/src/asm/spirv.rs +++ b/compiler/rustc_target/src/asm/spirv.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; def_reg_class! { @@ -21,11 +20,11 @@ impl SpirVInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/wasm.rs b/compiler/rustc_target/src/asm/wasm.rs index f095b7c6e118c..b5f4d10fb2b4b 100644 --- a/compiler/rustc_target/src/asm/wasm.rs +++ b/compiler/rustc_target/src/asm/wasm.rs @@ -1,5 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; -use rustc_macros::HashStable_Generic; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_span::Symbol; def_reg_class! { @@ -21,11 +20,11 @@ impl WasmInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 3902dac7ff654..28413a5bcbd17 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -1,7 +1,6 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; -use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -53,32 +52,28 @@ impl X86InlineAsmRegClass { } } - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { match self { Self::reg => match ty.size().bits() { - 16 => Some(('x', "ax")), - 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + 16 => Some(('x', "ax", 16).into()), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax", 32).into()), _ => None, }, Self::reg_abcd => match ty.size().bits() { - 16 => Some(('x', "ax")), - 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + 16 => Some(('x', "ax", 16).into()), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax", 32).into()), _ => None, }, Self::reg_byte => None, Self::xmm_reg => None, Self::ymm_reg => match ty.size().bits() { 256 => None, - _ => Some(('x', "xmm0")), + _ => Some(('x', "xmm0", 128).into()), }, Self::zmm_reg => match ty.size().bits() { 512 => None, - 256 => Some(('y', "ymm0")), - _ => Some(('x', "xmm0")), + 256 => Some(('y', "ymm0", 256).into()), + _ => Some(('x', "xmm0", 128).into()), }, Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, @@ -86,19 +81,19 @@ impl X86InlineAsmRegClass { } } - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, arch: InlineAsmArch) -> Option { match self { Self::reg | Self::reg_abcd => { if arch == InlineAsmArch::X86_64 { - Some(('r', "rax")) + Some(('r', "rax", 64).into()) } else { - Some(('e', "eax")) + Some(('e', "eax", 32).into()) } } Self::reg_byte => None, - Self::xmm_reg => Some(('x', "xmm0")), - Self::ymm_reg => Some(('y', "ymm0")), - Self::zmm_reg => Some(('z', "zmm0")), + Self::xmm_reg => Some(('x', "xmm0", 128).into()), + Self::ymm_reg => Some(('y', "ymm0", 256).into()), + Self::zmm_reg => Some(('z', "zmm0", 512).into()), Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, Self::tmm_reg => None, diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 8019d2b80cde0..84d7930663a30 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -9,25 +9,17 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![cfg_attr(bootstrap, feature(exhaustive_patterns))] -#![cfg_attr(not(bootstrap), feature(min_exhaustive_patterns))] +#![feature(min_exhaustive_patterns)] #![feature(rustdoc_internals)] #![feature(assert_matches)] #![feature(iter_intersperse)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![feature(rustc_attrs)] #![feature(step_trait)] #![allow(internal_features)] use std::path::{Path, PathBuf}; -#[macro_use] -extern crate rustc_macros; - -#[macro_use] -extern crate tracing; - pub mod abi; pub mod asm; pub mod json; diff --git a/compiler/rustc_target/src/spec/abi/mod.rs b/compiler/rustc_target/src/spec/abi/mod.rs index 388e76d83e243..bf51bb4bf8256 100644 --- a/compiler/rustc_target/src/spec/abi/mod.rs +++ b/compiler/rustc_target/src/spec/abi/mod.rs @@ -1,6 +1,6 @@ use std::fmt; -use rustc_macros::HashStable_Generic; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index dd75377ead2a7..d667bad44e3b7 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -102,6 +102,7 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs { "ios" => ios_deployment_target(arch, abi), "tvos" => tvos_deployment_target(), "watchos" => watchos_deployment_target(), + "visionos" => visionos_deployment_target(), "macos" => macos_deployment_target(arch), _ => unreachable!(), }; @@ -202,6 +203,8 @@ pub fn sdk_version(platform: u32) -> Option<(u32, u32)> { | object::macho::PLATFORM_TVOSSIMULATOR | object::macho::PLATFORM_MACCATALYST => Some((16, 2)), object::macho::PLATFORM_WATCHOS | object::macho::PLATFORM_WATCHOSSIMULATOR => Some((9, 1)), + // FIXME: Upgrade to `object-rs` 0.33+ implementation with visionOS platform definition + 11 | 12 => Some((1, 0)), _ => None, } } @@ -216,6 +219,9 @@ pub fn platform(target: &Target) -> Option { ("watchos", _) => object::macho::PLATFORM_WATCHOS, ("tvos", "sim") => object::macho::PLATFORM_TVOSSIMULATOR, ("tvos", _) => object::macho::PLATFORM_TVOS, + // FIXME: Upgrade to `object-rs` 0.33+ implementation with visionOS platform definition + ("visionos", "sim") => 12, + ("visionos", _) => 11, _ => return None, }) } @@ -240,6 +246,7 @@ pub fn deployment_target(target: &Target) -> Option<(u32, u32)> { } "watchos" => watchos_deployment_target(), "tvos" => tvos_deployment_target(), + "visionos" => visionos_deployment_target(), _ => return None, }; @@ -290,6 +297,8 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow]> { || sdkroot.contains("AppleTVSimulator.platform") || sdkroot.contains("WatchOS.platform") || sdkroot.contains("WatchSimulator.platform") + || sdkroot.contains("XROS.platform") + || sdkroot.contains("XRSimulator.platform") { env_remove.push("SDKROOT".into()) } @@ -299,6 +308,7 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow]> { // although this is apparently ignored when using the linker at "/usr/bin/ld". env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into()); env_remove.push("TVOS_DEPLOYMENT_TARGET".into()); + env_remove.push("XROS_DEPLOYMENT_TARGET".into()); env_remove.into() } else { // Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part @@ -359,7 +369,27 @@ fn watchos_deployment_target() -> (u32, u32) { from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0)) } +pub fn watchos_llvm_target(arch: Arch) -> String { + let (major, minor) = watchos_deployment_target(); + format!("{}-apple-watchos{}.{}.0", arch.target_name(), major, minor) +} + pub fn watchos_sim_llvm_target(arch: Arch) -> String { let (major, minor) = watchos_deployment_target(); format!("{}-apple-watchos{}.{}.0-simulator", arch.target_name(), major, minor) } + +fn visionos_deployment_target() -> (u32, u32) { + // If you are looking for the default deployment target, prefer `rustc --print deployment-target`. + from_set_deployment_target("XROS_DEPLOYMENT_TARGET").unwrap_or((1, 0)) +} + +pub fn visionos_llvm_target(arch: Arch) -> String { + let (major, minor) = visionos_deployment_target(); + format!("{}-apple-visionos{}.{}.0", arch.target_name(), major, minor) +} + +pub fn visionos_sim_llvm_target(arch: Arch) -> String { + let (major, minor) = visionos_deployment_target(); + format!("{}-apple-visionos{}.{}.0-simulator", arch.target_name(), major, minor) +} diff --git a/compiler/rustc_target/src/spec/base/apple/tests.rs b/compiler/rustc_target/src/spec/base/apple/tests.rs index 097039d6c7382..7a985ad4dc056 100644 --- a/compiler/rustc_target/src/spec/base/apple/tests.rs +++ b/compiler/rustc_target/src/spec/base/apple/tests.rs @@ -1,6 +1,7 @@ use crate::spec::targets::{ - aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_watchos_sim, i686_apple_darwin, - x86_64_apple_darwin, x86_64_apple_ios, x86_64_apple_tvos, x86_64_apple_watchos_sim, + aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_visionos_sim, + aarch64_apple_watchos_sim, i686_apple_darwin, x86_64_apple_darwin, x86_64_apple_ios, + x86_64_apple_tvos, x86_64_apple_watchos_sim, }; #[test] @@ -12,6 +13,7 @@ fn simulator_targets_set_abi() { aarch64_apple_ios_sim::target(), // Note: There is currently no ARM64 tvOS simulator target aarch64_apple_watchos_sim::target(), + aarch64_apple_visionos_sim::target(), ]; for target in &all_sim_targets { @@ -32,7 +34,11 @@ fn macos_link_environment_unmodified() { // for the host. assert_eq!( target.link_env_remove, - crate::spec::cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET"], + crate::spec::cvs![ + "IPHONEOS_DEPLOYMENT_TARGET", + "TVOS_DEPLOYMENT_TARGET", + "XROS_DEPLOYMENT_TARGET" + ], ); } } diff --git a/compiler/rustc_target/src/spec/base/msvc.rs b/compiler/rustc_target/src/spec/base/msvc.rs index efe949a4e9074..44fc376fea084 100644 --- a/compiler/rustc_target/src/spec/base/msvc.rs +++ b/compiler/rustc_target/src/spec/base/msvc.rs @@ -14,6 +14,7 @@ pub fn opts() -> TargetOptions { pre_link_args, abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, + archive_format: "coff".into(), // Currently this is the only supported method of debuginfo on MSVC // where `*.pdb` files show up next to the final artifact. diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 4b4d2aca26e4a..f237391016e77 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -80,9 +80,6 @@ pub fn options() -> TargetOptions { // threaded model which will legalize atomics to normal operations. singlethread: true, - // no dynamic linking, no need for default visibility! - default_hidden_visibility: true, - // Symbol visibility takes care of this for the WebAssembly. // Additionally the only known linker, LLD, doesn't support the script // arguments just yet diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 941d767b850dc..bd347c1b4b3fd 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -37,9 +37,10 @@ use crate::abi::call::Conv; use crate::abi::{Endian, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; use crate::json::{Json, ToJson}; -use crate::spec::abi::{lookup as lookup_abi, Abi}; +use crate::spec::abi::Abi; use crate::spec::crt_objects::CrtObjects; use rustc_fs_util::try_canonicalize; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::symbol::{kw, sym, Symbol}; use serde_json::Value; @@ -50,8 +51,7 @@ use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; - -use rustc_macros::HashStable_Generic; +use tracing::debug; pub mod abi; pub mod crt_objects; @@ -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 { + 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, @@ -1557,6 +1631,9 @@ supported_targets! { ("aarch64-apple-watchos", aarch64_apple_watchos), ("aarch64-apple-watchos-sim", aarch64_apple_watchos_sim), + ("aarch64-apple-visionos", aarch64_apple_visionos), + ("aarch64-apple-visionos-sim", aarch64_apple_visionos_sim), + ("armebv7r-none-eabi", armebv7r_none_eabi), ("armebv7r-none-eabihf", armebv7r_none_eabihf), ("armv7r-none-eabi", armv7r_none_eabi), @@ -1621,6 +1698,7 @@ supported_targets! { ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), ("riscv32im-risc0-zkvm-elf", riscv32im_risc0_zkvm_elf), ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf), + ("riscv32ima-unknown-none-elf", riscv32ima_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), ("riscv32imac-esp-espidf", riscv32imac_esp_espidf), @@ -1837,6 +1915,19 @@ impl HasTargetSpec for Target { } } +/// Which C ABI to use for `wasm32-unknown-unknown`. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum WasmCAbi { + /// Spec-compliant C ABI. + Spec, + /// Legacy ABI. Which is non-spec-compliant. + Legacy, +} + +pub trait HasWasmCAbiOpt { + fn wasm_c_abi_opt(&self) -> WasmCAbi; +} + type StaticCow = Cow<'static, T>; /// Optional aspects of a target specification. @@ -2091,6 +2182,9 @@ pub struct TargetOptions { /// compiling `rustc` will be used instead (or llvm if it is not set). /// /// N.B. when *using* the compiler, backend can always be overridden with `-Zcodegen-backend`. + /// + /// This was added by WaffleLapkin in #116793. The motivation is a rustc fork that requires a + /// custom codegen backend for a particular target. pub default_codegen_backend: Option>, /// Whether to generate trap instructions in places where optimization would @@ -2192,9 +2286,6 @@ pub struct TargetOptions { /// distributed with the target, the sanitizer should still appear in this list for the target. pub supported_sanitizers: SanitizerSet, - /// If present it's a default value to use for adjusting the C ABI. - pub default_adjusted_cabi: Option, - /// Minimum number of bits in #[repr(C)] enum. Defaults to the size of c_int pub c_enum_min_bits: Option, @@ -2426,7 +2517,6 @@ impl Default for TargetOptions { // `Off` is supported by default, but targets can remove this manually, e.g. Windows. supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), supported_sanitizers: SanitizerSet::empty(), - default_adjusted_cabi: None, c_enum_min_bits: None, generate_arange_section: true, supports_stack_protector: true, @@ -2457,9 +2547,21 @@ impl DerefMut for Target { impl Target { /// Given a function ABI, turn it into the correct ABI for this target. - pub fn adjust_abi(&self, abi: Abi, c_variadic: bool) -> Abi { + pub fn adjust_abi(&self, cx: &C, abi: Abi, c_variadic: bool) -> Abi + where + C: HasWasmCAbiOpt, + { match abi { - Abi::C { .. } => self.default_adjusted_cabi.unwrap_or(abi), + Abi::C { .. } => { + if self.arch == "wasm32" + && self.os == "unknown" + && cx.wasm_c_abi_opt() == WasmCAbi::Legacy + { + Abi::Wasm + } else { + abi + } + } // On Windows, `extern "system"` behaves like msvc's `__stdcall`. // `__stdcall` only applies on x86 and on non-variadic functions: @@ -2998,16 +3100,6 @@ impl Target { } } } ); - ($key_name:ident, Option) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match lookup_abi(s) { - Ok(abi) => base.$key_name = Some(abi), - _ => return Some(Err(format!("'{}' is not a valid value for abi", s))), - } - Some(Ok(())) - })).unwrap_or(Ok(())) - } ); ($key_name:ident, TargetFamilies) => ( { if let Some(value) = obj.remove("target-family") { if let Some(v) = value.as_array() { @@ -3157,7 +3249,6 @@ impl Target { key!(split_debuginfo, SplitDebuginfo)?; key!(supported_split_debuginfo, fallible_list)?; key!(supported_sanitizers, SanitizerSet)?; - key!(default_adjusted_cabi, Option)?; key!(generate_arange_section, bool); key!(supports_stack_protector, bool); key!(entry_name); @@ -3421,10 +3512,6 @@ impl ToJson for Target { target_option_val!(entry_abi); target_option_val!(supports_xray); - if let Some(abi) = self.default_adjusted_cabi { - d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json()); - } - // Serializing `-Clink-self-contained` needs a dynamic key to support the // backwards-compatible variants. d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json()); diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs new file mode 100644 index 0000000000000..7afe224163bcc --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs @@ -0,0 +1,27 @@ +use crate::spec::base::apple::{opts, visionos_llvm_target, Arch}; +use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions}; + +pub fn target() -> Target { + let arch = Arch::Arm64; + let mut base = opts("visionos", arch); + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD; + + Target { + llvm_target: visionos_llvm_target(arch).into(), + metadata: crate::spec::TargetMetadata { + description: Some("ARM64 Apple visionOS".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(), + arch: arch.target_arch(), + options: TargetOptions { + features: "+neon,+fp-armv8,+apple-a16".into(), + max_atomic_width: Some(128), + frame_pointer: FramePointer::NonLeaf, + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs new file mode 100644 index 0000000000000..422b2d7b922fc --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs @@ -0,0 +1,27 @@ +use crate::spec::base::apple::{opts, visionos_sim_llvm_target, Arch}; +use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions}; + +pub fn target() -> Target { + let arch = Arch::Arm64_sim; + let mut base = opts("visionos", arch); + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD; + + Target { + llvm_target: visionos_sim_llvm_target(arch).into(), + metadata: crate::spec::TargetMetadata { + description: Some("ARM64 Apple visionOS simulator".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(), + arch: arch.target_arch(), + options: TargetOptions { + features: "+neon,+fp-armv8,+apple-a16".into(), + max_atomic_width: Some(128), + frame_pointer: FramePointer::NonLeaf, + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs index 2169e2971d885..703a3206af21f 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI + | SanitizerSet::KCFI | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::MEMTAG diff --git a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs index 9bf09b4376d1c..f842a834c05bf 100644 --- a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs @@ -1,10 +1,11 @@ -use crate::spec::base::apple::{opts, Arch}; +use crate::spec::base::apple::{opts, watchos_llvm_target, Arch}; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { - let base = opts("watchos", Arch::Arm64_32); + let arch = Arch::Arm64_32; + let base = opts("watchos", arch); Target { - llvm_target: "arm64_32-apple-watchos".into(), + llvm_target: watchos_llvm_target(arch).into(), metadata: crate::spec::TargetMetadata { description: None, tier: None, diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs index d9ebc7fbc1ae8..2f86506e2d05b 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs @@ -22,7 +22,7 @@ pub fn target() -> Target { linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, - features: "+vfp3,-d32,-fp16".into(), + features: "+vfp3d16".into(), max_atomic_width: Some(64), emit_debug_gdb_scripts: false, // GCC defaults to 8 for arm-none here. diff --git a/compiler/rustc_target/src/spec/targets/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7r_none_eabihf.rs index a8c622ccce28b..7c39d2d38de8e 100644 --- a/compiler/rustc_target/src/spec/targets/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7r_none_eabihf.rs @@ -21,7 +21,7 @@ pub fn target() -> Target { linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, - features: "+vfp3,-d32,-fp16".into(), + features: "+vfp3d16".into(), max_atomic_width: Some(64), emit_debug_gdb_scripts: false, // GCC defaults to 8 for arm-none here. 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 becd2fd7afb21..970b43ad109ba 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 b68316f2830ed..ae1a44e44a85a 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_target/src/spec/targets/riscv32ima_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs new file mode 100644 index 0000000000000..9acf6a3b5a0ee --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs @@ -0,0 +1,29 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + max_atomic_width: Some(32), + features: "+m,+a".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabihf.rs index 8c6bc6d72675a..bff812a5d5c73 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabihf.rs @@ -25,16 +25,15 @@ pub fn target() -> Target { options: TargetOptions { abi: "eabihf".into(), - // `+vfp4` is the lowest common denominator between the Cortex-M4 (vfp4-16) and the - // Cortex-M7 (vfp5) - // `-d32` both the Cortex-M4 and the Cortex-M7 only have 16 double-precision registers - // available - // `-fp64` The Cortex-M4 only supports single precision floating point operations - // whereas in the Cortex-M7 double precision is optional + // vfp4 is the lowest common denominator between the Cortex-M4F (vfp4) and the + // Cortex-M7 (vfp5). + // Both the Cortex-M4 and the Cortex-M7 only have 16 double-precision registers + // available, and the Cortex-M4 only supports single-precision floating point operations + // whereas in the Cortex-M7 double-precision is optional. // // Reference: // ARMv7-M Architecture Reference Manual - A2.5 The optional floating-point extension - features: "+vfp4,-d32,-fp64".into(), + features: "+vfp4d16sp".into(), max_atomic_width: Some(32), ..base::thumb::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabihf.rs index 2fef08261e1e6..88796e7a756b0 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabihf.rs @@ -22,8 +22,7 @@ pub fn target() -> Target { // processor, the Cortex-M33 Technical Reference Manual states that // the FPU uses the FPv5 architecture, single-precision instructions // and 16 D registers. - // These parameters map to the following LLVM features. - features: "+fp-armv8,-fp64,-d32".into(), + features: "+fp-armv8d16sp".into(), max_atomic_width: Some(32), ..base::thumb::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs index e743a18ce80b2..23f4772c39cb5 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs @@ -10,23 +10,12 @@ //! This target is more or less managed by the Rust and WebAssembly Working //! Group nowadays at . -use crate::spec::abi::Abi; use crate::spec::{base, Cc, LinkerFlavor, Target}; pub fn target() -> Target { let mut options = base::wasm::options(); options.os = "unknown".into(); - // This is a default for backwards-compatibility with the original - // definition of this target oh-so-long-ago. Once the "wasm" ABI is - // stable and the wasm-bindgen project has switched to using it then there's - // no need for this and it can be removed. - // - // Currently this is the reason that this target's ABI is mismatched with - // clang's ABI. This means that, in the limit, you can't merge C and Rust - // code on this target due to this ABI mismatch. - options.default_adjusted_cabi = Some(Abi::Wasm); - options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::No), &[ diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index 98374023dc57f..11fb28a9aed7f 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -10,6 +10,7 @@ pub fn target() -> Target { base.static_position_independent_executables = true; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI + | SanitizerSet::KCFI | SanitizerSet::DATAFLOW | SanitizerSet::LEAK | SanitizerSet::MEMORY diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index c003982278e57..1b507bb2a155e 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -279,9 +279,9 @@ const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[ ("d", Unstable(sym::riscv_target_feature)), ("e", Unstable(sym::riscv_target_feature)), ("f", Unstable(sym::riscv_target_feature)), - ("fast-unaligned-access", Unstable(sym::riscv_target_feature)), ("m", Stable), ("relax", Unstable(sym::riscv_target_feature)), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature)), ("v", Unstable(sym::riscv_target_feature)), ("zba", Stable), ("zbb", Stable), @@ -312,14 +312,15 @@ const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[ const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start ("atomics", Unstable(sym::wasm_target_feature)), - ("bulk-memory", Unstable(sym::wasm_target_feature)), + ("bulk-memory", Stable), ("exception-handling", Unstable(sym::wasm_target_feature)), + ("extended-const", Stable), ("multivalue", Unstable(sym::wasm_target_feature)), - ("mutable-globals", Unstable(sym::wasm_target_feature)), - ("nontrapping-fptoint", Unstable(sym::wasm_target_feature)), + ("mutable-globals", Stable), + ("nontrapping-fptoint", Stable), ("reference-types", Unstable(sym::wasm_target_feature)), ("relaxed-simd", Unstable(sym::wasm_target_feature)), - ("sign-ext", Unstable(sym::wasm_target_feature)), + ("sign-ext", Stable), ("simd128", Stable), // tidy-alphabetical-end ]; diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 0dcba0e05f76f..f96bd98523774 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -19,6 +19,9 @@ trait_selection_closure_kind_mismatch = expected a closure that implements the ` trait_selection_closure_kind_requirement = the requirement to implement `{$trait_prefix}{$expected}` derives from here +trait_selection_disallowed_positional_argument = positional format arguments are not allowed here + .help = only named format arguments with the name of one of the generic types are allowed in this context + trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries} trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` @@ -30,6 +33,9 @@ trait_selection_ignored_diagnostic_option = `{$option_name}` is ignored due to p trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}` +trait_selection_invalid_format_specifier = invalid format specifier + .help = no format specifier are supported in this position + trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]` .label = invalid on-clause here @@ -60,3 +66,6 @@ trait_selection_unable_to_construct_constant_value = unable to construct a const trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}` .help = expect either a generic argument name or {"`{Self}`"} as format argument + +trait_selection_wrapped_parser_error = {$description} + .label = {$label} diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index b126062102eda..7228a9ba01672 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -3,7 +3,7 @@ use rustc_errors::{ codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, SubdiagMessageOp, Subdiagnostic, }; -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty}; use rustc_span::{Span, Symbol}; @@ -104,7 +104,7 @@ impl Subdiagnostic for AdjustSignatureBorrow { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, - _f: F, + _f: &F, ) { match self { AdjustSignatureBorrow::Borrow { to_borrow } => { diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index f694dd0070363..b2400cec42f16 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,10 +1,9 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, DefiningAnchor, ObligationCtxt, SelectionContext}; - -use crate::traits::TraitEngineExt as _; +use crate::traits::{self, ObligationCtxt, SelectionContext, TraitEngineExt as _}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt as _}; +use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::traits::query::NoSolution; @@ -49,8 +48,7 @@ impl<'tcx> InferCtxt<'tcx> { /// - the parameter environment /// /// Invokes `evaluate_obligation`, so in the event that evaluating - /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent - /// (or EvaluatedToAmbigStackDependent) will be returned. + /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned. #[instrument(level = "debug", skip(self, params), ret)] fn type_implements_trait( &self, @@ -133,9 +131,8 @@ impl<'tcx> InferCtxtBuilder<'tcx> { R: Debug + TypeFoldable>, Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>, { - let (infcx, key, canonical_inference_vars) = self - .with_opaque_type_inference(DefiningAnchor::Bubble) - .build_with_canonical(DUMMY_SP, canonical_key); + let (infcx, key, canonical_inference_vars) = + self.build_with_canonical(DUMMY_SP, canonical_key); let ocx = ObligationCtxt::new(&infcx); let value = operation(&ocx, key)?; ocx.make_canonicalized_query_response(canonical_inference_vars, value) diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index fd3d62d1321e2..f1f03b810a961 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -17,29 +17,21 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![feature(assert_matches)] -#![feature(associated_type_bounds)] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extract_if)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(option_take_if)] #![feature(never_type)] #![feature(type_alias_impl_trait)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![recursion_limit = "512"] // For rustdoc -#[macro_use] -extern crate rustc_macros; -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -#[macro_use] -extern crate rustc_data_structures; #[macro_use] extern crate tracing; #[macro_use] extern crate rustc_middle; -#[macro_use] -extern crate smallvec; pub mod errors; pub mod infer; diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 222d0b4d5e75d..5e0d7da4f06b1 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,5 +1,6 @@ use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, RegionResolutionError}; +use rustc_macros::extension; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::ObligationCause; diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 67657c81cf602..f2c441dcbeda6 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -2,8 +2,8 @@ //! Doing this via a separate goal is called "deferred alias relation" and part //! of our more general approach to "lazy normalization". //! -//! This is done by first normalizing both sides of the goal, ending up in -//! either a concrete type, rigid alias, or an infer variable. +//! This is done by first structurally normalizing both sides of the goal, ending +//! up in either a concrete type, rigid alias, or an infer variable. //! These are related further according to the rules below: //! //! (1.) If we end up with two rigid aliases, then we relate them structurally. @@ -14,17 +14,10 @@ //! //! (3.) Otherwise, if we end with two rigid (non-projection) or infer types, //! relate them structurally. -//! -//! Subtle: when relating an opaque to another type, we emit a -//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque. -//! This nested goal starts out as ambiguous and does not actually define the opaque. -//! However, if `?fresh_var` ends up geteting equated to another type, we retry the -//! `NormalizesTo` goal, at which point the opaque is actually defined. -use super::{EvalCtxt, GoalSource}; -use rustc_infer::traits::query::NoSolution; +use super::EvalCtxt; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] @@ -35,21 +28,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let tcx = self.tcx(); let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; - let Some(lhs) = self.try_normalize_term(param_env, lhs)? else { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true)); + // Structurally normalize the lhs. + let lhs = if let Some(alias) = lhs.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(lhs); + self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term })); + term + } else { + lhs }; - let Some(rhs) = self.try_normalize_term(param_env, rhs)? else { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true)); + // Structurally normalize the rhs. + let rhs = if let Some(alias) = rhs.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(rhs); + self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term })); + term + } else { + rhs }; + // Apply the constraints. + self.try_evaluate_added_goals()?; + let lhs = self.resolve_vars_if_possible(lhs); + let rhs = self.resolve_vars_if_possible(rhs); + debug!(?lhs, ?rhs); + let variance = match direction { ty::AliasRelationDirection::Equate => ty::Variance::Invariant, ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, }; - match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { (None, None) => { self.relate(param_env, lhs, variance, rhs)?; @@ -57,14 +63,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } (Some(alias), None) => { - self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs) + self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + (None, Some(alias)) => { + self.relate_rigid_alias_non_alias( + param_env, + alias, + variance.xform(ty::Variance::Contravariant), + lhs, + )?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - (None, Some(alias)) => self.relate_rigid_alias_non_alias( - param_env, - alias, - variance.xform(ty::Variance::Contravariant), - lhs, - ), (Some(alias_lhs), Some(alias_rhs)) => { self.relate(param_env, alias_lhs, variance, alias_rhs)?; @@ -72,96 +82,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } } - - /// Relate a rigid alias with another type. This is the same as - /// an ordinary relate except that we treat the outer most alias - /// constructor as rigid. - #[instrument(level = "debug", skip(self, param_env), ret)] - fn relate_rigid_alias_non_alias( - &mut self, - param_env: ty::ParamEnv<'tcx>, - alias: ty::AliasTy<'tcx>, - variance: ty::Variance, - term: ty::Term<'tcx>, - ) -> QueryResult<'tcx> { - // NOTE: this check is purely an optimization, the structural eq would - // always fail if the term is not an inference variable. - if term.is_infer() { - let tcx = self.tcx(); - // We need to relate `alias` to `term` treating only the outermost - // constructor as rigid, relating any contained generic arguments as - // normal. We do this by first structurally equating the `term` - // with the alias constructor instantiated with unconstrained infer vars, - // and then relate this with the whole `alias`. - // - // Alternatively we could modify `Equate` for this case by adding another - // variant to `StructurallyRelateAliases`. - let identity_args = self.fresh_args_for_item(alias.def_id); - let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args); - self.eq_structurally_relating_aliases(param_env, term, rigid_ctor.to_ty(tcx).into())?; - self.eq(param_env, alias, rigid_ctor)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - Err(NoSolution) - } - } - - // FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var. - /// Normalize the `term` to equate it later. - #[instrument(level = "debug", skip(self, param_env), ret)] - fn try_normalize_term( - &mut self, - param_env: ty::ParamEnv<'tcx>, - term: ty::Term<'tcx>, - ) -> Result>, NoSolution> { - match term.unpack() { - ty::TermKind::Ty(ty) => { - Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into)) - } - ty::TermKind::Const(_) => { - if let Some(alias) = term.to_alias_ty(self.tcx()) { - let term = self.next_term_infer_of_kind(term); - self.add_goal( - GoalSource::Misc, - Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }), - ); - self.try_evaluate_added_goals()?; - Ok(Some(self.resolve_vars_if_possible(term))) - } else { - Ok(Some(term)) - } - } - } - } - - #[instrument(level = "debug", skip(self, param_env), ret)] - fn try_normalize_ty_recur( - &mut self, - param_env: ty::ParamEnv<'tcx>, - depth: usize, - ty: Ty<'tcx>, - ) -> Option> { - if !self.tcx().recursion_limit().value_within_limit(depth) { - return None; - } - - let ty::Alias(_, alias) = *ty.kind() else { - return Some(ty); - }; - - match self.commit_if_ok(|this| { - let normalized_ty = this.next_ty_infer(); - let normalizes_to_goal = Goal::new( - this.tcx(), - param_env, - ty::NormalizesTo { alias, term: normalized_ty.into() }, - ); - this.add_goal(GoalSource::Misc, normalizes_to_goal); - this.try_evaluate_added_goals()?; - Ok(this.resolve_vars_if_possible(normalized_ty)) - }) { - Ok(ty) => self.try_normalize_ty_recur(param_env, depth + 1, ty), - Err(NoSolution) => Some(ty), - } - } } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 9c7fa5216d766..280975f63bd3d 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -1,8 +1,7 @@ //! Code shared by trait and projection goals for candidate assembly. -use super::{EvalCtxt, SolverMode}; use crate::solve::GoalSource; -use crate::traits::coherence; +use crate::solve::{inspect, EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::inspect::ProbeKind; @@ -16,6 +15,7 @@ use rustc_middle::ty::{fast_reject, TypeFoldable}; use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, DUMMY_SP}; use std::fmt::Debug; +use std::mem; pub(super) mod structural_traits; @@ -47,21 +47,23 @@ pub(super) trait GoalKind<'tcx>: /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, + source: CandidateSource, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// Consider a clause, which consists of a "assumption" and some "requirements", /// to satisfy a goal. If the requirements hold, then attempt to satisfy our /// goal by equating it with the assumption. - fn consider_implied_clause( + fn probe_and_consider_implied_clause( ecx: &mut EvalCtxt<'_, 'tcx>, + source: CandidateSource, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, requirements: impl IntoIterator>>, - ) -> QueryResult<'tcx> { - Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + ) -> Result, NoSolution> { + Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { // FIXME(-Znext-solver=coinductive): check whether this should be // `GoalSource::ImplWhereBound` for any caller. ecx.add_goals(GoalSource::Misc, requirements); @@ -72,15 +74,16 @@ pub(super) trait GoalKind<'tcx>: /// Consider a clause specifically for a `dyn Trait` self type. This requires /// additionally checking all of the supertraits and object bounds to hold, /// since they're not implied by the well-formedness of the object type. - fn consider_object_bound_candidate( + fn probe_and_consider_object_bound_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, + source: CandidateSource, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, - ) -> QueryResult<'tcx> { - Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + ) -> Result, NoSolution> { + Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { let tcx = ecx.tcx(); let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { - bug!("expected object type in `consider_object_bound_candidate`"); + bug!("expected object type in `probe_and_consider_object_bound_candidate`"); }; // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? ecx.add_goals( @@ -111,7 +114,7 @@ pub(super) trait GoalKind<'tcx>: fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, guar: ErrorGuaranteed, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A type implements an `auto trait` if its components do as well. /// @@ -120,22 +123,22 @@ pub(super) trait GoalKind<'tcx>: fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A trait alias holds if the RHS traits and `where` clauses hold. fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; - /// A type is `Copy` or `Clone` if its components are `Sized`. + /// A type is `Sized` if its tail component is `Sized`. /// /// These components are given by built-in rules from /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`]. fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. /// @@ -144,20 +147,20 @@ pub(super) trait GoalKind<'tcx>: fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A type is `PointerLike` if we can compute its layout, and that layout /// matches the layout of `usize`. fn consider_builtin_pointer_like_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A type is a `FnPtr` if it is of `FnPtr` type. fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn` /// family of traits where `A` is given by the signature of the type. @@ -165,7 +168,7 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, kind: ty::ClosureKind, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// An async closure is known to implement the `AsyncFn` family of traits /// where `A` is given by the signature of the type. @@ -173,7 +176,7 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, kind: ty::ClosureKind, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// Compute the built-in logic of the `AsyncFnKindHelper` helper trait, which /// is used internally to delay computation for async closures until after @@ -181,13 +184,13 @@ pub(super) trait GoalKind<'tcx>: fn consider_builtin_async_fn_kind_helper_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// `Tuple` is implemented if the `Self` type is a tuple. fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// `Pointee` is always implemented. /// @@ -197,7 +200,7 @@ pub(super) trait GoalKind<'tcx>: fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A coroutine (that comes from an `async` desugaring) is known to implement /// `Future`, where `O` is given by the coroutine's return type @@ -205,7 +208,7 @@ pub(super) trait GoalKind<'tcx>: fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A coroutine (that comes from a `gen` desugaring) is known to implement /// `Iterator`, where `O` is given by the generator's yield type @@ -213,12 +216,19 @@ pub(super) trait GoalKind<'tcx>: fn consider_builtin_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; + + /// A coroutine (that comes from a `gen` desugaring) is known to implement + /// `FusedIterator` + fn consider_builtin_fused_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Result, NoSolution>; fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to /// implement `Coroutine`, given the resume, yield, @@ -226,22 +236,27 @@ pub(super) trait GoalKind<'tcx>: fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; + + fn consider_builtin_async_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Result, NoSolution>; fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; fn consider_builtin_transmute_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; + ) -> Result, NoSolution>; /// Consider (possibly several) candidates to upcast or unsize a type to another /// type, excluding the coercion of a sized type into a `dyn Trait`. @@ -253,7 +268,7 @@ pub(super) trait GoalKind<'tcx>: fn consider_structural_builtin_unsize_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; + ) -> Vec>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -269,7 +284,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if normalized_self_ty.is_ty_var() { debug!("self type has been normalized to infer"); - return self.forced_ambiguity(MaybeCause::Ambiguity); + return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect(); } let goal = @@ -302,14 +317,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates } - fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec> { + pub(super) fn forced_ambiguity( + &mut self, + cause: MaybeCause, + ) -> Result, NoSolution> { + // This may fail if `try_evaluate_added_goals` overflows because it + // fails to reach a fixpoint but ends up getting an error after + // running for some additional step. + // + // cc trait-system-refactor-initiative#105 let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); let certainty = Certainty::Maybe(cause); - let result = self.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(); - let mut dummy_probe = self.inspect.new_probe(); - dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) }); - self.inspect.finish_probe(dummy_probe); - vec![Candidate { source, result }] + self.probe_trait_candidate(source) + .enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty)) } #[instrument(level = "debug", skip_all)] @@ -349,8 +369,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -497,12 +518,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_future_candidate(self, goal) } else if lang_items.iterator_trait() == Some(trait_def_id) { G::consider_builtin_iterator_candidate(self, goal) + } else if lang_items.fused_iterator_trait() == Some(trait_def_id) { + G::consider_builtin_fused_iterator_candidate(self, goal) } else if lang_items.async_iterator_trait() == Some(trait_def_id) { G::consider_builtin_async_iterator_candidate(self, goal) } else if lang_items.coroutine_trait() == Some(trait_def_id) { G::consider_builtin_coroutine_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { G::consider_builtin_discriminant_kind_candidate(self, goal) + } else if lang_items.async_destruct_trait() == Some(trait_def_id) { + G::consider_builtin_async_destruct_candidate(self, goal) } else if lang_items.destruct_trait() == Some(trait_def_id) { G::consider_builtin_destruct_candidate(self, goal) } else if lang_items.transmute_trait() == Some(trait_def_id) { @@ -511,20 +536,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Err(NoSolution) }; - match result { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), - result, - }), - Err(NoSolution) => (), - } + candidates.extend(result); // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar + Bar` and `dyn Foo: Unsize>` if lang_items.unsize_trait() == Some(trait_def_id) { - for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result }); - } + candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal)); } } @@ -535,12 +552,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec>, ) { for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { - match G::consider_implied_clause(self, goal, assumption, []) { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result }) - } - Err(NoSolution) => (), - } + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::ParamEnv(i), + goal, + assumption, + [], + )); } } @@ -580,8 +598,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -625,12 +644,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { for assumption in self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args) { - match G::consider_implied_clause(self, goal, assumption, []) { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::AliasBound, result }); - } - Err(NoSolution) => {} - } + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); } if kind != ty::Projection { @@ -668,8 +688,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -704,17 +725,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } ty::ExistentialPredicate::Projection(_) | ty::ExistentialPredicate::AutoTrait(_) => { - match G::consider_object_bound_candidate( + candidates.extend(G::probe_and_consider_object_bound_candidate( self, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, bound.with_self_ty(tcx, self_ty), - ) { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), - result, - }), - Err(NoSolution) => (), - } + )); } } } @@ -725,15 +741,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if let Some(principal) = bounds.principal() { let principal_trait_ref = principal.with_self_ty(tcx, self_ty); self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| { - match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object { - vtable_base, - }), - result, - }), - Err(NoSolution) => (), - } + candidates.extend(G::probe_and_consider_object_bound_candidate( + ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base }), + goal, + assumption.to_predicate(tcx), + )); }); } } @@ -751,25 +764,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec>, ) { let tcx = self.tcx(); - let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| { - let trait_ref = goal.predicate.trait_ref(tcx); - let lazily_normalize_ty = |ty| ecx.structurally_normalize_ty(goal.param_env, ty); - match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty)? { - Ok(()) => Err(NoSolution), - Err(_) => { + candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter( + |ecx| { + let trait_ref = goal.predicate.trait_ref(tcx); + if ecx.trait_ref_is_knowable(goal.param_env, trait_ref)? { + Err(NoSolution) + } else { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - } - }); - - match result { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), - result, - }), - Err(NoSolution) => {} - } + }, + )) } /// If there's a where-bound for the current goal, do not use any impl candidates @@ -787,6 +792,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, candidates: &mut Vec>, ) { + // HACK: We temporarily remove the `ProofTreeBuilder` to + // avoid adding `Trait` candidates to the candidates used + // to prove the current goal. + let inspect = mem::replace(&mut self.inspect, inspect::ProofTreeBuilder::new_noop()); + let tcx = self.tcx(); let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> = goal.with(tcx, goal.predicate.trait_ref(tcx)); @@ -809,6 +819,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { false } CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true, + CandidateSource::CoherenceUnknowable => bug!("uh oh"), }); } // If it is still ambiguous we instead just force the whole goal @@ -816,10 +827,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs Certainty::Maybe(cause) => { debug!(?cause, "force ambiguity"); - *candidates = self.forced_ambiguity(cause); + *candidates = self.forced_ambiguity(cause).into_iter().collect(); } } } + self.inspect = inspect; } /// If there are multiple ways to prove a trait or projection goal, we have diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 2bfb86b592b0d..a8b1a182d3c23 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -4,9 +4,10 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::LangItem; use rustc_hir::{def_id::DefId, Movability, Mutability}; use rustc_infer::traits::query::NoSolution; +use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::{ - self, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, }; use rustc_span::sym; @@ -46,11 +47,13 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( bug!("unexpected type `{ty}`") } - ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => { Ok(vec![ty::Binder::dummy(element_ty)]) } - ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![ty::Binder::dummy(element_ty)]), + ty::Pat(element_ty, _) | ty::Array(element_ty, _) | ty::Slice(element_ty) => { + Ok(vec![ty::Binder::dummy(element_ty)]) + } ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet @@ -73,8 +76,8 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::CoroutineWitness(def_id, args) => Ok(ecx .tcx() - .coroutine_hidden_types(def_id) - .map(|bty| replace_erased_lifetimes_with_bound_vars(tcx, bty.instantiate(tcx, args))) + .bound_coroutine_hidden_types(def_id) + .map(|bty| bty.instantiate(tcx, args)) .collect()), // For `PhantomData`, we pass `T`. @@ -93,27 +96,6 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( } } -pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> ty::Binder<'tcx, Ty<'tcx>> { - debug_assert!(!ty.has_bound_regions()); - let mut counter = 0; - let ty = tcx.fold_regions(ty, |r, current_depth| match r.kind() { - ty::ReErased => { - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon }; - counter += 1; - ty::Region::new_bound(tcx, current_depth, br) - } - // All free regions should be erased here. - r => bug!("unexpected region: {r:?}"), - }); - let bound_vars = tcx.mk_bound_variable_kinds_from_iter( - (0..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon)), - ); - ty::Binder::bind_with_vars(ty, bound_vars) -} - #[instrument(level = "debug", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ecx: &EvalCtxt<'_, 'tcx>, @@ -135,6 +117,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Array(..) + | ty::Pat(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never @@ -154,13 +137,25 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( bug!("unexpected type `{ty}`") } - // impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized - ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()), - - // impl Sized for Adt where T: Sized forall T in field types + // impl Sized for () + // impl Sized for (T1, T2, .., Tn) where Tn: Sized if n >= 1 + ty::Tuple(tys) => Ok(tys.last().map_or_else(Vec::new, |&ty| vec![ty::Binder::dummy(ty)])), + + // impl Sized for Adt where sized_constraint(Adt): Sized + // `sized_constraint(Adt)` is the deepest struct trail that can be determined + // by the definition of `Adt`, independent of the generic args. + // impl Sized for Adt if sized_constraint(Adt) == None + // As a performance optimization, `sized_constraint(Adt)` can return `None` + // if the ADTs definition implies that it is sized by for all possible args. + // In this case, the builtin impl will have no nested subgoals. This is a + // "best effort" optimization and `sized_constraint` may return `Some`, even + // if the ADT is sized for all possible args. ty::Adt(def, args) => { - let sized_crit = def.sized_constraint(ecx.tcx()); - Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect()) + if let Some(sized_crit) = def.sized_constraint(ecx.tcx()) { + Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.tcx(), args))]) + } else { + Ok(vec![]) + } } } } @@ -186,6 +181,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( | ty::Ref(_, _, Mutability::Not) | ty::Array(..) => Err(NoSolution), + // Cannot implement in core, as we can't be generic over patterns yet, + // so we'd have to list all patterns and type combinations. + ty::Pat(ty, ..) => Ok(vec![ty::Binder::dummy(ty)]), + ty::Dynamic(..) | ty::Str | ty::Slice(_) @@ -229,13 +228,8 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types ty::CoroutineWitness(def_id, args) => Ok(ecx .tcx() - .coroutine_hidden_types(def_id) - .map(|bty| { - replace_erased_lifetimes_with_bound_vars( - ecx.tcx(), - bty.instantiate(ecx.tcx(), args), - ) - }) + .bound_coroutine_hidden_types(def_id) + .map(|bty| bty.instantiate(ecx.tcx(), args)) .collect()), } } @@ -299,7 +293,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( let kind_ty = args.kind_ty(); let sig = args.coroutine_closure_sig().skip_binder(); - let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() { + let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() + && !args.tupled_upvars_ty().is_ty_var() + { if !closure_kind.extends(goal_kind) { return Err(NoSolution); } @@ -354,13 +350,14 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Dynamic(_, _, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(_) + | ty::Pat(_, _) | ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) @@ -407,7 +404,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc let kind_ty = args.kind_ty(); let sig = args.coroutine_closure_sig().skip_binder(); let mut nested = vec![]; - let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() { + let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() + && !args.tupled_upvars_ty().is_ty_var() + { if !closure_kind.extends(goal_kind) { return Err(NoSolution); } @@ -540,8 +539,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Dynamic(_, _, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 251b0a193f1bb..6722abd709c31 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -9,6 +9,7 @@ //! //! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; +use crate::solve::eval_ctxt::NestedGoals; use crate::solve::{ inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response, }; @@ -18,8 +19,12 @@ use rustc_infer::infer::canonical::query_response::make_query_region_constraints use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; use rustc_infer::infer::resolve::EagerResolver; +use rustc_infer::infer::type_variable::TypeVariableOrigin; +use rustc_infer::infer::RegionVariableOrigin; use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::traits::solve::NestedNormalizationGoals; use rustc_middle::infer::canonical::Canonical; +use rustc_middle::infer::unify_key::ConstVariableOrigin; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, @@ -27,7 +32,8 @@ use rustc_middle::traits::solve::{ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer}; -use rustc_span::DUMMY_SP; +use rustc_span::{Span, DUMMY_SP}; +use std::assert_matches::assert_matches; use std::iter; use std::ops::Deref; @@ -65,7 +71,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut orig_values, QueryInput { goal, - anchor: self.infcx.defining_use_anchor, predefined_opaques_in_body: self .tcx() .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), @@ -93,13 +98,33 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { previous call to `try_evaluate_added_goals!`" ); - let certainty = certainty.unify_with(goals_certainty); - - let var_values = self.var_values; - let external_constraints = self.compute_external_query_constraints()?; + self.inspect.make_canonical_response(certainty); + // When normalizing, we've replaced the expected term with an unconstrained + // inference variable. This means that we dropped information which could + // have been important. We handle this by instead returning the nested goals + // to the caller, where they are then handled. + // + // As we return all ambiguous nested goals, we can ignore the certainty returned + // by `try_evaluate_added_goals()`. + let (certainty, normalization_nested_goals) = if self.is_normalizes_to_goal { + let NestedGoals { normalizes_to_goals, goals } = std::mem::take(&mut self.nested_goals); + if cfg!(debug_assertions) { + assert!(normalizes_to_goals.is_empty()); + if goals.is_empty() { + assert_matches!(goals_certainty, Certainty::Yes); + } + } + (certainty, NestedNormalizationGoals(goals)) + } else { + let certainty = certainty.unify_with(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + }; + + let external_constraints = + self.compute_external_query_constraints(normalization_nested_goals)?; let (var_values, mut external_constraints) = - (var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx)); + (self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx)); // Remove any trivial region constraints once we've resolved regions external_constraints .region_constraints @@ -146,6 +171,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn compute_external_query_constraints( &self, + normalization_nested_goals: NestedNormalizationGoals<'tcx>, ) -> Result, NoSolution> { // We only check for leaks from universes which were entered inside // of the query. @@ -176,7 +202,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) }); - Ok(ExternalConstraintsData { region_constraints, opaque_types }) + Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }) } /// After calling a canonical query, we apply the constraints returned @@ -185,13 +211,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// This happens in three steps: /// - we instantiate the bound variables of the query response /// - we unify the `var_values` of the response with the `original_values` - /// - we apply the `external_constraints` returned by the query + /// - we apply the `external_constraints` returned by the query, returning + /// the `normalization_nested_goals` pub(super) fn instantiate_and_apply_query_response( &mut self, param_env: ty::ParamEnv<'tcx>, original_values: Vec>, response: CanonicalResponse<'tcx>, - ) -> Certainty { + ) -> (NestedNormalizationGoals<'tcx>, Certainty) { let instantiation = Self::compute_query_response_instantiation_values( self.infcx, &original_values, @@ -203,11 +230,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values); - let ExternalConstraintsData { region_constraints, opaque_types } = - external_constraints.deref(); + let ExternalConstraintsData { + region_constraints, + opaque_types, + normalization_nested_goals, + } = external_constraints.deref(); self.register_region_constraints(region_constraints); self.register_new_opaque_types(param_env, opaque_types); - certainty + (normalization_nested_goals.clone(), certainty) } /// This returns the canoncial variable values to instantiate the bound variables of @@ -306,7 +336,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// whether an alias is rigid by using the trait solver. When instantiating a response /// from the solver we assume that the solver correctly handled aliases and therefore /// always relate them structurally here. - #[instrument(level = "debug", skip(infcx), ret)] + #[instrument(level = "debug", skip(infcx))] fn unify_query_var_values( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -349,36 +379,70 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } -impl<'tcx> inspect::ProofTreeBuilder<'tcx> { - pub fn make_canonical_state>>( - ecx: &EvalCtxt<'_, 'tcx>, - data: T, - ) -> inspect::CanonicalState<'tcx, T> { - let state = inspect::State { var_values: ecx.var_values, data }; - let state = state.fold_with(&mut EagerResolver::new(ecx.infcx)); - Canonicalizer::canonicalize( - ecx.infcx, - CanonicalizeMode::Response { max_input_universe: ecx.max_input_universe }, - &mut vec![], - state, - ) +/// Used by proof trees to be able to recompute intermediate actions while +/// evaluating a goal. The `var_values` not only include the bound variables +/// of the query input, but also contain all unconstrained inference vars +/// created while evaluating this goal. +pub(in crate::solve) fn make_canonical_state<'tcx, T: TypeFoldable>>( + infcx: &InferCtxt<'tcx>, + var_values: &[ty::GenericArg<'tcx>], + max_input_universe: ty::UniverseIndex, + data: T, +) -> inspect::CanonicalState<'tcx, T> { + let var_values = CanonicalVarValues { var_values: infcx.tcx.mk_args(var_values) }; + let state = inspect::State { var_values, data }; + let state = state.fold_with(&mut EagerResolver::new(infcx)); + Canonicalizer::canonicalize( + infcx, + CanonicalizeMode::Response { max_input_universe }, + &mut vec![], + state, + ) +} + +/// Instantiate a `CanonicalState`. +/// +/// Unlike for query responses, `CanonicalState` also track fresh inference +/// variables created while evaluating a goal. When creating two separate +/// `CanonicalState` during a single evaluation both may reference this +/// fresh inference variable. When instantiating them we now create separate +/// inference variables for it and have to unify them somehow. We do this +/// by extending the `var_values` while building the proof tree. +/// +/// This currently assumes that unifying the var values trivially succeeds. +/// Adding any inference constraints which weren't present when originally +/// computing the canonical query can result in bugs. +pub(in crate::solve) fn instantiate_canonical_state<'tcx, T: TypeFoldable>>( + infcx: &InferCtxt<'tcx>, + span: Span, + param_env: ty::ParamEnv<'tcx>, + orig_values: &mut Vec>, + state: inspect::CanonicalState<'tcx, T>, +) -> T { + // In case any fresh inference variables have been created between `state` + // and the previous instantiation, extend `orig_values` for it. + assert!(orig_values.len() <= state.value.var_values.len()); + for i in orig_values.len()..state.value.var_values.len() { + let unconstrained = match state.value.var_values.var_values[i].unpack() { + ty::GenericArgKind::Lifetime(_) => { + infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)).into() + } + ty::GenericArgKind::Type(_) => { + infcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span }).into() + } + ty::GenericArgKind::Const(ct) => infcx + .next_const_var(ct.ty(), ConstVariableOrigin { param_def_id: None, span }) + .into(), + }; + + orig_values.push(unconstrained); } - /// Instantiate a `CanonicalState`. This assumes that unifying the var values - /// trivially succeeds. Adding any inference constraints which weren't present when - /// originally computing the canonical query can result in bugs. - pub fn instantiate_canonical_state>>( - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - original_values: &[ty::GenericArg<'tcx>], - state: inspect::CanonicalState<'tcx, T>, - ) -> T { - let instantiation = - EvalCtxt::compute_query_response_instantiation_values(infcx, original_values, &state); + let instantiation = + EvalCtxt::compute_query_response_instantiation_values(infcx, orig_values, &state); - let inspect::State { var_values, data } = state.instantiate(infcx.tcx, &instantiation); + let inspect::State { var_values, data } = state.instantiate(infcx.tcx, &instantiation); - EvalCtxt::unify_query_var_values(infcx, param_env, original_values, var_values); - data - } + EvalCtxt::unify_query_var_values(infcx, param_env, orig_values, var_values); + data } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs deleted file mode 100644 index 67b6801059af7..0000000000000 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs +++ /dev/null @@ -1,45 +0,0 @@ -use super::{EvalCtxt, NestedGoals}; -use crate::solve::inspect; -use rustc_middle::traits::query::NoSolution; - -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { - pub(in crate::solve) fn commit_if_ok( - &mut self, - f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result, - ) -> Result { - let mut nested_ecx = EvalCtxt { - infcx: self.infcx, - variables: self.variables, - var_values: self.var_values, - predefined_opaques_in_body: self.predefined_opaques_in_body, - max_input_universe: self.max_input_universe, - search_graph: self.search_graph, - nested_goals: NestedGoals::new(), - tainted: self.tainted, - inspect: self.inspect.new_probe(), - }; - - let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx)); - if result.is_ok() { - let EvalCtxt { - infcx: _, - variables: _, - var_values: _, - predefined_opaques_in_body: _, - max_input_universe: _, - search_graph: _, - nested_goals, - tainted, - inspect, - } = nested_ecx; - self.nested_goals.extend(nested_goals); - self.tainted = tainted; - self.inspect.integrate_snapshot(inspect); - } else { - nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk); - self.inspect.finish_probe(nested_ecx.inspect); - } - - result - } -} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs index 3b858cb449fa1..1710746ae504e 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -1,20 +1,21 @@ use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{ BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::MaybeCause; +use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals}; use rustc_infer::traits::ObligationCause; +use rustc_macros::{extension, HashStable}; use rustc_middle::infer::canonical::CanonicalVarInfos; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::infer::unify_key::ConstVariableOrigin; use rustc_middle::traits::solve::inspect; use rustc_middle::traits::solve::{ - CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques, - PredefinedOpaquesData, QueryResult, + CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaques, PredefinedOpaquesData, + QueryResult, }; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::{ @@ -24,9 +25,9 @@ use rustc_middle::ty::{ use rustc_session::config::DumpSolverProofTree; use rustc_span::DUMMY_SP; use std::io::Write; -use std::iter; use std::ops::ControlFlow; +use crate::traits::coherence; use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use super::inspect::ProofTreeBuilder; @@ -35,8 +36,7 @@ use super::{search_graph::SearchGraph, Goal}; use super::{GoalSource, SolverMode}; pub use select::InferCtxtSelectExt; -mod canonical; -mod commit_if_ok; +pub(super) mod canonical; mod probe; mod select; @@ -61,6 +61,14 @@ pub struct EvalCtxt<'a, 'tcx> { /// The variable info for the `var_values`, only used to make an ambiguous response /// with no constraints. variables: CanonicalVarInfos<'tcx>, + /// Whether we're currently computing a `NormalizesTo` goal. Unlike other goals, + /// `NormalizesTo` goals act like functions with the expected term always being + /// fully unconstrained. This would weaken inference however, as the nested goals + /// never get the inference constraints from the actual normalized-to type. Because + /// of this we return any ambiguous nested goals from `NormalizesTo` to the caller + /// when then adds these to its own context. The caller is always an `AliasRelate` + /// goal so this never leaks out of the solver. + is_normalizes_to_goal: bool, pub(super) var_values: CanonicalVarValues<'tcx>, predefined_opaques_in_body: PredefinedOpaques<'tcx>, @@ -78,7 +86,7 @@ pub struct EvalCtxt<'a, 'tcx> { pub(super) search_graph: &'a mut SearchGraph<'tcx>, - pub(super) nested_goals: NestedGoals<'tcx>, + nested_goals: NestedGoals<'tcx>, // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`? // @@ -91,9 +99,9 @@ pub struct EvalCtxt<'a, 'tcx> { pub(super) inspect: ProofTreeBuilder<'tcx>, } -#[derive(Debug, Clone)] +#[derive(Default, Debug, Clone)] pub(super) struct NestedGoals<'tcx> { - /// This normalizes-to goal that is treated specially during the evaluation + /// These normalizes-to goals are treated specially during the evaluation /// loop. In each iteration we take the RHS of the projection, replace it with /// a fresh inference variable, and only after evaluating that goal do we /// equate the fresh inference variable with the actual RHS of the predicate. @@ -101,27 +109,20 @@ pub(super) struct NestedGoals<'tcx> { /// This is both to improve caching, and to avoid using the RHS of the /// projection predicate to influence the normalizes-to candidate we select. /// - /// This is not a 'real' nested goal. We must not forget to replace the RHS - /// with a fresh inference variable when we evaluate this goal. That can result - /// in a trait solver cycle. This would currently result in overflow but can be - /// can be unsound with more powerful coinduction in the future. - pub(super) normalizes_to_hack_goal: Option>>, + /// Forgetting to replace the RHS with a fresh inference variable when we evaluate + /// this goal results in an ICE.. + pub(super) normalizes_to_goals: Vec>>, /// The rest of the goals which have not yet processed or remain ambiguous. pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>, } impl<'tcx> NestedGoals<'tcx> { pub(super) fn new() -> Self { - Self { normalizes_to_hack_goal: None, goals: Vec::new() } + Self { normalizes_to_goals: Vec::new(), goals: Vec::new() } } pub(super) fn is_empty(&self) -> bool { - self.normalizes_to_hack_goal.is_none() && self.goals.is_empty() - } - - pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) { - assert_eq!(other.normalizes_to_hack_goal, None); - self.goals.extend(other.goals) + self.normalizes_to_goals.is_empty() && self.goals.is_empty() } } @@ -155,10 +156,14 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.search_graph.solver_mode() } + pub(super) fn set_is_normalizes_to_goal(&mut self) { + self.is_normalizes_to_goal = true; + } + /// Creates a root evaluation context and search graph. This should only be /// used from outside of any evaluation, and other methods should be preferred /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]). - fn enter_root( + pub(super) fn enter_root( infcx: &InferCtxt<'tcx>, generate_proof_tree: GenerateProofTree, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R, @@ -167,8 +172,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut search_graph = search_graph::SearchGraph::new(mode); let mut ecx = EvalCtxt { - search_graph: &mut search_graph, infcx, + search_graph: &mut search_graph, nested_goals: NestedGoals::new(), inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), @@ -180,6 +185,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { max_input_universe: ty::UniverseIndex::ROOT, variables: ty::List::empty(), var_values: CanonicalVarValues::dummy(), + is_normalizes_to_goal: false, tainted: Ok(()), }; let result = f(&mut ecx); @@ -226,19 +232,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { .infer_ctxt() .intercrate(intercrate) .with_next_trait_solver(true) - .with_opaque_type_inference(canonical_input.value.anchor) .build_with_canonical(DUMMY_SP, &canonical_input); let mut ecx = EvalCtxt { infcx, variables: canonical_input.variables, var_values, + is_normalizes_to_goal: false, predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.max_universe, search_graph, nested_goals: NestedGoals::new(), tainted: Ok(()), - inspect: canonical_goal_evaluation.new_goal_evaluation_step(input), + inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values, input), }; for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { @@ -251,7 +257,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } let result = f(&mut ecx, input.goal); - + ecx.inspect.probe_final_state(ecx.infcx, ecx.max_input_universe); canonical_goal_evaluation.goal_evaluation_step(ecx.inspect); // When creating a query response we clone the opaque type constraints @@ -319,6 +325,27 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>, ) -> Result<(bool, Certainty), NoSolution> { + let (normalization_nested_goals, has_changed, certainty) = + self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?; + assert!(normalization_nested_goals.is_empty()); + Ok((has_changed, certainty)) + } + + /// Recursively evaluates `goal`, returning the nested goals in case + /// the nested goal is a `NormalizesTo` goal. + /// + /// As all other goal kinds do not return any nested goals and + /// `NormalizesTo` is only used by `AliasRelate`, all other callsites + /// should use [`EvalCtxt::evaluate_goal`] which discards that empty + /// storage. + // FIXME(-Znext-solver=coinduction): `_source` is currently unused but will + // be necessary once we implement the new coinduction approach. + pub(super) fn evaluate_goal_raw( + &mut self, + goal_evaluation_kind: GoalEvaluationKind, + _source: GoalSource, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> Result<(NestedNormalizationGoals<'tcx>, bool, Certainty), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); @@ -336,12 +363,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { Ok(response) => response, }; - let (certainty, has_changed) = self.instantiate_response_discarding_overflow( - goal.param_env, - source, - orig_values, - canonical_response, - ); + let (normalization_nested_goals, certainty, has_changed) = self + .instantiate_response_discarding_overflow( + goal.param_env, + orig_values, + canonical_response, + ); self.inspect.goal_evaluation(goal_evaluation); // FIXME: We previously had an assert here that checked that recomputing // a goal after applying its constraints did not change its response. @@ -353,47 +380,25 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // Once we have decided on how to handle trait-system-refactor-initiative#75, // we should re-add an assert here. - Ok((has_changed, certainty)) + Ok((normalization_nested_goals, has_changed, certainty)) } fn instantiate_response_discarding_overflow( &mut self, param_env: ty::ParamEnv<'tcx>, - source: GoalSource, original_values: Vec>, response: CanonicalResponse<'tcx>, - ) -> (Certainty, bool) { - // The old solver did not evaluate nested goals when normalizing. - // It returned the selection constraints allowing a `Projection` - // obligation to not hold in coherence while avoiding the fatal error - // from overflow. - // - // We match this behavior here by considering all constraints - // from nested goals which are not from where-bounds. We will already - // need to track which nested goals are required by impl where-bounds - // for coinductive cycles, so we simply reuse that here. - // - // While we could consider overflow constraints in more cases, this should - // not be necessary for backcompat and results in better perf. It also - // avoids a potential inconsistency which would otherwise require some - // tracking for root goals as well. See #119071 for an example. - let keep_overflow_constraints = || { - self.search_graph.current_goal_is_normalizes_to() - && source != GoalSource::ImplWhereBound - }; - - if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty - && !keep_overflow_constraints() - { - return (response.value.certainty, false); + ) -> (NestedNormalizationGoals<'tcx>, Certainty, bool) { + if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty { + return (NestedNormalizationGoals::empty(), response.value.certainty, false); } let has_changed = !response.value.var_values.is_identity_modulo_regions() || !response.value.external_constraints.opaque_types.is_empty(); - let certainty = + let (normalization_nested_goals, certainty) = self.instantiate_and_apply_query_response(param_env, original_values, response); - (certainty, has_changed) + (normalization_nested_goals, certainty, has_changed) } fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { @@ -455,13 +460,23 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self))] + pub(super) fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { + self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal); + self.nested_goals.normalizes_to_goals.push(goal); + } + + #[instrument(level = "debug", skip(self))] + pub(super) fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) { + self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal); + self.nested_goals.goals.push((source, goal)); + } + // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning // the certainty of all the goals. #[instrument(level = "debug", skip(self))] pub(super) fn try_evaluate_added_goals(&mut self) -> Result { - let inspect = self.inspect.new_evaluate_added_goals(); - let inspect = core::mem::replace(&mut self.inspect, inspect); - + self.inspect.start_evaluate_added_goals(); let mut response = Ok(Certainty::overflow(false)); for _ in 0..FIXPOINT_STEP_LIMIT { // FIXME: This match is a bit ugly, it might be nice to change the inspect @@ -479,15 +494,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } - self.inspect.eval_added_goals_result(response); + self.inspect.evaluate_added_goals_result(response); if response.is_err() { self.tainted = Err(NoSolution); } - let goal_evaluations = std::mem::replace(&mut self.inspect, inspect); - self.inspect.added_goals_evaluation(goal_evaluations); - response } @@ -496,19 +508,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> { let tcx = self.tcx(); - let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); - - self.inspect.evaluate_added_goals_loop_start(); - - fn with_misc_source<'tcx>( - it: impl IntoIterator>>, - ) -> impl Iterator>)> { - iter::zip(iter::repeat(GoalSource::Misc), it) - } + self.inspect.start_evaluate_added_goals_step(); + let mut goals = core::mem::take(&mut self.nested_goals); // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); - if let Some(goal) = goals.normalizes_to_hack_goal.take() { + for goal in goals.normalizes_to_goals { // Replace the goal with an unconstrained infer var, so the // RHS does not affect projection candidate assembly. let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); @@ -517,46 +522,56 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, ); - let (_, certainty) = self.evaluate_goal( - GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes }, + let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw( + GoalEvaluationKind::Nested, GoalSource::Misc, unconstrained_goal, )?; + // Add the nested goals from normalization to our own nested goals. + debug!(?nested_goals); + goals.goals.extend(nested_goals); // Finally, equate the goal's RHS with the unconstrained var. - // We put the nested goals from this into goals instead of - // next_goals to avoid needing to process the loop one extra - // time if this goal returns something -- I don't think this - // matters in practice, though. - let eq_goals = - self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?; - goals.goals.extend(with_misc_source(eq_goals)); + // + // SUBTLE: + // We structurally relate aliases here. This is necessary + // as we otherwise emit a nested `AliasRelate` goal in case the + // returned term is a rigid alias, resulting in overflow. + // + // It is correct as both `goal.predicate.term` and `unconstrained_rhs` + // start out as an unconstrained inference variable so any aliases get + // fully normalized when instantiating it. + // + // FIXME: Strictly speaking this may be incomplete if the normalized-to + // type contains an ambiguous alias referencing bound regions. We should + // consider changing this to only use "shallow structural equality". + self.eq_structurally_relating_aliases( + goal.param_env, + goal.predicate.term, + unconstrained_rhs, + )?; // We only look at the `projection_ty` part here rather than // looking at the "has changed" return from evaluate_goal, // because we expect the `unconstrained_rhs` part of the predicate // to have changed -- that means we actually normalized successfully! - if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) { + let with_resolved_vars = self.resolve_vars_if_possible(goal); + if goal.predicate.alias != with_resolved_vars.predicate.alias { unchanged_certainty = None; } match certainty { Certainty::Yes => {} Certainty::Maybe(_) => { - // We need to resolve vars here so that we correctly - // deal with `has_changed` in the next iteration. - self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal)); + self.nested_goals.normalizes_to_goals.push(with_resolved_vars); unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); } } } - for (source, goal) in goals.goals.drain(..) { - let (has_changed, certainty) = self.evaluate_goal( - GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No }, - source, - goal, - )?; + for (source, goal) in goals.goals { + let (has_changed, certainty) = + self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; if has_changed { unchanged_certainty = None; } @@ -579,23 +594,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.infcx.tcx } - pub(super) fn next_ty_infer(&self) -> Ty<'tcx> { - self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }) + pub(super) fn next_ty_infer(&mut self) -> Ty<'tcx> { + let ty = self.infcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span: DUMMY_SP }); + self.inspect.add_var_value(ty); + ty } - pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> { - self.infcx.next_const_var( - ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span: DUMMY_SP }, - ) + pub(super) fn next_const_infer(&mut self, ty: Ty<'tcx>) -> ty::Const<'tcx> { + let ct = self + .infcx + .next_const_var(ty, ConstVariableOrigin { param_def_id: None, span: DUMMY_SP }); + self.inspect.add_var_value(ct); + ct } /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`. /// If `kind` is an integer inference variable this will still return a ty infer var. - pub(super) fn next_term_infer_of_kind(&self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> { + pub(super) fn next_term_infer_of_kind(&mut self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> { match kind.unpack() { ty::TermKind::Ty(_) => self.next_ty_infer().into(), ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(), @@ -711,7 +726,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result<(), NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .eq(DefineOpaqueTypes::No, lhs, rhs) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .eq(DefineOpaqueTypes::Yes, lhs, rhs) .map(|InferOk { value: (), obligations }| { self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); }) @@ -721,6 +737,46 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + /// This should be used when relating a rigid alias with another type. + /// + /// Normally we emit a nested `AliasRelate` when equating an inference + /// variable and an alias. This causes us to instead constrain the inference + /// variable to the alias without emitting a nested alias relate goals. + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn relate_rigid_alias_non_alias( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + variance: ty::Variance, + term: ty::Term<'tcx>, + ) -> Result<(), NoSolution> { + // NOTE: this check is purely an optimization, the structural eq would + // always fail if the term is not an inference variable. + if term.is_infer() { + let tcx = self.tcx(); + // We need to relate `alias` to `term` treating only the outermost + // constructor as rigid, relating any contained generic arguments as + // normal. We do this by first structurally equating the `term` + // with the alias constructor instantiated with unconstrained infer vars, + // and then relate this with the whole `alias`. + // + // Alternatively we could modify `Equate` for this case by adding another + // variant to `StructurallyRelateAliases`. + let identity_args = self.fresh_args_for_item(alias.def_id); + let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args); + let ctor_ty = rigid_ctor.to_ty(tcx); + let InferOk { value: (), obligations } = self + .infcx + .at(&ObligationCause::dummy(), param_env) + .trace(term, ctor_ty.into()) + .eq_structurally_relating_aliases(term, ctor_ty.into())?; + debug_assert!(obligations.is_empty()); + self.relate(param_env, alias, variance, rigid_ctor) + } else { + Err(NoSolution) + } + } + /// This sohuld only be used when we're either instantiating a previously /// unconstrained "return value" or when we're sure that all aliases in /// the types are rigid. @@ -750,7 +806,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result<(), NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .sub(DefineOpaqueTypes::No, sub, sup) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .sub(DefineOpaqueTypes::Yes, sub, sup) .map(|InferOk { value: (), obligations }| { self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); }) @@ -770,7 +827,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result<(), NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .relate(DefineOpaqueTypes::No, lhs, variance, rhs) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .relate(DefineOpaqueTypes::Yes, lhs, variance, rhs) .map(|InferOk { value: (), obligations }| { self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); }) @@ -794,7 +852,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> Result>>, NoSolution> { self.infcx .at(&ObligationCause::dummy(), param_env) - .eq(DefineOpaqueTypes::No, lhs, rhs) + // New solver ignores DefineOpaqueTypes, so choose Yes for consistency + .eq(DefineOpaqueTypes::Yes, lhs, rhs) .map(|InferOk { value: (), obligations }| { obligations.into_iter().map(|o| o.into()).collect() }) @@ -884,8 +943,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool { - self.infcx.opaque_type_origin(def_id).is_some() + pub(super) fn trait_ref_is_knowable( + &mut self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + ) -> Result { + let infcx = self.infcx; + let lazily_normalize_ty = |ty| self.structurally_normalize_ty(param_env, ty); + coherence::trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty) + .map(|is_knowable| is_knowable.is_ok()) + } + + pub(super) fn can_define_opaque_ty(&self, def_id: impl Into) -> bool { + self.infcx.can_define_opaque_ty(def_id) } pub(super) fn insert_hidden_type( @@ -941,19 +1011,24 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if candidate_key.def_id != key.def_id { continue; } - values.extend(self.probe_misc_candidate("opaque type storage").enter(|ecx| { - for (a, b) in std::iter::zip(candidate_key.args, key.args) { - ecx.eq(param_env, a, b)?; - } - ecx.eq(param_env, candidate_ty, ty)?; - ecx.add_item_bounds_for_hidden_type( - candidate_key.def_id.to_def_id(), - candidate_key.args, - param_env, - candidate_ty, - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - })); + values.extend( + self.probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup { + result: *result, + }) + .enter(|ecx| { + for (a, b) in std::iter::zip(candidate_key.args, key.args) { + ecx.eq(param_env, a, b)?; + } + ecx.eq(param_env, candidate_ty, ty)?; + ecx.add_item_bounds_for_hidden_type( + candidate_key.def_id.to_def_id(), + candidate_key.args, + param_env, + candidate_ty, + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }), + ); } values } @@ -968,7 +1043,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty: Ty<'tcx>, ) -> Option> { use rustc_middle::mir::interpret::ErrorHandled; - match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) { + match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, DUMMY_SP) { Ok(ct) => Some(ct), Err(ErrorHandled::Reported(e, _)) => { Some(ty::Const::new_error(self.tcx(), e.into(), ty)) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 91fd48807a4d8..ee23f49939b7c 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -1,6 +1,7 @@ use crate::solve::assembly::Candidate; use super::EvalCtxt; +use rustc_infer::traits::BuiltinImplSource; use rustc_middle::traits::{ query::NoSolution, solve::{inspect, CandidateSource, QueryResult}, @@ -20,22 +21,29 @@ where pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T { let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self; + let infcx = outer_ecx.infcx; + let max_input_universe = outer_ecx.max_input_universe; let mut nested_ecx = EvalCtxt { - infcx: outer_ecx.infcx, + infcx, variables: outer_ecx.variables, var_values: outer_ecx.var_values, + is_normalizes_to_goal: outer_ecx.is_normalizes_to_goal, predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body, - max_input_universe: outer_ecx.max_input_universe, + max_input_universe, search_graph: outer_ecx.search_graph, nested_goals: outer_ecx.nested_goals.clone(), tainted: outer_ecx.tainted, - inspect: outer_ecx.inspect.new_probe(), + inspect: outer_ecx.inspect.take_and_enter_probe(), }; - let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx)); - if !outer_ecx.inspect.is_noop() { + let r = nested_ecx.infcx.probe(|_| { + let r = f(&mut nested_ecx); + nested_ecx.inspect.probe_final_state(infcx, max_input_universe); + r + }); + if !nested_ecx.inspect.is_noop() { let probe_kind = probe_kind(&r); nested_ecx.inspect.probe_kind(probe_kind); - outer_ecx.inspect.finish_probe(nested_ecx.inspect); + outer_ecx.inspect = nested_ecx.inspect.finish_probe(); } r } @@ -68,24 +76,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ProbeCtxt { ecx: self, probe_kind, _result: PhantomData } } - pub(in crate::solve) fn probe_misc_candidate( + pub(in crate::solve) fn probe_builtin_trait_candidate( &mut self, - name: &'static str, - ) -> ProbeCtxt< - '_, - 'a, - 'tcx, - impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, - QueryResult<'tcx>, - > { - ProbeCtxt { - ecx: self, - probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::MiscCandidate { - name, - result: *result, - }, - _result: PhantomData, - } + source: BuiltinImplSource, + ) -> TraitProbeCtxt<'_, 'a, 'tcx, impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>> + { + self.probe_trait_candidate(CandidateSource::BuiltinImpl(source)) } pub(in crate::solve) fn probe_trait_candidate( diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 3262d64cb7d5f..16fe045b82de7 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -1,21 +1,17 @@ -use rustc_hir as hir; +use std::ops::ControlFlow; + use rustc_hir::def_id::DefId; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_infer::traits::solve::inspect::ProbeKind; +use rustc_infer::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_infer::traits::{ - Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine, -}; -use rustc_middle::traits::solve::{CandidateSource, CanonicalInput, Certainty, Goal}; -use rustc_middle::traits::{ - BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError, + BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, Obligation, ObligationCause, + PolyTraitObligation, PredicateObligation, Selection, SelectionError, SelectionResult, }; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::DUMMY_SP; +use rustc_macros::extension; +use rustc_span::Span; -use crate::solve::assembly::Candidate; -use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; -use crate::solve::inspect::ProofTreeBuilder; -use crate::traits::StructurallyNormalizeExt; -use crate::traits::TraitEngineExt; +use crate::solve::inspect::{self, ProofTreeInferCtxtExt}; #[extension(pub trait InferCtxtSelectExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { @@ -25,352 +21,192 @@ impl<'tcx> InferCtxt<'tcx> { ) -> SelectionResult<'tcx, Selection<'tcx>> { assert!(self.next_trait_solver()); - self.enter_forall(obligation.predicate, |pred| { - let trait_goal = Goal::new(self.tcx, obligation.param_env, pred); - - let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::Never, |ecx| { - let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate); - let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal); - let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal); - - // pseudo-winnow - if candidates.len() == 0 { - return Err(SelectionError::Unimplemented); - } else if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { - candidate_should_be_dropped_in_favor_of( - ecx.tcx(), - &candidates[i], - &candidates[j], - ) - }); - if should_drop_i { - candidates.swap_remove(i); - } else { - i += 1; - if i > 1 { - return Ok(None); - } - } - } - } + self.visit_proof_tree( + Goal::new(self.tcx, obligation.param_env, obligation.predicate), + &mut Select { span: obligation.cause.span }, + ) + .break_value() + .unwrap() + } +} - let candidate = candidates.pop().unwrap(); - let certainty = ecx.instantiate_and_apply_query_response( - trait_goal.param_env, - orig_values, - candidate.result, - ); +struct Select { + span: Span, +} - Ok(Some((candidate, certainty))) - }); +impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select { + type Result = ControlFlow>>; - let (candidate, certainty) = match result { - Ok(Some(result)) => result, - Ok(None) => return Ok(None), - Err(e) => return Err(e), - }; + fn span(&self) -> Span { + self.span + } - let goal = self.resolve_vars_if_possible(trait_goal); - match (certainty, candidate.source) { - // Rematching the implementation will instantiate the same nested goals that - // would have caused the ambiguity, so we can still make progress here regardless. - (_, CandidateSource::Impl(def_id)) => rematch_impl(self, goal, def_id), + fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result { + let mut candidates = goal.candidates(); + candidates.retain(|cand| cand.result().is_ok()); - // If an unsize goal is ambiguous, then we can manually rematch it to make - // selection progress for coercion during HIR typeck. If it is *not* ambiguous, - // but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals, - // and we need to rematch those to detect tuple unsizing and trait upcasting. - // FIXME: This will be wrong if we have param-env or where-clause bounds - // with the unsize goal -- we may need to mark those with different impl - // sources. - (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src)) - | (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc)) - if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => - { - rematch_unsize(self, goal, src, certainty) - } + // No candidates -- not implemented. + if candidates.is_empty() { + return ControlFlow::Break(Err(SelectionError::Unimplemented)); + } - // Technically some builtin impls have nested obligations, but if - // `Certainty::Yes`, then they should've all been verified and don't - // need re-checking. - (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => { - Ok(Some(ImplSource::Builtin(src, vec![]))) - } + // One candidate, no need to winnow. + if candidates.len() == 1 { + return ControlFlow::Break(Ok(to_selection( + self.span, + candidates.into_iter().next().unwrap(), + ))); + } - // It's fine not to do anything to rematch these, since there are no - // nested obligations. - (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => { - Ok(Some(ImplSource::Param(vec![]))) + // We need to winnow. See comments on `candidate_should_be_dropped_in_favor_of`. + let mut i = 0; + while i < candidates.len() { + let should_drop_i = (0..candidates.len()) + .filter(|&j| i != j) + .any(|j| candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])); + if should_drop_i { + candidates.swap_remove(i); + } else { + i += 1; + if i > 1 { + return ControlFlow::Break(Ok(None)); } - - (Certainty::Maybe(_), _) => Ok(None), } - }) - } -} + } -impl<'tcx> EvalCtxt<'_, 'tcx> { - fn compute_canonical_trait_candidates( - &mut self, - canonical_input: CanonicalInput<'tcx>, - ) -> Vec> { - // This doesn't record the canonical goal on the stack during the - // candidate assembly step, but that's fine. Selection is conceptually - // outside of the solver, and if there were any cycles, we'd encounter - // the cycle anyways one step later. - EvalCtxt::enter_canonical( - self.tcx(), - self.search_graph, - canonical_input, - // FIXME: This is wrong, idk if we even want to track stuff here. - &mut ProofTreeBuilder::new_noop(), - |ecx, goal| { - let trait_goal = Goal { - param_env: goal.param_env, - predicate: goal - .predicate - .to_opt_poly_trait_pred() - .expect("we canonicalized a trait goal") - .no_bound_vars() - .expect("we instantiated all bound vars"), - }; - ecx.assemble_and_evaluate_candidates(trait_goal) - }, - ) + ControlFlow::Break(Ok(to_selection(self.span, candidates.into_iter().next().unwrap()))) } } +/// This is a lot more limited than the old solver's equivalent method. This may lead to more `Ok(None)` +/// results when selecting traits in polymorphic contexts, but we should never rely on the lack of ambiguity, +/// and should always just gracefully fail here. We shouldn't rely on this incompleteness. fn candidate_should_be_dropped_in_favor_of<'tcx>( - tcx: TyCtxt<'tcx>, - victim: &Candidate<'tcx>, - other: &Candidate<'tcx>, + victim: &inspect::InspectCandidate<'_, 'tcx>, + other: &inspect::InspectCandidate<'_, 'tcx>, ) -> bool { - match (victim.source, other.source) { - (CandidateSource::ParamEnv(victim_idx), CandidateSource::ParamEnv(other_idx)) => { - victim_idx >= other_idx + // Don't winnow until `Certainty::Yes` -- we don't need to winnow until + // codegen, technically. + if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + return false; + } + + let inspect::ProbeKind::TraitCandidate { source: victim_source, result: _ } = victim.kind() + else { + return false; + }; + let inspect::ProbeKind::TraitCandidate { source: other_source, result: _ } = other.kind() + else { + return false; + }; + + match (victim_source, other_source) { + (_, CandidateSource::CoherenceUnknowable) | (CandidateSource::CoherenceUnknowable, _) => { + bug!("should not have assembled a CoherenceUnknowable candidate") } - (_, CandidateSource::ParamEnv(_)) => true, - // FIXME: we could prefer earlier vtable bases perhaps... + // Prefer dyn candidates over non-dyn candidates. This is necessary to + // handle the unsoundness between `impl Any for T` and `dyn Any: Any`. ( CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), ) => false, - (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true, + ( + CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound, + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), + ) => true, + // Prefer specializing candidates over specialized candidates. (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => { - tcx.specializes((other_def_id, victim_def_id)) - && other.result.value.certainty == Certainty::Yes + victim.goal().infcx().tcx.specializes((other_def_id, victim_def_id)) } _ => false, } } +fn to_selection<'tcx>( + span: Span, + cand: inspect::InspectCandidate<'_, 'tcx>, +) -> Option> { + if let Certainty::Maybe(..) = cand.shallow_certainty() { + return None; + } + + let make_nested = || { + cand.instantiate_nested_goals(span) + .into_iter() + .map(|nested| { + Obligation::new( + nested.infcx().tcx, + ObligationCause::dummy_with_span(span), + nested.goal().param_env, + nested.goal().predicate, + ) + }) + .collect() + }; + + Some(match cand.kind() { + ProbeKind::TraitCandidate { source, result: _ } => match source { + CandidateSource::Impl(impl_def_id) => { + // FIXME: Remove this in favor of storing this in the tree + // For impl candidates, we do the rematch manually to compute the args. + ImplSource::UserDefined(rematch_impl(cand.goal(), impl_def_id, span)) + } + CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, make_nested()), + CandidateSource::ParamEnv(_) => ImplSource::Param(make_nested()), + CandidateSource::AliasBound => { + ImplSource::Builtin(BuiltinImplSource::Misc, make_nested()) + } + CandidateSource::CoherenceUnknowable => { + span_bug!(span, "didn't expect to select an unknowable candidate") + } + }, + ProbeKind::TryNormalizeNonRigid { result: _ } + | ProbeKind::NormalizedSelfTyAssembly + | ProbeKind::UnsizeAssembly + | ProbeKind::UpcastProjectionCompatibility + | ProbeKind::OpaqueTypeStorageLookup { result: _ } + | ProbeKind::Root { result: _ } => { + span_bug!(span, "didn't expect to assemble trait candidate from {:#?}", cand.kind()) + } + }) +} + fn rematch_impl<'tcx>( - infcx: &InferCtxt<'tcx>, - goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, + goal: &inspect::InspectGoal<'_, 'tcx>, impl_def_id: DefId, -) -> SelectionResult<'tcx, Selection<'tcx>> { - let args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + span: Span, +) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> { + let infcx = goal.infcx(); + let goal_trait_ref = infcx + .enter_forall_and_leak_universe(goal.goal().predicate.to_opt_poly_trait_pred().unwrap()) + .trait_ref; + + let args = infcx.fresh_args_for_item(span, impl_def_id); let impl_trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(infcx.tcx, args); - let mut nested = infcx - .at(&ObligationCause::dummy(), goal.param_env) - .eq(DefineOpaqueTypes::No, goal.predicate.trait_ref, impl_trait_ref) - .map_err(|_| SelectionError::Unimplemented)? - .into_obligations(); + let InferOk { value: (), obligations: mut nested } = infcx + .at(&ObligationCause::dummy_with_span(span), goal.goal().param_env) + .eq(DefineOpaqueTypes::Yes, goal_trait_ref, impl_trait_ref) + .expect("rematching impl failed"); + + // FIXME(-Znext-solver=coinductive): We need to add supertraits here eventually. nested.extend( infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, args).into_iter().map( - |(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred), - ), - ); - - Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested }))) -} - -/// The `Unsize` trait is particularly important to coercion, so we try rematch it. -/// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait -/// goal assembly in the solver, both for soundness and in order to avoid ICEs. -fn rematch_unsize<'tcx>( - infcx: &InferCtxt<'tcx>, - goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, - source: BuiltinImplSource, - certainty: Certainty, -) -> SelectionResult<'tcx, Selection<'tcx>> { - let tcx = infcx.tcx; - let mut nested = vec![]; - let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); - let b_ty = structurally_normalize( - goal.predicate.trait_ref.args.type_at(1), - infcx, - goal.param_env, - &mut nested, - ); - - match (a_ty.kind(), b_ty.kind()) { - // Don't try to coerce `?0` to `dyn Trait` - (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => Ok(None), - // Stall any ambiguous upcasting goals, since we can't rematch those - (ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty { - Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))), - _ => Ok(None), - }, - // `T` -> `dyn Trait` upcasting - (_, &ty::Dynamic(data, region, ty::Dyn)) => { - // Check that the type implements all of the predicates of the def-id. - // (i.e. the principal, all of the associated types match, and any auto traits) - nested.extend(data.iter().map(|pred| { + |(clause, _)| { Obligation::new( infcx.tcx, - ObligationCause::dummy(), - goal.param_env, - pred.with_self_ty(tcx, a_ty), + ObligationCause::dummy_with_span(span), + goal.goal().param_env, + clause, ) - })); - // The type must be Sized to be unsized. - let sized_def_id = tcx.require_lang_item(hir::LangItem::Sized, None); - nested.push(Obligation::new( - infcx.tcx, - ObligationCause::dummy(), - goal.param_env, - ty::TraitRef::new(tcx, sized_def_id, [a_ty]), - )); - // The type must outlive the lifetime of the `dyn` we're unsizing into. - nested.push(Obligation::new( - infcx.tcx, - ObligationCause::dummy(), - goal.param_env, - ty::OutlivesPredicate(a_ty, region), - )); - - Ok(Some(ImplSource::Builtin(source, nested))) - } - // `[T; n]` -> `[T]` unsizing - (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { - nested.extend( - infcx - .at(&ObligationCause::dummy(), goal.param_env) - .eq(DefineOpaqueTypes::No, a_elem_ty, b_elem_ty) - .expect("expected rematch to succeed") - .into_obligations(), - ); - - Ok(Some(ImplSource::Builtin(source, nested))) - } - // Struct unsizing `Struct` -> `Struct` where `T: Unsize` - (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) - if a_def.is_struct() && a_def.did() == b_def.did() => - { - let unsizing_params = tcx.unsizing_params_for_adt(a_def.did()); - // We must be unsizing some type parameters. This also implies - // that the struct has a tail field. - if unsizing_params.is_empty() { - bug!("expected rematch to succeed") - } - - let tail_field = a_def - .non_enum_variant() - .fields - .raw - .last() - .expect("expected unsized ADT to have a tail field"); - let tail_field_ty = tcx.type_of(tail_field.did); - - let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); - let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); - - // Instantiate just the unsizing params from B into A. The type after - // this instantiation must be equal to B. This is so we don't unsize - // unrelated type parameters. - let new_a_args = tcx.mk_args_from_iter( - a_args - .iter() - .enumerate() - .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }), - ); - let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args); - - nested.extend( - infcx - .at(&ObligationCause::dummy(), goal.param_env) - .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty) - .expect("expected rematch to succeed") - .into_obligations(), - ); - - // Finally, we require that `TailA: Unsize` for the tail field - // types. - nested.push(Obligation::new( - tcx, - ObligationCause::dummy(), - goal.param_env, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), - )); - - Ok(Some(ImplSource::Builtin(source, nested))) - } - // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize` - (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) - if a_tys.len() == b_tys.len() && !a_tys.is_empty() => - { - let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); - let b_last_ty = b_tys.last().unwrap(); - - // Instantiate just the tail field of B., and require that they're equal. - let unsized_a_ty = - Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); - nested.extend( - infcx - .at(&ObligationCause::dummy(), goal.param_env) - .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty) - .expect("expected rematch to succeed") - .into_obligations(), - ); - - // Similar to ADTs, require that we can unsize the tail. - nested.push(Obligation::new( - tcx, - ObligationCause::dummy(), - goal.param_env, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), - )); - - // We need to be able to detect tuple unsizing to require its feature gate. - assert_eq!( - source, - BuiltinImplSource::TupleUnsizing, - "compiler-errors wants to know if this can ever be triggered..." - ); - Ok(Some(ImplSource::Builtin(source, nested))) - } - _ => { - assert_ne!(certainty, Certainty::Yes); - Ok(None) - } - } -} + }, + ), + ); -fn structurally_normalize<'tcx>( - ty: Ty<'tcx>, - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - nested: &mut Vec>, -) -> Ty<'tcx> { - if matches!(ty.kind(), ty::Alias(..)) { - let mut engine = >::new(infcx); - let normalized_ty = infcx - .at(&ObligationCause::dummy(), param_env) - .structurally_normalize(ty, &mut *engine) - .expect("normalization shouldn't fail if we got to here"); - nested.extend(engine.pending_obligations()); - normalized_ty - } else { - ty - } + ImplSourceUserDefinedData { impl_def_id, nested, args } } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 9e3e6a4676efb..7c6dd4d3febc9 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -11,35 +11,94 @@ use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; -use rustc_infer::infer::InferCtxt; +use rustc_infer::infer::resolve::EagerResolver; +use rustc_infer::infer::type_variable::TypeVariableOrigin; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_infer::traits::{TraitEngine, TraitEngineExt}; +use rustc_macros::extension; +use rustc_middle::infer::unify_key::ConstVariableOrigin; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{inspect, QueryResult}; use rustc_middle::traits::solve::{Certainty, Goal}; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty; +use rustc_middle::ty::TypeFoldable; +use rustc_span::{Span, DUMMY_SP}; -use crate::solve::inspect::ProofTreeBuilder; +use crate::solve::eval_ctxt::canonical; +use crate::solve::FulfillmentCtxt; +use crate::solve::{EvalCtxt, GoalEvaluationKind, GoalSource}; use crate::solve::{GenerateProofTree, InferCtxtEvalExt}; +pub struct InspectConfig { + pub max_depth: usize, +} + pub struct InspectGoal<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, depth: usize, - orig_values: &'a [ty::GenericArg<'tcx>], + orig_values: Vec>, goal: Goal<'tcx, ty::Predicate<'tcx>>, - evaluation: &'a inspect::GoalEvaluation<'tcx>, + result: Result, + evaluation_kind: inspect::CanonicalGoalEvaluationKind<'tcx>, + normalizes_to_term_hack: Option>, +} + +/// The expected term of a `NormalizesTo` goal gets replaced +/// with an unconstrained inference variable when computing +/// `NormalizesTo` goals and we return the nested goals to the +/// caller, who also equates the actual term with the expected. +/// +/// This is an implementation detail of the trait solver and +/// not something we want to leak to users. We therefore +/// treat `NormalizesTo` goals as if they apply the expected +/// type at the end of each candidate. +#[derive(Copy, Clone)] +struct NormalizesToTermHack<'tcx> { + term: ty::Term<'tcx>, + unconstrained_term: ty::Term<'tcx>, +} + +impl<'tcx> NormalizesToTermHack<'tcx> { + /// Relate the `term` with the new `unconstrained_term` created + /// when computing the proof tree for this `NormalizesTo` goals. + /// This handles nested obligations. + fn constrain( + self, + infcx: &InferCtxt<'tcx>, + span: Span, + param_env: ty::ParamEnv<'tcx>, + ) -> Result { + infcx + .at(&ObligationCause::dummy_with_span(span), param_env) + .eq(DefineOpaqueTypes::Yes, self.term, self.unconstrained_term) + .map_err(|_| NoSolution) + .and_then(|InferOk { value: (), obligations }| { + let mut fulfill_cx = FulfillmentCtxt::new(infcx); + fulfill_cx.register_predicate_obligations(infcx, obligations); + if fulfill_cx.select_where_possible(infcx).is_empty() { + if fulfill_cx.pending_obligations().is_empty() { + Ok(Certainty::Yes) + } else { + Ok(Certainty::AMBIGUOUS) + } + } else { + Err(NoSolution) + } + }) + } } pub struct InspectCandidate<'a, 'tcx> { goal: &'a InspectGoal<'a, 'tcx>, kind: inspect::ProbeKind<'tcx>, nested_goals: Vec>>>, + final_state: inspect::CanonicalState<'tcx, ()>, result: QueryResult<'tcx>, + shallow_certainty: Certainty, } impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { - pub fn infcx(&self) -> &'a InferCtxt<'tcx> { - self.goal.infcx - } - pub fn kind(&self) -> inspect::ProbeKind<'tcx> { self.kind } @@ -48,43 +107,112 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { self.result.map(|c| c.value.certainty) } - /// Visit the nested goals of this candidate. + pub fn goal(&self) -> &'a InspectGoal<'a, 'tcx> { + self.goal + } + + /// Certainty passed into `evaluate_added_goals_and_make_canonical_response`. /// - /// FIXME(@lcnr): we have to slightly adapt this API - /// to also use it to compute the most relevant goal - /// for fulfillment errors. Will do that once we actually - /// need it. - pub fn visit_nested>(&self, visitor: &mut V) -> V::Result { - // HACK: An arbitrary cutoff to avoid dealing with overflow and cycles. - if self.goal.depth <= 10 { - let infcx = self.goal.infcx; - infcx.probe(|_| { - let mut instantiated_goals = vec![]; - for goal in &self.nested_goals { - let goal = ProofTreeBuilder::instantiate_canonical_state( - infcx, - self.goal.goal.param_env, - self.goal.orig_values, - *goal, - ); - instantiated_goals.push(goal); - } + /// If this certainty is `Yes`, then we must be confident that the candidate + /// must hold iff it's nested goals hold. This is not true if the certainty is + /// `Maybe(..)`, which suggests we forced ambiguity instead. + /// + /// This is *not* the certainty of the candidate's full nested evaluation, which + /// can be accessed with [`Self::result`] instead. + pub fn shallow_certainty(&self) -> Certainty { + self.shallow_certainty + } + + /// Visit all nested goals of this candidate without rolling + /// back their inference constraints. This function modifies + /// the state of the `infcx`. + pub fn visit_nested_no_probe>(&self, visitor: &mut V) -> V::Result { + if self.goal.depth < visitor.config().max_depth { + for goal in self.instantiate_nested_goals(visitor.span()) { + try_visit!(visitor.visit_goal(&goal)); + } + } - for &goal in &instantiated_goals { - let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes); - let proof_tree = proof_tree.unwrap(); - try_visit!(visitor.visit_goal(&InspectGoal::new( + V::Result::output() + } + + /// Instantiate the nested goals for the candidate without rolling back their + /// inference constraints. This function modifies the state of the `infcx`. + pub fn instantiate_nested_goals(&self, span: Span) -> Vec> { + let infcx = self.goal.infcx; + let param_env = self.goal.goal.param_env; + let mut orig_values = self.goal.orig_values.to_vec(); + let instantiated_goals: Vec<_> = self + .nested_goals + .iter() + .map(|goal| { + canonical::instantiate_canonical_state( + infcx, + span, + param_env, + &mut orig_values, + *goal, + ) + }) + .collect(); + + let () = canonical::instantiate_canonical_state( + infcx, + span, + param_env, + &mut orig_values, + self.final_state, + ); + + if let Some(term_hack) = self.goal.normalizes_to_term_hack { + // FIXME: We ignore the expected term of `NormalizesTo` goals + // when computing the result of its candidates. This is + // scuffed. + let _ = term_hack.constrain(infcx, span, param_env); + } + + instantiated_goals + .into_iter() + .map(|goal| match goal.predicate.kind().no_bound_vars() { + Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => { + let unconstrained_term = match term.unpack() { + ty::TermKind::Ty(_) => infcx + .next_ty_var(TypeVariableOrigin { param_def_id: None, span }) + .into(), + ty::TermKind::Const(ct) => infcx + .next_const_var( + ct.ty(), + ConstVariableOrigin { param_def_id: None, span }, + ) + .into(), + }; + let goal = + goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term }); + let proof_tree = EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| { + ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal) + }) + .1; + InspectGoal::new( infcx, self.goal.depth + 1, - &proof_tree, - ))); + proof_tree.unwrap(), + Some(NormalizesToTermHack { term, unconstrained_term }), + ) } - - V::Result::output() + _ => InspectGoal::new( + infcx, + self.goal.depth + 1, + infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1.unwrap(), + None, + ), }) - } else { - V::Result::output() - } + .collect() + } + + /// Visit all nested goals of this candidate, rolling back + /// all inference constraints. + pub fn visit_nested_in_probe>(&self, visitor: &mut V) -> V::Result { + self.goal.infcx.probe(|_| self.visit_nested_no_probe(visitor)) } } @@ -98,7 +226,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { } pub fn result(&self) -> Result { - self.evaluation.evaluation.result.map(|c| c.value.certainty) + self.result } fn candidates_recur( @@ -107,6 +235,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { nested_goals: &mut Vec>>>, probe: &inspect::Probe<'tcx>, ) { + let mut shallow_certainty = None; for step in &probe.steps { match step { &inspect::ProbeStep::AddGoal(_source, goal) => nested_goals.push(goal), @@ -118,52 +247,47 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { self.candidates_recur(candidates, nested_goals, probe); nested_goals.truncate(num_goals); } - inspect::ProbeStep::EvaluateGoals(_) - | inspect::ProbeStep::CommitIfOkStart - | inspect::ProbeStep::CommitIfOkSuccess => (), + inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { + assert_eq!(shallow_certainty.replace(*c), None); + } + inspect::ProbeStep::EvaluateGoals(_) => (), } } match probe.kind { inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly - | inspect::ProbeKind::UpcastProjectionCompatibility - | inspect::ProbeKind::CommitIfOk => (), - // We add a candidate for the root evaluation if there + | inspect::ProbeKind::UpcastProjectionCompatibility => (), + + // We add a candidate even for the root evaluation if there // is only one way to prove a given goal, e.g. for `WellFormed`. - // - // FIXME: This is currently wrong if we don't even try any - // candidates, e.g. for a trait goal, as in this case `candidates` is - // actually supposed to be empty. - inspect::ProbeKind::Root { result } => { - if candidates.is_empty() { + inspect::ProbeKind::Root { result } + | inspect::ProbeKind::TryNormalizeNonRigid { result } + | inspect::ProbeKind::TraitCandidate { source: _, result } + | inspect::ProbeKind::OpaqueTypeStorageLookup { result } => { + // We only add a candidate if `shallow_certainty` was set, which means + // that we ended up calling `evaluate_added_goals_and_make_canonical_response`. + if let Some(shallow_certainty) = shallow_certainty { candidates.push(InspectCandidate { goal: self, kind: probe.kind, nested_goals: nested_goals.clone(), + final_state: probe.final_state, result, + shallow_certainty, }); } } - inspect::ProbeKind::MiscCandidate { name: _, result } - | inspect::ProbeKind::TraitCandidate { source: _, result } => { - candidates.push(InspectCandidate { - goal: self, - kind: probe.kind, - nested_goals: nested_goals.clone(), - result, - }); - } } } pub fn candidates(&'a self) -> Vec> { let mut candidates = vec![]; - let last_eval_step = match self.evaluation.evaluation.kind { + let last_eval_step = match self.evaluation_kind { inspect::CanonicalGoalEvaluationKind::Overflow | inspect::CanonicalGoalEvaluationKind::CycleInStack | inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => { - warn!("unexpected root evaluation: {:?}", self.evaluation); + warn!("unexpected root evaluation: {:?}", self.evaluation_kind); return vec![]; } inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => { @@ -181,20 +305,44 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { candidates } + /// Returns the single candidate applicable for the current goal, if it exists. + /// + /// Returns `None` if there are either no or multiple applicable candidates. + pub fn unique_applicable_candidate(&'a self) -> Option> { + // FIXME(-Znext-solver): This does not handle impl candidates + // hidden by env candidates. + let mut candidates = self.candidates(); + candidates.retain(|c| c.result().is_ok()); + candidates.pop().filter(|_| candidates.is_empty()) + } + fn new( infcx: &'a InferCtxt<'tcx>, depth: usize, - root: &'a inspect::GoalEvaluation<'tcx>, + root: inspect::GoalEvaluation<'tcx>, + normalizes_to_term_hack: Option>, ) -> Self { - match root.kind { - inspect::GoalEvaluationKind::Root { ref orig_values } => InspectGoal { - infcx, - depth, - orig_values, - goal: infcx.resolve_vars_if_possible(root.uncanonicalized_goal), - evaluation: root, - }, - inspect::GoalEvaluationKind::Nested { .. } => unreachable!(), + let inspect::GoalEvaluation { uncanonicalized_goal, kind, evaluation } = root; + let inspect::GoalEvaluationKind::Root { orig_values } = kind else { unreachable!() }; + + let result = evaluation.result.and_then(|ok| { + if let Some(term_hack) = normalizes_to_term_hack { + infcx + .probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env)) + .map(|certainty| ok.value.certainty.unify_with(certainty)) + } else { + Ok(ok.value.certainty) + } + }); + + InspectGoal { + infcx, + depth, + orig_values, + goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)), + result, + evaluation_kind: evaluation.kind, + normalizes_to_term_hack, } } } @@ -203,6 +351,12 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { pub trait ProofTreeVisitor<'tcx> { type Result: VisitorResult = (); + fn span(&self) -> Span; + + fn config(&self) -> InspectConfig { + InspectConfig { max_depth: 10 } + } + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result; } @@ -213,10 +367,8 @@ impl<'tcx> InferCtxt<'tcx> { goal: Goal<'tcx, ty::Predicate<'tcx>>, visitor: &mut V, ) -> V::Result { - self.probe(|_| { - let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes); - let proof_tree = proof_tree.unwrap(); - visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree)) - }) + let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes); + let proof_tree = proof_tree.unwrap(); + visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree, None)) } } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index f7b310a7abe27..466d0d8006018 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -5,14 +5,17 @@ //! see the comment on [ProofTreeBuilder]. use std::mem; +use rustc_infer::infer::InferCtxt; +use rustc_middle::infer::canonical::CanonicalVarValues; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ - CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, QueryInput, QueryResult, + CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult, }; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::DumpSolverProofTree; -use crate::solve::{self, inspect, EvalCtxt, GenerateProofTree}; +use crate::solve::eval_ctxt::canonical; +use crate::solve::{self, inspect, GenerateProofTree}; /// The core data structure when building proof trees. /// @@ -47,9 +50,7 @@ enum DebugSolver<'tcx> { Root, GoalEvaluation(WipGoalEvaluation<'tcx>), CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>), - AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>), GoalEvaluationStep(WipGoalEvaluationStep<'tcx>), - Probe(WipProbe<'tcx>), } impl<'tcx> From> for DebugSolver<'tcx> { @@ -64,24 +65,12 @@ impl<'tcx> From> for DebugSolver<'tcx> { } } -impl<'tcx> From> for DebugSolver<'tcx> { - fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> { - DebugSolver::AddedGoalsEvaluation(g) - } -} - impl<'tcx> From> for DebugSolver<'tcx> { fn from(g: WipGoalEvaluationStep<'tcx>) -> DebugSolver<'tcx> { DebugSolver::GoalEvaluationStep(g) } } -impl<'tcx> From> for DebugSolver<'tcx> { - fn from(p: WipProbe<'tcx>) -> DebugSolver<'tcx> { - DebugSolver::Probe(p) - } -} - #[derive(Eq, PartialEq, Debug)] struct WipGoalEvaluation<'tcx> { pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>, @@ -97,9 +86,7 @@ impl<'tcx> WipGoalEvaluation<'tcx> { WipGoalEvaluationKind::Root { orig_values } => { inspect::GoalEvaluationKind::Root { orig_values } } - WipGoalEvaluationKind::Nested { is_normalizes_to_hack } => { - inspect::GoalEvaluationKind::Nested { is_normalizes_to_hack } - } + WipGoalEvaluationKind::Nested => inspect::GoalEvaluationKind::Nested, }, evaluation: self.evaluation.unwrap().finalize(), } @@ -109,7 +96,7 @@ impl<'tcx> WipGoalEvaluation<'tcx> { #[derive(Eq, PartialEq, Debug)] pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> { Root { orig_values: Vec> }, - Nested { is_normalizes_to_hack: IsNormalizesToHack }, + Nested, } #[derive(Eq, PartialEq)] @@ -186,12 +173,41 @@ impl<'tcx> WipAddedGoalsEvaluation<'tcx> { #[derive(Eq, PartialEq, Debug)] struct WipGoalEvaluationStep<'tcx> { + /// Unlike `EvalCtxt::var_values`, we append a new + /// generic arg here whenever we create a new inference + /// variable. + /// + /// This is necessary as we otherwise don't unify these + /// vars when instantiating multiple `CanonicalState`. + var_values: Vec>, instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, - + probe_depth: usize, evaluation: WipProbe<'tcx>, } impl<'tcx> WipGoalEvaluationStep<'tcx> { + fn current_evaluation_scope(&mut self) -> &mut WipProbe<'tcx> { + let mut current = &mut self.evaluation; + for _ in 0..self.probe_depth { + match current.steps.last_mut() { + Some(WipProbeStep::NestedProbe(p)) => current = p, + _ => bug!(), + } + } + current + } + + fn added_goals_evaluation(&mut self) -> &mut WipAddedGoalsEvaluation<'tcx> { + let mut current = &mut self.evaluation; + loop { + match current.steps.last_mut() { + Some(WipProbeStep::NestedProbe(p)) => current = p, + Some(WipProbeStep::EvaluateGoals(evaluation)) => return evaluation, + _ => bug!(), + } + } + } + fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> { let evaluation = self.evaluation.finalize(); match evaluation.kind { @@ -204,8 +220,10 @@ impl<'tcx> WipGoalEvaluationStep<'tcx> { #[derive(Eq, PartialEq, Debug)] struct WipProbe<'tcx> { - pub steps: Vec>, - pub kind: Option>, + initial_num_var_values: usize, + steps: Vec>, + kind: Option>, + final_state: Option>, } impl<'tcx> WipProbe<'tcx> { @@ -213,6 +231,7 @@ impl<'tcx> WipProbe<'tcx> { inspect::Probe { steps: self.steps.into_iter().map(WipProbeStep::finalize).collect(), kind: self.kind.unwrap(), + final_state: self.final_state.unwrap(), } } } @@ -222,8 +241,7 @@ enum WipProbeStep<'tcx> { AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), EvaluateGoals(WipAddedGoalsEvaluation<'tcx>), NestedProbe(WipProbe<'tcx>), - CommitIfOkStart, - CommitIfOkSuccess, + MakeCanonicalResponse { shallow_certainty: Certainty }, } impl<'tcx> WipProbeStep<'tcx> { @@ -232,8 +250,9 @@ impl<'tcx> WipProbeStep<'tcx> { WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal), WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()), WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()), - WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart, - WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess, + WipProbeStep::MakeCanonicalResponse { shallow_certainty } => { + inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty } + } } } } @@ -251,6 +270,12 @@ impl<'tcx> ProofTreeBuilder<'tcx> { self.state.as_deref_mut() } + pub fn take_and_enter_probe(&mut self) -> ProofTreeBuilder<'tcx> { + let mut nested = ProofTreeBuilder { state: self.state.take() }; + nested.enter_probe(); + nested + } + pub fn finalize(self) -> Option> { match *self.state? { DebugSolver::GoalEvaluation(wip_goal_evaluation) => { @@ -305,9 +330,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> { solve::GoalEvaluationKind::Root => { WipGoalEvaluationKind::Root { orig_values: orig_values.to_vec() } } - solve::GoalEvaluationKind::Nested { is_normalizes_to_hack } => { - WipGoalEvaluationKind::Nested { is_normalizes_to_hack } - } + solve::GoalEvaluationKind::Nested => WipGoalEvaluationKind::Nested, }, evaluation: None, }) @@ -370,11 +393,14 @@ impl<'tcx> ProofTreeBuilder<'tcx> { if let Some(this) = self.as_mut() { match (this, *goal_evaluation.state.unwrap()) { ( - DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation { - evaluations, .. - }), + DebugSolver::GoalEvaluationStep(state), DebugSolver::GoalEvaluation(goal_evaluation), - ) => evaluations.last_mut().unwrap().push(goal_evaluation), + ) => state + .added_goals_evaluation() + .evaluations + .last_mut() + .unwrap() + .push(goal_evaluation), (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation, _ => unreachable!(), } @@ -383,13 +409,22 @@ impl<'tcx> ProofTreeBuilder<'tcx> { pub fn new_goal_evaluation_step( &mut self, + var_values: CanonicalVarValues<'tcx>, instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, ) -> ProofTreeBuilder<'tcx> { self.nested(|| WipGoalEvaluationStep { + var_values: var_values.var_values.to_vec(), instantiated_goal, - evaluation: WipProbe { steps: vec![], kind: None }, + evaluation: WipProbe { + initial_num_var_values: var_values.len(), + steps: vec![], + kind: None, + final_state: None, + }, + probe_depth: 0, }) } + pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) { if let Some(this) = self.as_mut() { match (this, *goal_evaluation_step.state.unwrap()) { @@ -404,124 +439,159 @@ impl<'tcx> ProofTreeBuilder<'tcx> { } } - pub fn new_probe(&mut self) -> ProofTreeBuilder<'tcx> { - self.nested(|| WipProbe { steps: vec![], kind: None }) + pub fn add_var_value>>(&mut self, arg: T) { + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + state.var_values.push(arg.into()); + } + Some(s) => bug!("tried to add var values to {s:?}"), + } + } + + pub fn enter_probe(&mut self) { + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + let initial_num_var_values = state.var_values.len(); + state.current_evaluation_scope().steps.push(WipProbeStep::NestedProbe(WipProbe { + initial_num_var_values, + steps: vec![], + kind: None, + final_state: None, + })); + state.probe_depth += 1; + } + Some(s) => bug!("tried to start probe to {s:?}"), + } } pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<'tcx>) { - if let Some(this) = self.as_mut() { - match this { - DebugSolver::Probe(this) => { - assert_eq!(this.kind.replace(probe_kind), None) - } - _ => unreachable!(), + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + let prev = state.current_evaluation_scope().kind.replace(probe_kind); + assert_eq!(prev, None); } + _ => bug!(), } } - pub fn add_goal( - ecx: &mut EvalCtxt<'_, 'tcx>, - source: GoalSource, - goal: Goal<'tcx, ty::Predicate<'tcx>>, + pub fn probe_final_state( + &mut self, + infcx: &InferCtxt<'tcx>, + max_input_universe: ty::UniverseIndex, ) { - // Can't use `if let Some(this) = ecx.inspect.as_mut()` here because - // we have to immutably use the `EvalCtxt` for `make_canonical_state`. - if ecx.inspect.is_noop() { - return; + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + let final_state = canonical::make_canonical_state( + infcx, + &state.var_values, + max_input_universe, + (), + ); + let prev = state.current_evaluation_scope().final_state.replace(final_state); + assert_eq!(prev, None); + } + _ => bug!(), } + } - let goal = Self::make_canonical_state(ecx, goal); + pub fn add_normalizes_to_goal( + &mut self, + infcx: &InferCtxt<'tcx>, + max_input_universe: ty::UniverseIndex, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) { + self.add_goal( + infcx, + max_input_universe, + GoalSource::Misc, + goal.with(infcx.tcx, goal.predicate), + ); + } - match ecx.inspect.as_mut().unwrap() { - DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { - evaluation: WipProbe { steps, .. }, - .. - }) - | DebugSolver::Probe(WipProbe { steps, .. }) => { - steps.push(WipProbeStep::AddGoal(source, goal)) + pub fn add_goal( + &mut self, + infcx: &InferCtxt<'tcx>, + max_input_universe: ty::UniverseIndex, + source: GoalSource, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) { + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + let goal = canonical::make_canonical_state( + infcx, + &state.var_values, + max_input_universe, + goal, + ); + state.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal)) } - s => unreachable!("tried to add {goal:?} to {s:?}"), + _ => bug!(), } } - pub fn finish_probe(&mut self, probe: ProofTreeBuilder<'tcx>) { - if let Some(this) = self.as_mut() { - match (this, *probe.state.unwrap()) { - ( - DebugSolver::Probe(WipProbe { steps, .. }) - | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { - evaluation: WipProbe { steps, .. }, - .. - }), - DebugSolver::Probe(probe), - ) => steps.push(WipProbeStep::NestedProbe(probe)), - _ => unreachable!(), + pub fn make_canonical_response(&mut self, shallow_certainty: Certainty) { + match self.as_mut() { + Some(DebugSolver::GoalEvaluationStep(state)) => { + state + .current_evaluation_scope() + .steps + .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty }); } + None => {} + _ => {} } } - /// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside - /// of the probe into the parent. - pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) { - if let Some(this) = self.as_mut() { - match (this, *probe.state.unwrap()) { - ( - DebugSolver::Probe(WipProbe { steps, .. }) - | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { - evaluation: WipProbe { steps, .. }, - .. - }), - DebugSolver::Probe(probe), - ) => { - steps.push(WipProbeStep::CommitIfOkStart); - assert_eq!(probe.kind, None); - steps.extend(probe.steps); - steps.push(WipProbeStep::CommitIfOkSuccess); - } - _ => unreachable!(), + pub fn finish_probe(mut self) -> ProofTreeBuilder<'tcx> { + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + assert_ne!(state.probe_depth, 0); + let num_var_values = state.current_evaluation_scope().initial_num_var_values; + state.var_values.truncate(num_var_values); + state.probe_depth -= 1; } + _ => bug!(), } - } - pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { - self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None }) + self } - pub fn evaluate_added_goals_loop_start(&mut self) { - if let Some(this) = self.as_mut() { - match this { - DebugSolver::AddedGoalsEvaluation(this) => { - this.evaluations.push(vec![]); - } - _ => unreachable!(), + pub fn start_evaluate_added_goals(&mut self) { + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + state.current_evaluation_scope().steps.push(WipProbeStep::EvaluateGoals( + WipAddedGoalsEvaluation { evaluations: vec![], result: None }, + )); } + _ => bug!(), } } - pub fn eval_added_goals_result(&mut self, result: Result) { - if let Some(this) = self.as_mut() { - match this { - DebugSolver::AddedGoalsEvaluation(this) => { - assert_eq!(this.result.replace(result), None); - } - _ => unreachable!(), + pub fn start_evaluate_added_goals_step(&mut self) { + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + state.added_goals_evaluation().evaluations.push(vec![]); } + _ => bug!(), } } - pub fn added_goals_evaluation(&mut self, added_goals_evaluation: ProofTreeBuilder<'tcx>) { - if let Some(this) = self.as_mut() { - match (this, *added_goals_evaluation.state.unwrap()) { - ( - DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { - evaluation: WipProbe { steps, .. }, - .. - }) - | DebugSolver::Probe(WipProbe { steps, .. }), - DebugSolver::AddedGoalsEvaluation(added_goals_evaluation), - ) => steps.push(WipProbeStep::EvaluateGoals(added_goals_evaluation)), - _ => unreachable!(), + pub fn evaluate_added_goals_result(&mut self, result: Result) { + match self.as_mut() { + None => {} + Some(DebugSolver::GoalEvaluationStep(state)) => { + let prev = state.added_goals_evaluation().result.replace(result); + assert_eq!(prev, None); } + _ => bug!(), } } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 0bf28f520a4d2..b2b076e28e648 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -16,10 +16,10 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::traits::query::NoSolution; +use rustc_macros::extension; use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::traits::solve::{ - CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack, - QueryResult, Response, + CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, QueryResult, Response, }; use rustc_middle::ty::{self, AliasRelationDirection, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ @@ -69,7 +69,7 @@ enum SolverMode { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum GoalEvaluationKind { Root, - Nested { is_normalizes_to_hack: IsNormalizesToHack }, + Nested, } #[extension(trait CanonicalResponseExt)] @@ -201,21 +201,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } impl<'tcx> EvalCtxt<'_, 'tcx> { - #[instrument(level = "debug", skip(self))] - fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { - assert!( - self.nested_goals.normalizes_to_hack_goal.is_none(), - "attempted to set the projection eq hack goal when one already exists" - ); - self.nested_goals.normalizes_to_hack_goal = Some(goal); - } - - #[instrument(level = "debug", skip(self))] - fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) { - inspect::ProofTreeBuilder::add_goal(self, source, goal); - self.nested_goals.goals.push((source, goal)); - } - #[instrument(level = "debug", skip(self, goals))] fn add_goals( &mut self, @@ -320,5 +305,6 @@ fn response_no_constraints_raw<'tcx>( external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()), certainty, }, + defining_opaque_types: Default::default(), } } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index b1c03e82cab9b..65ef465990727 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -3,11 +3,11 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{BoundVarReplacer, PlaceholderReplacer}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::TraitEngineExt; use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::infer::unify_key::ConstVariableOrigin; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; @@ -74,10 +74,8 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { self.depth += 1; - let new_infer_ty = infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, - span: self.at.cause.span, - }); + let new_infer_ty = + infcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span: self.at.cause.span }); let obligation = Obligation::new( tcx, self.at.cause.clone(), @@ -124,10 +122,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { let new_infer_ct = infcx.next_const_var( ty, - ConstVariableOrigin { - kind: ConstVariableOriginKind::MiscVariable, - span: self.at.cause.span, - }, + ConstVariableOrigin { param_def_id: None, span: self.at.cause.span }, ); let obligation = Obligation::new( tcx, @@ -177,7 +172,7 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ty, infcx.shallow_resolve(ty)); - if !ty.has_projections() { + if !ty.has_aliases() { return Ok(ty); } @@ -203,8 +198,8 @@ impl<'tcx> FallibleTypeFolder> for NormalizationFolder<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result, Self::Error> { let infcx = self.at.infcx; - debug_assert_eq!(ct, infcx.shallow_resolve(ct)); - if !ct.has_projections() { + debug_assert_eq!(ct, infcx.shallow_resolve_const(ct)); + if !ct.has_aliases() { return Ok(ct); } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs index 911462f4b9afe..37d5645289398 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs @@ -16,7 +16,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .no_bound_vars() .expect("const ty should not rely on other generics"), ) { - self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; + self.instantiate_normalizes_to_term(goal, normalized_const.into()); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs index 52d2fe1e3ec22..439f9eec831f2 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs @@ -1,5 +1,5 @@ //! Computes a normalizes-to (projection) goal for inherent associated types, -//! `#![feature(inherent_associated_type)]`. Since astconv already determines +//! `#![feature(inherent_associated_type)]`. Since HIR ty lowering already determines //! which impl the IAT is being projected from, we just: //! 1. instantiate generic parameters, //! 2. equate the self type, and @@ -16,7 +16,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { let tcx = self.tcx(); let inherent = goal.predicate.alias; - let expected = goal.predicate.term.ty().expect("inherent consts are treated separately"); let impl_def_id = tcx.parent(inherent.def_id); let impl_args = self.fresh_args_for_item(impl_def_id); @@ -30,12 +29,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Equate IAT with the RHS of the project goal let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, tcx); - self.eq( - goal.param_env, - expected, - tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args), - ) - .expect("expected goal term to be fully unconstrained"); // Check both where clauses on the impl and IAT // @@ -51,6 +44,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .map(|(pred, _)| goal.with(tcx, pred)), ); + let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args); + self.instantiate_normalizes_to_term(goal, normalized.into()); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index a45c1c344107c..dab87fffe461e 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -1,4 +1,4 @@ -use crate::traits::{check_args_compatible, specialization_graph}; +use crate::traits::specialization_graph; use super::assembly::structural_traits::AsyncCallableRelevantTypes; use super::assembly::{self, structural_traits, Candidate}; @@ -7,11 +7,11 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::solve::inspect::ProbeKind; +use rustc_infer::traits::solve::MaybeCause; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; -use rustc_middle::traits::solve::{ - CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, -}; +use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult}; use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::NormalizesTo; @@ -29,34 +29,48 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn compute_normalizes_to_goal( &mut self, goal: Goal<'tcx, NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + self.set_is_normalizes_to_goal(); + debug_assert!(self.term_is_fully_unconstrained(goal)); + let normalize_result = self + .probe(|&result| ProbeKind::TryNormalizeNonRigid { result }) + .enter(|this| this.normalize_at_least_one_step(goal)); + + match normalize_result { + Ok(res) => Ok(res), + Err(NoSolution) => { + let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal; + if alias.opt_kind(self.tcx()).is_some() { + self.relate_rigid_alias_non_alias( + param_env, + alias, + ty::Variance::Invariant, + term, + )?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + // FIXME(generic_const_exprs): we currently do not support rigid + // unevaluated constants. + Err(NoSolution) + } + } + } + } + + /// Normalize the given alias by at least one step. If the alias is rigid, this + /// returns `NoSolution`. + #[instrument(level = "debug", skip(self), ret)] + fn normalize_at_least_one_step( + &mut self, + goal: Goal<'tcx, NormalizesTo<'tcx>>, ) -> QueryResult<'tcx> { let def_id = goal.predicate.def_id(); match self.tcx().def_kind(def_id) { DefKind::AssocTy | DefKind::AssocConst => { match self.tcx().associated_item(def_id).container { ty::AssocItemContainer::TraitContainer => { - // To only compute normalization once for each projection we only - // assemble normalization candidates if the expected term is an - // unconstrained inference variable. - // - // Why: For better cache hits, since if we have an unconstrained RHS then - // there are only as many cache keys as there are (canonicalized) alias - // types in each normalizes-to goal. This also weakens inference in a - // forwards-compatible way so we don't use the value of the RHS term to - // affect candidate assembly for projections. - // - // E.g. for `::Assoc == u32` we recursively compute the goal - // `exists ::Assoc == U` and then take the resulting type for - // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver, since we're piggybacking off of regular - // goal caching. - if self.term_is_fully_unconstrained(goal) { - let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) - } else { - self.set_normalizes_to_hack_goal(goal); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) } ty::AssocItemContainer::ImplContainer => { self.normalize_inherent_associated_type(goal) @@ -64,37 +78,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } DefKind::AnonConst => self.normalize_anon_const(goal), - DefKind::OpaqueTy => self.normalize_opaque_type(goal), DefKind::TyAlias => self.normalize_weak_type(goal), - kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), + DefKind::OpaqueTy => self.normalize_opaque_type(goal), + kind => bug!("unknown DefKind {} in normalizes-to goal: {goal:#?}", kind.descr(def_id)), } } - /// When normalizing an associated item, constrain the result to `term`. - /// - /// While `NormalizesTo` goals have the normalized-to term as an argument, - /// this argument is always fully unconstrained for associated items. - /// It is therefore appropriate to instead think of these `NormalizesTo` goals - /// as function returning a term after normalizing. - /// - /// When equating an inference variable and an alias, we tend to emit `alias-relate` - /// goals and only actually instantiate the inference variable with an alias if the - /// alias is rigid. However, this means that constraining the expected term of - /// such goals ends up fully structurally normalizing the resulting type instead of - /// only by one step. To avoid this we instead use structural equality here, resulting - /// in each `NormalizesTo` only projects by a single step. + /// When normalizing an associated item, constrain the expected term to `term`. /// - /// Not doing so, currently causes issues because trying to normalize an opaque type - /// during alias-relate doesn't actually constrain the opaque if the concrete type - /// is an inference variable. This means that `NormalizesTo` for associated types - /// normalizing to an opaque type always resulted in ambiguity, breaking tests e.g. - /// tests/ui/type-alias-impl-trait/issue-78450.rs. + /// We know `term` to always be a fully unconstrained inference variable, so + /// `eq` should never fail here. However, in case `term` contains aliases, we + /// emit nested `AliasRelate` goals to structurally normalize the alias. pub fn instantiate_normalizes_to_term( &mut self, goal: Goal<'tcx, NormalizesTo<'tcx>>, term: ty::Term<'tcx>, ) { - self.eq_structurally_relating_aliases(goal.param_env, goal.predicate.term, term) + self.eq(goal.param_env, goal.predicate.term, term) .expect("expected goal term to be fully unconstrained"); } } @@ -118,14 +118,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, + source: CandidateSource, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { if let Some(projection_pred) = assumption.as_projection_clause() { if projection_pred.projection_def_id() == goal.predicate.def_id() { let tcx = ecx.tcx(); - ecx.probe_misc_candidate("assumption").enter(|ecx| { + ecx.probe_trait_candidate(source).enter(|ecx| { let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred); ecx.eq( @@ -261,7 +262,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { assoc_def.defining_node, ); - if !check_args_compatible(tcx, assoc_def.item, args) { + if !tcx.check_args_compatible(assoc_def.item.def_id, args) { return error_response( ecx, "associated item has mismatched generic item arguments", @@ -299,14 +300,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn consider_error_guaranteed_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, _guar: ErrorGuaranteed, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { Err(NoSolution) } fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { ecx.tcx().dcx().span_delayed_bug( ecx.tcx().def_span(goal.predicate.def_id()), "associated types not allowed on auto traits", @@ -317,35 +318,35 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn consider_trait_alias_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { bug!("trait aliases do not have associated types: {:?}", goal); } fn consider_builtin_sized_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { bug!("`Sized` does not have an associated type: {:?}", goal); } fn consider_builtin_copy_clone_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal); } fn consider_builtin_pointer_like_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { bug!("`PointerLike` does not have an associated type: {:?}", goal); } fn consider_builtin_fn_ptr_trait_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { bug!("`FnPtr` does not have an associated type: {:?}", goal); } @@ -353,7 +354,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let tupled_inputs_and_output = match structural_traits::extract_tupled_inputs_and_output_from_callable( @@ -363,8 +364,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { )? { Some(tupled_inputs_and_output) => tupled_inputs_and_output, None => { - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + return ecx.forced_ambiguity(MaybeCause::Ambiguity); } }; let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { @@ -384,14 +384,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)]) + Self::probe_and_consider_implied_clause( + ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + goal, + pred, + [goal.with(tcx, output_is_sized_pred)], + ) } fn consider_builtin_async_fn_trait_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let env_region = match goal_kind { @@ -428,7 +434,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ), output_coroutine_ty.into(), ), - sym::CallMutFuture | sym::CallFuture => ( + sym::CallRefFuture => ( ty::AliasTy::new( tcx, goal.predicate.def_id(), @@ -460,8 +466,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { // A built-in `AsyncFn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - Self::consider_implied_clause( + Self::probe_and_consider_implied_clause( ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, pred, [goal.with(tcx, output_is_sized_pred)] @@ -473,7 +480,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn consider_builtin_async_fn_kind_helper_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let [ closure_fn_kind_ty, goal_kind_ty, @@ -486,6 +493,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { bug!(); }; + // Bail if the upvars haven't been constrained. + if tupled_upvars_ty.expect_ty().is_ty_var() { + return ecx.forced_ambiguity(MaybeCause::Ambiguity); + } + let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else { // We don't need to worry about the self type being an infer var. return Err(NoSolution); @@ -506,25 +518,27 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { borrow_region.expect_region(), ); - ecx.instantiate_normalizes_to_term(goal, upvars_ty.into()); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.instantiate_normalizes_to_term(goal, upvars_ty.into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) } fn consider_builtin_tuple_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { bug!("`Tuple` does not have an associated type: {:?}", goal); } fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); assert_eq!(metadata_def_id, goal.predicate.def_id()); - ecx.probe_misc_candidate("builtin pointee").enter(|ecx| { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool | ty::Char @@ -532,6 +546,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { | ty::Uint(..) | ty::Float(..) | ty::Array(..) + | ty::Pat(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -602,7 +617,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = *self_ty.kind() else { return Err(NoSolution); @@ -616,8 +631,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { let term = args.as_coroutine().return_ty().into(); - Self::consider_implied_clause( + Self::probe_and_consider_implied_clause( ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]), @@ -633,7 +649,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn consider_builtin_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = *self_ty.kind() else { return Err(NoSolution); @@ -647,8 +663,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { let term = args.as_coroutine().yield_ty().into(); - Self::consider_implied_clause( + Self::probe_and_consider_implied_clause( ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]), @@ -661,10 +678,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ) } + fn consider_builtin_fused_iterator_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Result, NoSolution> { + bug!("`FusedIterator` does not have an associated type: {:?}", goal); + } + fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = *self_ty.kind() else { return Err(NoSolution); @@ -676,7 +700,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { return Err(NoSolution); } - ecx.probe_misc_candidate("builtin AsyncIterator kind").enter(|ecx| { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let expected_ty = ecx.next_ty_infer(); // Take `AsyncIterator` and turn it into the corresponding // coroutine yield ty `Poll>`. @@ -700,7 +724,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); let ty::Coroutine(def_id, args) = *self_ty.kind() else { return Err(NoSolution); @@ -723,8 +747,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { bug!("unexpected associated item `<{self_ty} as Coroutine>::{name}`") }; - Self::consider_implied_clause( + Self::probe_and_consider_implied_clause( ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionPredicate { projection_ty: ty::AliasTy::new( @@ -744,14 +769,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + ) -> Vec> { bug!("`Unsize` does not have an associated type: {:?}", goal); } fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); let discriminant_ty = match *self_ty.kind() { ty::Bool @@ -760,6 +785,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { | ty::Uint(..) | ty::Float(..) | ty::Array(..) + | ty::Pat(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) @@ -793,23 +819,76 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ), }; - ecx.probe_misc_candidate("builtin discriminant kind").enter(|ecx| { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.instantiate_normalizes_to_term(goal, discriminant_ty.into()); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } + fn consider_builtin_async_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Result, NoSolution> { + let self_ty = goal.predicate.self_ty(); + let async_destructor_ty = match *self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Never + | ty::Adt(_, _) + | ty::Str + | ty::Slice(_) + | ty::Tuple(_) + | ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env), + + // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder + // types, which return `::AsyncDestructor` + // (or ICE in the case of placeholders). Projecting a type to itself + // is never really productive. + ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + return Err(NoSolution); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Foreign(..) + | ty::Bound(..) => bug!( + "unexpected self ty `{:?}` when normalizing `::AsyncDestructor`", + goal.predicate.self_ty() + ), + + ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => bug!( + "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}" + ), + }; + + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.eq(goal.param_env, goal.predicate.term, async_destructor_ty.into()) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + fn consider_builtin_destruct_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { bug!("`Destruct` does not have an associated type: {:?}", goal); } fn consider_builtin_transmute_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal) } } diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs index 356c3776c04ce..9fdb280cdc6da 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs @@ -58,12 +58,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - let expected = self.structurally_normalize_ty(goal.param_env, expected)?; - if expected.is_ty_var() { - return self - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); - } - // Otherwise, define a new opaque type self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; self.add_item_bounds_for_hidden_type( diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs index 9f91c02c1ab60..13af5068b6c99 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs @@ -15,10 +15,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { let tcx = self.tcx(); let weak_ty = goal.predicate.alias; - let expected = goal.predicate.term.ty().expect("no such thing as a const alias"); - - let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args); - self.eq(goal.param_env, expected, actual)?; // Check where clauses self.add_goals( @@ -30,6 +26,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .map(|pred| goal.with(tcx, pred)), ); + let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args); + self.instantiate_normalizes_to_term(goal, actual.into()); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 07a8aca85a011..a48b2f2478b0d 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -10,7 +10,6 @@ use rustc_index::IndexVec; use rustc_middle::dep_graph::dep_kinds; use rustc_middle::traits::solve::CacheData; use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; -use rustc_middle::ty; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; use std::mem; @@ -175,15 +174,6 @@ impl<'tcx> SearchGraph<'tcx> { } } - pub(super) fn current_goal_is_normalizes_to(&self) -> bool { - self.stack.raw.last().map_or(false, |e| { - matches!( - e.input.value.goal.predicate.kind().skip_binder(), - ty::PredicateKind::NormalizesTo(..) - ) - }) - } - /// Returns the remaining depth allowed for nested goals. /// /// This is generally simply one less than the current depth. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index c252ad76dfe1d..c8cb14abb554d 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -9,12 +9,11 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::solve::MaybeCause; use rustc_middle::traits::solve::inspect::ProbeKind; -use rustc_middle::traits::solve::{ - CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, -}; +use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult}; use rustc_middle::traits::{BuiltinImplSource, Reveal}; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, DUMMY_SP}; @@ -55,17 +54,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // An upper bound of the certainty of this goal, used to lower the certainty // of reservation impl to ambiguous during coherence. let impl_polarity = impl_trait_header.polarity; - let maximal_certainty = match impl_polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => { - match impl_polarity == goal.predicate.polarity { - true => Certainty::Yes, - false => return Err(NoSolution), - } - } - ty::ImplPolarity::Reservation => match ecx.solver_mode() { - SolverMode::Normal => return Err(NoSolution), + let maximal_certainty = match (impl_polarity, goal.predicate.polarity) { + // In intercrate mode, this is ambiguous. But outside of intercrate, + // it's not a real impl. + (ty::ImplPolarity::Reservation, _) => match ecx.solver_mode() { SolverMode::Coherence => Certainty::AMBIGUOUS, + SolverMode::Normal => return Err(NoSolution), }, + + // Impl matches polarity + (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => Certainty::Yes, + + // Impl doesn't match polarity + (ty::ImplPolarity::Positive, ty::PredicatePolarity::Negative) + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Positive) => { + return Err(NoSolution); + } }; ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { @@ -88,21 +93,24 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_error_guaranteed_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, _guar: ErrorGuaranteed, - ) -> QueryResult<'tcx> { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ) -> Result, NoSolution> { + // FIXME: don't need to enter a probe here. + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, + source: CandidateSource, goal: Goal<'tcx, Self>, assumption: ty::Clause<'tcx>, then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { if let Some(trait_clause) = assumption.as_trait_clause() { if trait_clause.def_id() == goal.predicate.def_id() && trait_clause.polarity() == goal.predicate.polarity { - ecx.probe_misc_candidate("assumption").enter(|ecx| { + ecx.probe_trait_candidate(source).enter(|ecx| { let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); ecx.eq( goal.param_env, @@ -122,8 +130,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -149,16 +157,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() { if matches!(goal.param_env.reveal(), Reveal::All) || matches!(ecx.solver_mode(), SolverMode::Coherence) - || opaque_ty - .def_id - .as_local() - .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id)) + || ecx.can_define_opaque_ty(opaque_ty.def_id) { return Err(NoSolution); } } ecx.probe_and_evaluate_goal_for_constituent_tys( + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, structural_traits::instantiate_constituent_tys_for_auto_trait, ) @@ -167,14 +173,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } let tcx = ecx.tcx(); - ecx.probe_misc_candidate("trait alias").enter(|ecx| { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) .instantiate(tcx, goal.predicate.trait_ref.args); @@ -190,12 +196,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } ecx.probe_and_evaluate_goal_for_constituent_tys( + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, structural_traits::instantiate_constituent_tys_for_sized_trait, ) @@ -204,12 +211,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } ecx.probe_and_evaluate_goal_for_constituent_tys( + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, structural_traits::instantiate_constituent_tys_for_copy_clone_trait, ) @@ -218,8 +226,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_builtin_pointer_like_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -231,14 +239,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty())); // But if there are inference variables, we have to wait until it's resolved. if key.has_non_region_infer() { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + return ecx.forced_ambiguity(MaybeCause::Ambiguity); } if let Ok(layout) = tcx.layout_of(key) && layout.layout.is_pointer_like(&tcx.data_layout) { // FIXME: We could make this faster by making a no-constraints response - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } else { Err(NoSolution) } @@ -247,31 +256,31 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let self_ty = goal.predicate.self_ty(); match goal.predicate.polarity { // impl FnPtr for FnPtr {} - ty::ImplPolarity::Positive => { + ty::PredicatePolarity::Positive => { if self_ty.is_fn_ptr() { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) } else { Err(NoSolution) } } // impl !FnPtr for T where T != FnPtr && T is rigid {} - ty::ImplPolarity::Negative => { + ty::PredicatePolarity::Negative => { // If a type is rigid and not a fn ptr, then we know for certain // that it does *not* implement `FnPtr`. if !self_ty.is_fn_ptr() && self_ty.is_known_rigid() { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) } else { Err(NoSolution) } } - // FIXME: Goal polarity should be split from impl polarity - ty::ImplPolarity::Reservation => { - bug!("we never expect a `Reservation` polarity in a trait goal") - } } } @@ -279,8 +288,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -293,8 +302,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { )? { Some(a) => a, None => { - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + return ecx.forced_ambiguity(MaybeCause::Ambiguity); } }; let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { @@ -308,15 +316,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { .to_predicate(tcx); // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)]) + Self::probe_and_consider_implied_clause( + ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + goal, + pred, + [goal.with(tcx, output_is_sized_pred)], + ) } fn consider_builtin_async_fn_trait_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -346,8 +360,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { .to_predicate(tcx); // A built-in `AsyncFn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - Self::consider_implied_clause( + Self::probe_and_consider_implied_clause( ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, pred, [goal.with(tcx, output_is_sized_pred)] @@ -359,7 +374,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_builtin_async_fn_kind_helper_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let [closure_fn_kind_ty, goal_kind_ty] = **goal.predicate.trait_ref.args else { bug!(); }; @@ -370,7 +385,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }; let goal_kind = goal_kind_ty.expect_ty().to_opt_closure_kind().unwrap(); if closure_kind.extends(goal_kind) { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } else { Err(NoSolution) } @@ -385,13 +401,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } if let ty::Tuple(..) = goal.predicate.self_ty().kind() { - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } else { Err(NoSolution) } @@ -400,19 +417,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -429,14 +447,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Async coroutine unconditionally implement `Future` // Technically, we need to check that the future output type is Sized, // but that's already proven by the coroutine being WF. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + // FIXME: use `consider_implied` + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } fn consider_builtin_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -453,14 +473,40 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Gen coroutines unconditionally implement `Iterator` // Technically, we need to check that the iterator output type is Sized, // but that's already proven by the coroutines being WF. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + // FIXME: use `consider_implied` + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) + } + + fn consider_builtin_fused_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + // Coroutines are not iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_gen(def_id) { + return Err(NoSolution); + } + + // Gen coroutines unconditionally implement `FusedIterator` + // FIXME: use `consider_implied` + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -477,14 +523,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Gen coroutines unconditionally implement `Iterator` // Technically, we need to check that the iterator output type is Sized, // but that's already proven by the coroutines being WF. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + // FIXME: use `consider_implied` + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } fn consider_builtin_coroutine_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -500,8 +548,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } let coroutine = args.as_coroutine(); - Self::consider_implied_clause( + Self::probe_and_consider_implied_clause( ecx, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()]) .to_predicate(tcx), @@ -514,20 +563,34 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } // `DiscriminantKind` is automatically implemented for every type. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) + } + + fn consider_builtin_async_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + // `AsyncDestruct` is automatically implemented for every type. + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -535,14 +598,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // `Destruct` is automatically implemented for every type in // non-const environments. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } fn consider_builtin_transmute_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -561,11 +625,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); }; - let certainty = ecx.is_transmutable( - rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, - assume, - )?; - ecx.evaluate_added_goals_and_make_canonical_response(certainty) + // FIXME: This actually should destructure the `Result` we get from transmutability and + // register candiates. We probably need to register >1 since we may have an OR of ANDs. + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let certainty = ecx.is_transmutable( + rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, + assume, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + }) } /// ```ignore (builtin impl example) @@ -578,20 +646,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn consider_structural_builtin_unsize_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + ) -> Vec> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return vec![]; } - let misc_candidate = |ecx: &mut EvalCtxt<'_, 'tcx>, certainty| { - ( - ecx.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(), - BuiltinImplSource::Misc, - ) - }; - - let result_to_single = |result, source| match result { - Ok(resp) => vec![(resp, source)], + let result_to_single = |result| match result { + Ok(resp) => vec![resp], Err(NoSolution) => vec![], }; @@ -609,7 +670,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); match (a_ty.kind(), b_ty.kind()) { (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"), - (_, ty::Infer(ty::TyVar(..))) => vec![misc_candidate(ecx, Certainty::AMBIGUOUS)], + + (_, ty::Infer(ty::TyVar(..))) => { + result_to_single(ecx.forced_ambiguity(MaybeCause::Ambiguity)) + } // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. ( @@ -622,14 +686,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // `T` -> `dyn Trait` unsizing. (_, &ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single( ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data), - BuiltinImplSource::Misc, ), // `[T; N]` -> `[T]` unsizing - (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single( - ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty), - BuiltinImplSource::Misc, - ), + (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { + result_to_single(ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty)) + } // `Struct` -> `Struct` where `T: Unsize` (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) @@ -637,7 +699,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { { result_to_single( ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args), - BuiltinImplSource::Misc, ) } @@ -645,10 +706,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) if a_tys.len() == b_tys.len() && !a_tys.is_empty() => { - result_to_single( - ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys), - BuiltinImplSource::TupleUnsizing, - ) + result_to_single(ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys)) } _ => vec![], @@ -674,7 +732,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { a_region: ty::Region<'tcx>, b_data: &'tcx ty::List>, b_region: ty::Region<'tcx>, - ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + ) -> Vec> { let tcx = self.tcx(); let Goal { predicate: (a_ty, _b_ty), .. } = goal; @@ -682,35 +740,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(resp) = self.consider_builtin_upcast_to_principal( + responses.extend(self.consider_builtin_upcast_to_principal( goal, + CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), a_data, a_region, b_data, b_region, a_data.principal(), - ) { - responses.push((resp, BuiltinImplSource::Misc)); - } + )); } else if let Some(a_principal) = a_data.principal() { self.walk_vtable( a_principal.with_self_ty(tcx, a_ty), |ecx, new_a_principal, _, vtable_vptr_slot| { - if let Ok(resp) = ecx.probe_misc_candidate("dyn upcast").enter(|ecx| { - ecx.consider_builtin_upcast_to_principal( - goal, - a_data, - a_region, - b_data, - b_region, - Some(new_a_principal.map_bound(|trait_ref| { - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) - })), - ) - }) { - responses - .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot })); - } + responses.extend(ecx.consider_builtin_upcast_to_principal( + goal, + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting { + vtable_vptr_slot, + }), + a_data, + a_region, + b_data, + b_region, + Some(new_a_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + })), + )); }, ); } @@ -723,7 +778,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, b_data: &'tcx ty::List>, b_region: ty::Region<'tcx>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = self.tcx(); let Goal { predicate: (a_ty, _), .. } = goal; @@ -732,37 +787,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return Err(NoSolution); } - // Check that the type implements all of the predicates of the trait object. - // (i.e. the principal, all of the associated types match, and any auto traits) - self.add_goals( - GoalSource::ImplWhereBound, - b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), - ); - - // The type must be `Sized` to be unsized. - if let Some(sized_def_id) = tcx.lang_items().sized_trait() { - self.add_goal( + self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + // Check that the type implements all of the predicates of the trait object. + // (i.e. the principal, all of the associated types match, and any auto traits) + ecx.add_goals( GoalSource::ImplWhereBound, - goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])), + b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), ); - } else { - return Err(NoSolution); - } - // The type must outlive the lifetime of the `dyn` we're unsizing into. - self.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + // The type must be `Sized` to be unsized. + if let Some(sized_def_id) = tcx.lang_items().sized_trait() { + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])), + ); + } else { + return Err(NoSolution); + } + + // The type must outlive the lifetime of the `dyn` we're unsizing into. + ecx.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) } fn consider_builtin_upcast_to_principal( &mut self, goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + source: CandidateSource, a_data: &'tcx ty::List>, a_region: ty::Region<'tcx>, b_data: &'tcx ty::List>, b_region: ty::Region<'tcx>, upcast_principal: Option>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let param_env = goal.param_env; // We may upcast to auto traits that are either explicitly listed in @@ -781,7 +839,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // having any inference side-effects. We process obligations because // unification may initially succeed due to deferred projection equality. let projection_may_match = - |ecx: &mut Self, + |ecx: &mut EvalCtxt<'_, 'tcx>, source_projection: ty::PolyExistentialProjection<'tcx>, target_projection: ty::PolyExistentialProjection<'tcx>| { source_projection.item_def_id() == target_projection.item_def_id() @@ -795,54 +853,60 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .is_ok() }; - for bound in b_data { - match bound.skip_binder() { - // Check that a's supertrait (upcast_principal) is compatible - // with the target (b_ty). - ty::ExistentialPredicate::Trait(target_principal) => { - self.eq(param_env, upcast_principal.unwrap(), bound.rebind(target_principal))?; - } - // Check that b_ty's projection is satisfied by exactly one of - // a_ty's projections. First, we look through the list to see if - // any match. If not, error. Then, if *more* than one matches, we - // return ambiguity. Otherwise, if exactly one matches, equate - // it with b_ty's projection. - ty::ExistentialPredicate::Projection(target_projection) => { - let target_projection = bound.rebind(target_projection); - let mut matching_projections = - a_data.projection_bounds().filter(|source_projection| { - projection_may_match(self, *source_projection, target_projection) - }); - let Some(source_projection) = matching_projections.next() else { - return Err(NoSolution); - }; - if matching_projections.next().is_some() { - return self.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ); + self.probe_trait_candidate(source).enter(|ecx| { + for bound in b_data { + match bound.skip_binder() { + // Check that a's supertrait (upcast_principal) is compatible + // with the target (b_ty). + ty::ExistentialPredicate::Trait(target_principal) => { + ecx.eq( + param_env, + upcast_principal.unwrap(), + bound.rebind(target_principal), + )?; } - self.eq(param_env, source_projection, target_projection)?; - } - // Check that b_ty's auto traits are present in a_ty's bounds. - ty::ExistentialPredicate::AutoTrait(def_id) => { - if !a_auto_traits.contains(&def_id) { - return Err(NoSolution); + // Check that b_ty's projection is satisfied by exactly one of + // a_ty's projections. First, we look through the list to see if + // any match. If not, error. Then, if *more* than one matches, we + // return ambiguity. Otherwise, if exactly one matches, equate + // it with b_ty's projection. + ty::ExistentialPredicate::Projection(target_projection) => { + let target_projection = bound.rebind(target_projection); + let mut matching_projections = + a_data.projection_bounds().filter(|source_projection| { + projection_may_match(ecx, *source_projection, target_projection) + }); + let Some(source_projection) = matching_projections.next() else { + return Err(NoSolution); + }; + if matching_projections.next().is_some() { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + ecx.eq(param_env, source_projection, target_projection)?; + } + // Check that b_ty's auto traits are present in a_ty's bounds. + ty::ExistentialPredicate::AutoTrait(def_id) => { + if !a_auto_traits.contains(&def_id) { + return Err(NoSolution); + } } } } - } - // Also require that a_ty's lifetime outlives b_ty's lifetime. - self.add_goal( - GoalSource::ImplWhereBound, - Goal::new( - self.tcx(), - param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - ), - ); + // Also require that a_ty's lifetime outlives b_ty's lifetime. + ecx.add_goal( + GoalSource::ImplWhereBound, + Goal::new( + ecx.tcx(), + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + ), + ); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) } /// We have the following builtin impls for arrays: @@ -858,9 +922,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, a_elem_ty: Ty<'tcx>, b_elem_ty: Ty<'tcx>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { self.eq(goal.param_env, a_elem_ty, b_elem_ty)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + self.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } /// We generate a builtin `Unsize` impls for structs with generic parameters only @@ -882,7 +947,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { def: ty::AdtDef<'tcx>, a_args: ty::GenericArgsRef<'tcx>, b_args: ty::GenericArgsRef<'tcx>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = self.tcx(); let Goal { predicate: (_a_ty, b_ty), .. } = goal; @@ -924,7 +989,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ), ), ); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + self.probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } /// We generate the following builtin impl for tuples of all sizes. @@ -942,7 +1008,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, a_tys: &'tcx ty::List>, b_tys: &'tcx ty::List>, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = self.tcx(); let Goal { predicate: (_a_ty, b_ty), .. } = goal; @@ -966,7 +1032,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ), ), ); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + self.probe_builtin_trait_candidate(BuiltinImplSource::TupleUnsizing) + .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } // Return `Some` if there is an impl (built-in or user provided) that may @@ -976,7 +1043,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn disqualify_auto_trait_candidate_due_to_possible_impl( &mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>, - ) -> Option> { + ) -> Option, NoSolution>> { let self_ty = goal.predicate.self_ty(); match *self_ty.kind() { // Stall int and float vars until they are resolved to a concrete @@ -985,7 +1052,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // we probably don't want to treat an `impl !AutoTrait for i32` as // disqualifying the built-in auto impl for `i64: AutoTrait` either. ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { - Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)) + Some(self.forced_ambiguity(MaybeCause::Ambiguity)) } // These types cannot be structurally decomposed into constituent @@ -1006,12 +1073,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { { match self.tcx().coroutine_movability(def_id) { Movability::Static => Some(Err(NoSolution)), - Movability::Movable => { - Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) - } + Movability::Movable => Some( + self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }), + ), } } + // If we still have an alias here, it must be rigid. For opaques, it's always + // okay to consider auto traits because that'll reveal its hidden type. For + // non-opaque aliases, we will not assemble any candidates since there's no way + // to further look into its type. + ty::Alias(..) => None, + // For rigid types, any possible implementation that could apply to // the type (even if after unification and processing nested goals // it does not hold) will disqualify the built-in auto impl. @@ -1027,8 +1102,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Float(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -1038,15 +1114,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(_) - | ty::Adt(_, _) - // FIXME: Handling opaques here is kinda sus. Especially because we - // simplify them to SimplifiedType::Placeholder. - | ty::Alias(ty::Opaque, _) => { + | ty::Adt(_, _) => { let mut disqualifying_impl = None; - self.tcx().for_each_relevant_impl_treating_projections( + self.tcx().for_each_relevant_impl( goal.predicate.def_id(), goal.predicate.self_ty(), - TreatProjections::NextSolverLookup, |impl_def_id| { disqualifying_impl = Some(impl_def_id); }, @@ -1070,13 +1142,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// wrapped in one. fn probe_and_evaluate_goal_for_constituent_tys( &mut self, + source: CandidateSource, goal: Goal<'tcx, TraitPredicate<'tcx>>, constituent_tys: impl Fn( &EvalCtxt<'_, 'tcx>, Ty<'tcx>, ) -> Result>>, NoSolution>, - ) -> QueryResult<'tcx> { - self.probe_misc_candidate("constituent tys").enter(|ecx| { + ) -> Result, NoSolution> { + self.probe_trait_candidate(source).enter(|ecx| { ecx.add_goals( GoalSource::ImplWhereBound, constituent_tys(ecx, goal.predicate.self_ty())? diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index fbf96833187d8..053de2c673b8a 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -6,13 +6,13 @@ use super::*; use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::traits::project::ProjectAndUnifyResult; + +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; +use rustc_data_structures::unord::UnordSet; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::{ImplPolarity, Region, RegionVid}; - -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_middle::ty::{Region, RegionVid}; -use std::collections::hash_map::Entry; use std::collections::VecDeque; use std::iter; @@ -25,8 +25,8 @@ pub enum RegionTarget<'tcx> { #[derive(Default, Debug, Clone)] pub struct RegionDeps<'tcx> { - larger: FxIndexSet>, - smaller: FxIndexSet>, + pub larger: FxIndexSet>, + pub smaller: FxIndexSet>, } pub enum AutoTraitResult { @@ -35,17 +35,10 @@ pub enum AutoTraitResult { NegativeImpl, } -#[allow(dead_code)] -impl AutoTraitResult { - fn is_auto(&self) -> bool { - matches!(self, AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl) - } -} - pub struct AutoTraitInfo<'cx> { pub full_user_env: ty::ParamEnv<'cx>, pub region_data: RegionConstraintData<'cx>, - pub vid_to_region: FxHashMap>, + pub vid_to_region: FxIndexMap>, } pub struct AutoTraitFinder<'tcx> { @@ -88,19 +81,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { let infcx = tcx.infer_ctxt().build(); let mut selcx = SelectionContext::new(&infcx); - for polarity in [true, false] { + for polarity in [ty::PredicatePolarity::Positive, ty::PredicatePolarity::Negative] { let result = selcx.select(&Obligation::new( tcx, ObligationCause::dummy(), orig_env, - ty::TraitPredicate { - trait_ref, - polarity: if polarity { - ImplPolarity::Positive - } else { - ImplPolarity::Negative - }, - }, + ty::TraitPredicate { trait_ref, polarity }, )); if let Ok(Some(ImplSource::UserDefined(_))) = result { debug!( @@ -114,7 +100,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } let infcx = tcx.infer_ctxt().build(); - let mut fresh_preds = FxHashSet::default(); + let mut fresh_preds = FxIndexSet::default(); // Due to the way projections are handled by SelectionContext, we need to run // evaluate_predicates twice: once on the original param env, and once on the result of @@ -166,7 +152,6 @@ impl<'tcx> AutoTraitFinder<'tcx> { with {:?}", trait_ref, full_env ); - infcx.clear_caches(); // At this point, we already have all of the bounds we need. FulfillmentContext is used // to store all of the necessary region/lifetime bounds in the InferContext, as well as @@ -190,9 +175,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { AutoTraitResult::PositiveImpl(auto_trait_callback(info)) } -} -impl<'tcx> AutoTraitFinder<'tcx> { /// The core logic responsible for computing the bounds for our synthesized impl. /// /// To calculate the bounds, we call `SelectionContext.select` in a loop. Like @@ -239,7 +222,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, user_env: ty::ParamEnv<'tcx>, - fresh_preds: &mut FxHashSet>, + fresh_preds: &mut FxIndexSet>, ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> { let tcx = infcx.tcx; @@ -252,13 +235,13 @@ impl<'tcx> AutoTraitFinder<'tcx> { let mut select = SelectionContext::new(infcx); - let mut already_visited = FxHashSet::default(); + let mut already_visited = UnordSet::new(); let mut predicates = VecDeque::new(); predicates.push_back(ty::Binder::dummy(ty::TraitPredicate { trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]), // Auto traits are positive - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, })); let computed_preds = param_env.caller_bounds().iter().map(|c| c.as_predicate()); @@ -269,8 +252,6 @@ impl<'tcx> AutoTraitFinder<'tcx> { let dummy_cause = ObligationCause::dummy(); while let Some(pred) = predicates.pop_front() { - infcx.clear_caches(); - if !already_visited.insert(pred) { continue; } @@ -295,7 +276,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { }) = impl_source { // Blame 'tidy' for the weird bracket placement. - if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative { + if infcx.tcx.impl_polarity(*impl_def_id) != ty::ImplPolarity::Positive { debug!( "evaluate_nested_obligations: found explicit negative impl\ {:?}, bailing out", @@ -473,9 +454,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { fn map_vid_to_region<'cx>( &self, regions: &RegionConstraintData<'cx>, - ) -> FxHashMap> { - let mut vid_map: FxHashMap, RegionDeps<'cx>> = FxHashMap::default(); - let mut finished_map = FxHashMap::default(); + ) -> FxIndexMap> { + let mut vid_map = FxIndexMap::, RegionDeps<'cx>>::default(); + let mut finished_map = FxIndexMap::default(); for (constraint, _) in ®ions.constraints { match constraint { @@ -513,25 +494,22 @@ impl<'tcx> AutoTraitFinder<'tcx> { } while !vid_map.is_empty() { - #[allow(rustc::potential_query_instability)] - let target = *vid_map.keys().next().expect("Keys somehow empty"); - let deps = vid_map.remove(&target).expect("Entry somehow missing"); + let target = *vid_map.keys().next().unwrap(); + let deps = vid_map.swap_remove(&target).unwrap(); for smaller in deps.smaller.iter() { for larger in deps.larger.iter() { match (smaller, larger) { (&RegionTarget::Region(_), &RegionTarget::Region(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { + if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) { let smaller_deps = v.into_mut(); smaller_deps.larger.insert(*larger); - // FIXME(#120456) - is `swap_remove` correct? smaller_deps.larger.swap_remove(&target); } - if let Entry::Occupied(v) = vid_map.entry(*larger) { + if let IndexEntry::Occupied(v) = vid_map.entry(*larger) { let larger_deps = v.into_mut(); larger_deps.smaller.insert(*smaller); - // FIXME(#120456) - is `swap_remove` correct? larger_deps.smaller.swap_remove(&target); } } @@ -542,17 +520,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { // Do nothing; we don't care about regions that are smaller than vids. } (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { - if let Entry::Occupied(v) = vid_map.entry(*smaller) { + if let IndexEntry::Occupied(v) = vid_map.entry(*smaller) { let smaller_deps = v.into_mut(); smaller_deps.larger.insert(*larger); - // FIXME(#120456) - is `swap_remove` correct? smaller_deps.larger.swap_remove(&target); } - if let Entry::Occupied(v) = vid_map.entry(*larger) { + if let IndexEntry::Occupied(v) = vid_map.entry(*larger) { let larger_deps = v.into_mut(); larger_deps.smaller.insert(*smaller); - // FIXME(#120456) - is `swap_remove` correct? larger_deps.smaller.swap_remove(&target); } } @@ -560,6 +536,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } } } + finished_map } @@ -588,7 +565,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty: Ty<'_>, nested: impl Iterator>, computed_preds: &mut FxIndexSet>, - fresh_preds: &mut FxHashSet>, + fresh_preds: &mut FxIndexSet>, predicates: &mut VecDeque>, selcx: &mut SelectionContext<'_, 'tcx>, ) -> bool { @@ -786,7 +763,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { match selcx.infcx.const_eval_resolve( obligation.param_env, unevaluated, - Some(obligation.cause.span), + obligation.cause.span, ) { Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())), Ok(None) => { @@ -807,7 +784,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { - match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(DefineOpaqueTypes::No,c1, c2) + match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(DefineOpaqueTypes::Yes,c1, c2) { Ok(_) => (), Err(_) => return false, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 2712ba1945156..86e7c42376aa0 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -20,30 +20,44 @@ use crate::traits::{ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::DefId; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{util, FulfillmentErrorCode, TraitEngine, TraitEngineExt}; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::sym; -use rustc_span::DUMMY_SP; +use rustc_span::{Span, DUMMY_SP}; use std::fmt::Debug; use std::ops::ControlFlow; use super::error_reporting::suggest_new_overflow_limit; -/// Whether we do the orphan check relative to this crate or -/// to some remote crate. +/// Whether we do the orphan check relative to this crate or to some remote crate. #[derive(Copy, Clone, Debug)] -enum InCrate { - Local, +pub enum InCrate { + Local { mode: OrphanCheckMode }, Remote, } +#[derive(Copy, Clone, Debug)] +pub enum OrphanCheckMode { + /// Proper orphan check. + Proper, + /// Improper orphan check for backward compatibility. + /// + /// In this mode, type params inside projections are considered to be covered + /// even if the projection may normalize to a type that doesn't actually cover + /// them. This is unsound. See also [#124559] and [#99554]. + /// + /// [#124559]: https://github.com/rust-lang/rust/issues/124559 + /// [#99554]: https://github.com/rust-lang/rust/issues/99554 + Compat, +} + #[derive(Debug, Copy, Clone)] pub enum Conflict { Upstream, @@ -210,7 +224,7 @@ fn overlap<'tcx>( .intercrate(true) .with_next_trait_solver(tcx.next_trait_solver_in_coherence()) .build(); - let selcx = &mut SelectionContext::with_treat_inductive_cycle_as_ambig(&infcx); + let selcx = &mut SelectionContext::new(&infcx); if track_ambiguity_causes.is_yes() { selcx.enable_tracking_intercrate_ambiguity_causes(); } @@ -477,7 +491,8 @@ fn plug_infer_with_placeholders<'tcx>( if ty.is_ty_var() { let Ok(InferOk { value: (), obligations }) = self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( - DefineOpaqueTypes::No, + // Comparing against a type variable never registers hidden types anyway + DefineOpaqueTypes::Yes, ty, Ty::new_placeholder( self.infcx.tcx, @@ -500,11 +515,13 @@ fn plug_infer_with_placeholders<'tcx>( } fn visit_const(&mut self, ct: ty::Const<'tcx>) { - let ct = self.infcx.shallow_resolve(ct); + let ct = self.infcx.shallow_resolve_const(ct); if ct.is_ct_infer() { let Ok(InferOk { value: (), obligations }) = self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( - DefineOpaqueTypes::No, + // The types of the constants are the same, so there is no hidden type + // registration happening anyway. + DefineOpaqueTypes::Yes, ct, ty::Const::new_placeholder( self.infcx.tcx, @@ -532,7 +549,8 @@ fn plug_infer_with_placeholders<'tcx>( if r.is_var() { let Ok(InferOk { value: (), obligations }) = self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( - DefineOpaqueTypes::No, + // Lifetimes don't contain opaque types (or any types for that matter). + DefineOpaqueTypes::Yes, r, ty::Region::new_placeholder( self.infcx.tcx, @@ -554,11 +572,7 @@ fn plug_infer_with_placeholders<'tcx>( } } - value.visit_with(&mut PlugInferWithPlaceholder { - infcx, - universe, - var: ty::BoundVar::from_u32(0), - }); + value.visit_with(&mut PlugInferWithPlaceholder { infcx, universe, var: ty::BoundVar::ZERO }); } fn try_prove_negated_where_clause<'tcx>( @@ -604,19 +618,20 @@ fn try_prove_negated_where_clause<'tcx>( /// This both checks whether any downstream or sibling crates could /// implement it and whether an upstream crate can add this impl /// without breaking backwards compatibility. -#[instrument(level = "debug", skip(tcx, lazily_normalize_ty), ret)] +#[instrument(level = "debug", skip(infcx, lazily_normalize_ty), ret)] pub fn trait_ref_is_knowable<'tcx, E: Debug>( - tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result, E>, ) -> Result, E> { - if orphan_check_trait_ref(trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() { + if orphan_check_trait_ref(infcx, trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() + { // A downstream or cousin crate is allowed to implement some // generic parameters of this trait-ref. return Ok(Err(Conflict::Downstream)); } - if trait_ref_is_local_or_fundamental(tcx, trait_ref) { + if trait_ref_is_local_or_fundamental(infcx.tcx, trait_ref) { // This is a local or fundamental trait, so future-compatibility // is no concern. We know that downstream/cousin crates are not // allowed to implement a generic parameter of this trait ref, @@ -633,7 +648,14 @@ pub fn trait_ref_is_knowable<'tcx, E: Debug>( // and if we are an intermediate owner, then we don't care // about future-compatibility, which means that we're OK if // we are an owner. - if orphan_check_trait_ref(trait_ref, InCrate::Local, &mut lazily_normalize_ty)?.is_ok() { + if orphan_check_trait_ref( + infcx, + trait_ref, + InCrate::Local { mode: OrphanCheckMode::Proper }, + &mut lazily_normalize_ty, + )? + .is_ok() + { Ok(Ok(())) } else { Ok(Err(Conflict::Upstream)) @@ -644,7 +666,7 @@ pub fn trait_ref_is_local_or_fundamental<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, ) -> bool { - trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental) + trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental) } #[derive(Debug, Copy, Clone)] @@ -663,31 +685,15 @@ impl From for IsFirstInputType { } #[derive(Debug)] -pub enum OrphanCheckErr<'tcx> { +pub enum OrphanCheckErr<'tcx, T> { NonLocalInputType(Vec<(Ty<'tcx>, IsFirstInputType)>), - UncoveredTy(Ty<'tcx>, Option>), + UncoveredTyParams(UncoveredTyParams<'tcx, T>), } -/// Checks the coherence orphan rules. `impl_def_id` should be the -/// `DefId` of a trait impl. To pass, either the trait must be local, or else -/// two conditions must be satisfied: -/// -/// 1. All type parameters in `Self` must be "covered" by some local type constructor. -/// 2. Some local type must appear in `Self`. -#[instrument(level = "debug", skip(tcx), ret)] -pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> { - // We only except this routine to be invoked on implementations - // of a trait, not inherent implementations. - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); - debug!(?trait_ref); - - // If the *trait* is local to the crate, ok. - if trait_ref.def_id.is_local() { - debug!("trait {:?} is local to current crate", trait_ref.def_id); - return Ok(()); - } - - orphan_check_trait_ref::(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap() +#[derive(Debug)] +pub struct UncoveredTyParams<'tcx, T> { + pub uncovered: T, + pub local_ty: Option>, } /// Checks whether a trait-ref is potentially implementable by a crate. @@ -735,6 +741,9 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// To check that a local impl follows the orphan rules, we check it in /// InCrate::Local mode, using type parameters for the "generic" types. /// +/// In InCrate::Local mode the orphan check succeeds if the current crate +/// is definitely allowed to implement the given trait (no false positives). +/// /// 2. They ground negative reasoning for coherence. If a user wants to /// write both a conditional blanket impl and a specific impl, we need to /// make sure they do not overlap. For example, if we write @@ -753,6 +762,9 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// try to implement this trait-ref. To check for this, we use InCrate::Remote /// mode. That is sound because we already know all the impls from known crates. /// +/// In InCrate::Remote mode the orphan check succeeds if a foreign crate +/// *could* implement the given trait (no false negatives). +/// /// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can /// add "non-blanket" impls without breaking negative reasoning in dependent /// crates. This is the "rebalancing coherence" (RFC 1023) restriction. @@ -776,54 +788,56 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// /// Note that this function is never called for types that have both type /// parameters and inference variables. -#[instrument(level = "trace", skip(lazily_normalize_ty), ret)] -fn orphan_check_trait_ref<'tcx, E: Debug>( +#[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)] +pub fn orphan_check_trait_ref<'tcx, E: Debug>( + infcx: &InferCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, in_crate: InCrate, lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result, E>, -) -> Result>, E> { - if trait_ref.has_infer() && trait_ref.has_param() { - bug!( - "can't orphan check a trait ref with both params and inference variables {:?}", - trait_ref - ); +) -> Result>>, E> { + if trait_ref.has_param() { + bug!("orphan check only expects inference variables: {trait_ref:?}"); } - let mut checker = OrphanChecker::new(in_crate, lazily_normalize_ty); + let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty); Ok(match trait_ref.visit_with(&mut checker) { ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), - ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err), - ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => { - // Does there exist some local type after the `ParamTy`. - checker.search_first_local_ty = true; - if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) = - trait_ref.visit_with(&mut checker).break_value() - { - Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty))) - } else { - Err(OrphanCheckErr::UncoveredTy(ty, None)) + ControlFlow::Break(residual) => match residual { + OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err), + OrphanCheckEarlyExit::UncoveredTyParam(ty) => { + // Does there exist some local type after the `ParamTy`. + checker.search_first_local_ty = true; + let local_ty = match trait_ref.visit_with(&mut checker).break_value() { + Some(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty), + _ => None, + }; + Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { + uncovered: ty, + local_ty, + })) } - } - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()), + OrphanCheckEarlyExit::LocalTy(_) => Ok(()), + }, }) } -struct OrphanChecker<'tcx, F> { +struct OrphanChecker<'a, 'tcx, F> { + infcx: &'a InferCtxt<'tcx>, in_crate: InCrate, in_self_ty: bool, lazily_normalize_ty: F, - /// Ignore orphan check failures and exclusively search for the first - /// local type. + /// Ignore orphan check failures and exclusively search for the first local type. search_first_local_ty: bool, non_local_tys: Vec<(Ty<'tcx>, IsFirstInputType)>, } -impl<'tcx, F, E> OrphanChecker<'tcx, F> +impl<'a, 'tcx, F, E> OrphanChecker<'a, 'tcx, F> where F: FnOnce(Ty<'tcx>) -> Result, E>, { - fn new(in_crate: InCrate, lazily_normalize_ty: F) -> Self { + fn new(infcx: &'a InferCtxt<'tcx>, in_crate: InCrate, lazily_normalize_ty: F) -> Self { OrphanChecker { + infcx, in_crate, in_self_ty: true, lazily_normalize_ty, @@ -837,17 +851,20 @@ where ControlFlow::Continue(()) } - fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow> { + fn found_uncovered_ty_param( + &mut self, + ty: Ty<'tcx>, + ) -> ControlFlow> { if self.search_first_local_ty { - ControlFlow::Continue(()) - } else { - ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t)) + return ControlFlow::Continue(()); } + + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) } fn def_id_is_local(&mut self, def_id: DefId) -> bool { match self.in_crate { - InCrate::Local => def_id.is_local(), + InCrate::Local { .. } => def_id.is_local(), InCrate::Remote => false, } } @@ -855,23 +872,25 @@ where enum OrphanCheckEarlyExit<'tcx, E> { NormalizationFailure(E), - ParamTy(Ty<'tcx>), + UncoveredTyParam(Ty<'tcx>), LocalTy(Ty<'tcx>), } -impl<'tcx, F, E> TypeVisitor> for OrphanChecker<'tcx, F> +impl<'a, 'tcx, F, E> TypeVisitor> for OrphanChecker<'a, 'tcx, F> where F: FnMut(Ty<'tcx>) -> Result, E>, { type Result = ControlFlow>; + fn visit_region(&mut self, _r: ty::Region<'tcx>) -> Self::Result { ControlFlow::Continue(()) } fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - // Need to lazily normalize here in with `-Znext-solver=coherence`. + let ty = self.infcx.shallow_resolve(ty); let ty = match (self.lazily_normalize_ty)(ty) { - Ok(ty) => ty, + Ok(norm_ty) if norm_ty.is_ty_var() => ty, + Ok(norm_ty) => norm_ty, Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), }; @@ -883,24 +902,52 @@ where | ty::Float(..) | ty::Str | ty::FnDef(..) + | ty::Pat(..) | ty::FnPtr(_) | ty::Array(..) | ty::Slice(..) | ty::RawPtr(..) | ty::Never - | ty::Tuple(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => { - self.found_non_local_ty(ty) + | ty::Tuple(..) => self.found_non_local_ty(ty), + + ty::Param(..) => bug!("unexpected ty param"), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { + match self.in_crate { + InCrate::Local { .. } => self.found_uncovered_ty_param(ty), + // The inference variable might be unified with a local + // type in that remote crate. + InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + } } - ty::Param(..) => self.found_param_ty(ty), + ty::Alias(kind @ (ty::Projection | ty::Inherent | ty::Weak), ..) => { + if ty.has_type_flags(ty::TypeFlags::HAS_TY_PARAM) { + bug!("unexpected ty param in alias ty"); + } - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate { - InCrate::Local => self.found_non_local_ty(ty), - // The inference variable might be unified with a local - // type in that remote crate. - InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - }, + if ty.has_type_flags( + ty::TypeFlags::HAS_TY_PLACEHOLDER + | ty::TypeFlags::HAS_TY_BOUND + | ty::TypeFlags::HAS_TY_INFER, + ) { + match self.in_crate { + InCrate::Local { mode } => match kind { + ty::Projection if let OrphanCheckMode::Compat = mode => { + ControlFlow::Continue(()) + } + _ => self.found_uncovered_ty_param(ty), + }, + InCrate::Remote => { + // The inference variable might be unified with a local + // type in that remote crate. + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } + } + } else { + ControlFlow::Continue(()) + } + } // For fundamental types, we just look inside of them. ty::Ref(_, ty, _) => ty.visit_with(self), @@ -1021,10 +1068,14 @@ struct AmbiguityCausesVisitor<'a, 'tcx> { } impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { + fn span(&self) -> Span { + DUMMY_SP + } + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) { let infcx = goal.infcx(); for cand in goal.candidates() { - cand.visit_nested(self); + cand.visit_nested_in_probe(self); } // When searching for intercrate ambiguity causes, we only need to look // at ambiguous goals, as for others the coherence unknowable candidate @@ -1073,9 +1124,8 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { // Add ambiguity causes for unknowable goals. let mut ambiguity_cause = None; for cand in goal.candidates() { - // FIXME: boiiii, using string comparisions here sure is scuffed. - if let inspect::ProbeKind::MiscCandidate { - name: "coherence unknowable", + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::CoherenceUnknowable, result: Ok(_), } = cand.kind() { @@ -1098,7 +1148,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { }; infcx.probe(|_| { - match trait_ref_is_knowable(infcx.tcx, trait_ref, lazily_normalize_ty) { + match trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty) { Err(()) => {} Ok(Ok(())) => warn!("expected an unknowable trait ref: {trait_ref:?}"), Ok(Err(conflict)) => { @@ -1156,5 +1206,5 @@ fn search_ambiguity_causes<'tcx>( goal: Goal<'tcx, ty::Predicate<'tcx>>, causes: &mut FxIndexSet>, ) { - infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes }); + infcx.probe(|_| infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes })); } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 7f0f9a12d6a05..a8be5627fed81 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -8,15 +8,14 @@ //! In this case we try to build an abstract representation of this constant using //! `thir_abstract_const` which can then be checked for structural equality with other //! generic constants mentioned in the `caller_bounds` of the current environment. + use rustc_hir::def::DefKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::interpret::ErrorHandled; - use rustc_middle::traits::ObligationCause; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor}; - -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; use crate::traits::ObligationCtxt; @@ -68,7 +67,7 @@ pub fn is_const_evaluatable<'tcx>( tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); } ty::ConstKind::Unevaluated(uv) => { - let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); + let concrete = infcx.const_eval_resolve(param_env, uv, span); match concrete { Err(ErrorHandled::TooGeneric(_)) => { Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( @@ -99,7 +98,7 @@ pub fn is_const_evaluatable<'tcx>( // and hopefully soon change this to an error. // // See #74595 for more details about this. - let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); + let concrete = infcx.const_eval_resolve(param_env, uv, span); match concrete { // If we're evaluating a generic foreign constant, under a nightly compiler while // the current crate does not enable `feature(generic_const_exprs)`, abort @@ -116,12 +115,12 @@ pub fn is_const_evaluatable<'tcx>( tcx.dcx() .struct_span_fatal( // Slightly better span than just using `span` alone - if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def) } else { span }, + if span == DUMMY_SP { tcx.def_span(uv.def) } else { span }, "failed to evaluate generic const expression", ) .with_note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") .with_span_suggestion_verbose( - rustc_span::DUMMY_SP, + DUMMY_SP, "consider enabling this feature", "#![feature(generic_const_exprs)]\n", rustc_errors::Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 9fbec174ce8d2..432d51ff8f90b 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -19,6 +19,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_infer::traits::{ FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _, }; +use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::error::TypeError; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs index c6f5af08013e6..e6cb28df593af 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -5,20 +5,24 @@ use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation}; use rustc_middle::ty; use rustc_span::{Span, DUMMY_SP}; +use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::ObligationCtxt; -pub enum Ambiguity { +#[derive(Debug)] +pub enum CandidateSource { DefId(DefId), ParamEnv(Span), } -pub fn recompute_applicable_impls<'tcx>( +pub fn compute_applicable_impls_for_diagnostics<'tcx>( infcx: &InferCtxt<'tcx>, obligation: &PolyTraitObligation<'tcx>, -) -> Vec { +) -> Vec { let tcx = infcx.tcx; let param_env = obligation.param_env; + let predicate_polarity = obligation.predicate.skip_binder().polarity; + let impl_may_apply = |impl_def_id| { let ocx = ObligationCtxt::new(infcx); infcx.enter_forall(obligation.predicate, |placeholder_obligation| { @@ -40,10 +44,30 @@ pub fn recompute_applicable_impls<'tcx>( return false; } - let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args); - ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| { - Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) - })); + let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap(); + let impl_polarity = impl_trait_header.polarity; + + match (impl_polarity, predicate_polarity) { + (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {} + _ => return false, + } + + let obligations = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .into_iter() + .map(|(predicate, _)| { + Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) + }) + // Kinda hacky, but let's just throw away obligations that overflow. + // This may reduce the accuracy of this check (if the obligation guides + // inference or it actually resulted in error after others are processed) + // ... but this is diagnostics code. + .filter(|obligation| { + infcx.next_trait_solver() || infcx.evaluate_obligation(obligation).is_ok() + }); + ocx.register_obligations(obligations); ocx.select_where_possible().is_empty() }) @@ -86,7 +110,7 @@ pub fn recompute_applicable_impls<'tcx>( obligation.predicate.skip_binder().trait_ref.self_ty(), |impl_def_id| { if infcx.probe(|_| impl_may_apply(impl_def_id)) { - ambiguities.push(Ambiguity::DefId(impl_def_id)) + ambiguities.push(CandidateSource::DefId(impl_def_id)) } }, ); @@ -101,9 +125,9 @@ pub fn recompute_applicable_impls<'tcx>( if kind.rebind(trait_pred.trait_ref) == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) { - ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id()))) + ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id()))) } else { - ambiguities.push(Ambiguity::ParamEnv(span)) + ambiguities.push(CandidateSource::ParamEnv(span)) } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs index cbe2ec0f0eb8c..9e5701ffffc60 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs @@ -1,9 +1,10 @@ -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::type_variable::TypeVariableOrigin; use crate::infer::InferCtxt; use crate::traits::{Obligation, ObligationCause, ObligationCtxt}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag}; use rustc_hir as hir; use rustc_hir::Node; +use rustc_macros::extension; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, DUMMY_SP}; @@ -206,7 +207,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, param_env: ty::ParamEnv<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>, - polarity: ty::ImplPolarity, + polarity: ty::PredicatePolarity, ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { self.commit_if_ok(|_| { for trait_def_id in [ @@ -217,10 +218,8 @@ impl<'tcx> InferCtxt<'tcx> { let Some(trait_def_id) = trait_def_id else { continue }; // Make a fresh inference variable so we can determine what the generic parameters // of the trait are. - let var = self.next_ty_var(TypeVariableOrigin { - span: DUMMY_SP, - kind: TypeVariableOriginKind::MiscVariable, - }); + let var = + self.next_ty_var(TypeVariableOrigin { span: DUMMY_SP, param_def_id: None }); // FIXME(effects) let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); let obligation = Obligation::new( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 0515b09ae46a0..e50edfdc656ce 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1,6 +1,6 @@ // ignore-tidy-filelength :( -mod ambiguity; +pub mod ambiguity; mod infer_ctxt_ext; pub mod on_unimplemented; pub mod suggestions; @@ -40,7 +40,7 @@ pub struct ImplCandidate<'tcx> { enum GetSafeTransmuteErrorAndReason { Silent, - Error { err_msg: String, safe_transmute_explanation: String }, + Error { err_msg: String, safe_transmute_explanation: Option }, } struct UnsatisfiedConst(pub bool); @@ -50,22 +50,38 @@ pub struct FindExprBySpan<'hir> { pub span: Span, pub result: Option<&'hir hir::Expr<'hir>>, pub ty_result: Option<&'hir hir::Ty<'hir>>, + pub include_closures: bool, + pub tcx: TyCtxt<'hir>, } impl<'hir> FindExprBySpan<'hir> { - pub fn new(span: Span) -> Self { - Self { span, result: None, ty_result: None } + pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self { + Self { span, result: None, ty_result: None, tcx, include_closures: false } } } impl<'v> Visitor<'v> for FindExprBySpan<'v> { + type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { if self.span == ex.span { self.result = Some(ex); } else { + if let hir::ExprKind::Closure(..) = ex.kind + && self.include_closures + && let closure_header_sp = self.span.with_hi(ex.span.hi()) + && closure_header_sp == ex.span + { + self.result = Some(ex); + } hir::intravisit::walk_expr(self, ex); } } + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { if self.span == ty.span { self.ty_result = Some(ty); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 745ddfc08af47..07bd209e62394 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -9,6 +9,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_macros::{extension, LintDiagnostic}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; @@ -128,7 +129,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { match obligation.cause.code() { ObligationCauseCode::BuiltinDerivedObligation(..) | ObligationCauseCode::ImplDerivedObligation(..) - | ObligationCauseCode::DerivedObligation(..) => {} + | ObligationCauseCode::WellFormedDerivedObligation(..) => {} _ => { // this is a "direct", user-specified, rather than derived, // obligation. @@ -367,6 +368,23 @@ pub struct UnknownFormatParameterForOnUnimplementedAttr { trait_name: Symbol, } +#[derive(LintDiagnostic)] +#[diag(trait_selection_disallowed_positional_argument)] +#[help] +pub struct DisallowedPositionalArgument; + +#[derive(LintDiagnostic)] +#[diag(trait_selection_invalid_format_specifier)] +#[help] +pub struct InvalidFormatSpecifier; + +#[derive(LintDiagnostic)] +#[diag(trait_selection_wrapped_parser_error)] +pub struct WrappedParserError { + description: String, + label: String, +} + impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -758,64 +776,108 @@ impl<'tcx> OnUnimplementedFormatString { let trait_name = tcx.item_name(trait_def_id); let generics = tcx.generics_of(item_def_id); let s = self.symbol.as_str(); - let parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut parser = Parser::new(s, None, None, false, ParseMode::Format); let mut result = Ok(()); - for token in parser { + for token in &mut parser { match token { Piece::String(_) => (), // Normal string, no need to check it - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => { - match Symbol::intern(s) { - // `{ThisTraitsName}` is allowed - s if s == trait_name && !self.is_diagnostic_namespace_variant => (), - s if ALLOWED_FORMAT_SYMBOLS.contains(&s) - && !self.is_diagnostic_namespace_variant => - { - () - } - // So is `{A}` if A is a type parameter - s if generics.params.iter().any(|param| param.name == s) => (), - s => { - if self.is_diagnostic_namespace_variant { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id.expect_local()), - self.span, - UnknownFormatParameterForOnUnimplementedAttr { - argument_name: s, - trait_name, - }, - ); - } else { - result = Err(struct_span_code_err!( - tcx.dcx(), - self.span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{trait_name}`") - } else { - "impl".to_string() - } - ) - .emit()); + Piece::NextArgument(a) => { + let format_spec = a.format; + if self.is_diagnostic_namespace_variant + && (format_spec.ty_span.is_some() + || format_spec.width_span.is_some() + || format_spec.precision_span.is_some() + || format_spec.fill_span.is_some()) + { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + InvalidFormatSpecifier, + ); + } + match a.position { + Position::ArgumentNamed(s) => { + match Symbol::intern(s) { + // `{ThisTraitsName}` is allowed + s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if ALLOWED_FORMAT_SYMBOLS.contains(&s) + && !self.is_diagnostic_namespace_variant => + { + () + } + // So is `{A}` if A is a type parameter + s if generics.params.iter().any(|param| param.name == s) => (), + s => { + if self.is_diagnostic_namespace_variant { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + UnknownFormatParameterForOnUnimplementedAttr { + argument_name: s, + trait_name, + }, + ); + } else { + result = Err(struct_span_code_err!( + tcx.dcx(), + self.span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{trait_name}`") + } else { + "impl".to_string() + } + ) + .emit()); + } } } } + // `{:1}` and `{}` are not to be used + Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { + if self.is_diagnostic_namespace_variant { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + DisallowedPositionalArgument, + ); + } else { + let reported = struct_span_code_err!( + tcx.dcx(), + self.span, + E0231, + "only named generic parameters are allowed" + ) + .emit(); + result = Err(reported); + } + } } - // `{:1}` and `{}` are not to be used - Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { - let reported = struct_span_code_err!( - tcx.dcx(), - self.span, - E0231, - "only named generic parameters are allowed" - ) - .emit(); - result = Err(reported); - } - }, + } + } + } + // we cannot return errors from processing the format string as hard error here + // as the diagnostic namespace gurantees that malformed input cannot cause an error + // + // if we encounter any error while processing we nevertheless want to show it as warning + // so that users are aware that something is not correct + for e in parser.errors { + if self.is_diagnostic_namespace_variant { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + WrappedParserError { description: e.description, label: e.label }, + ); + } else { + let reported = + struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit(); + result = Err(reported); } } @@ -853,9 +915,9 @@ impl<'tcx> OnUnimplementedFormatString { let empty_string = String::new(); let s = self.symbol.as_str(); - let parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut parser = Parser::new(s, None, None, false, ParseMode::Format); let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); - parser + let constructed_message = (&mut parser) .map(|p| match p { Piece::String(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { @@ -895,9 +957,29 @@ impl<'tcx> OnUnimplementedFormatString { } } } + Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => { + String::from("{}") + } + Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => { + format!("{{{idx}}}") + } _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol), }, }) - .collect() + .collect(); + // we cannot return errors from processing the format string as hard error here + // as the diagnostic namespace gurantees that malformed input cannot cause an error + // + // if we encounter any error while processing the format string + // we don't want to show the potentially half assembled formated string, + // therefore we fall back to just showing the input string in this case + // + // The actual parser errors are emitted earlier + // as lint warnings in OnUnimplementedFormatString::verify + if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() { + String::from(s) + } else { + constructed_message + } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 067ca883bd8ad..3d2574ac92b66 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -24,15 +24,16 @@ use rustc_hir::is_range_literal; use rustc_hir::lang_items::LangItem; use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; +use rustc_macros::extension; use rustc_middle::hir::map; use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, - InferTy, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, TypeckResults, + InferTy, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, TypeckResults, }; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -124,7 +125,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( msg: &str, err: &mut Diag<'_, G>, fn_sig: Option<&hir::FnSig<'_>>, - projection: Option<&ty::AliasTy<'_>>, + projection: Option>, trait_pred: ty::PolyTraitPredicate<'tcx>, // When we are dealing with a trait, `super_traits` will be `Some`: // Given `trait T: A + B + C {}` @@ -142,7 +143,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( let generics = tcx.generics_of(item_id); // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`... if let Some((param, bound_str, fn_sig)) = - fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() { + fn_sig.zip(projection).and_then(|(sig, p)| match *p.self_ty().kind() { // Shenanigans to get the `Trait` from the `impl Trait`. ty::Param(param) => { let param_def = generics.type_param(param, tcx); @@ -194,8 +195,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string()))); // Suggest `fn foo(t: T) where ::A: Bound`. - // FIXME: once `#![feature(associated_type_bounds)]` is stabilized, we should suggest - // `fn foo(t: impl Trait)` instead. + // FIXME: we should suggest `fn foo(t: impl Trait)` instead. err.multipart_suggestion( "introduce a type parameter with a trait bound instead of using `impl Trait`", sugg, @@ -246,14 +246,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, ) { - if trait_pred.skip_binder().polarity == ty::ImplPolarity::Negative { + if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive { return; } let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); let self_ty = trait_pred.skip_binder().self_ty(); - let (param_ty, projection) = match self_ty.kind() { + let (param_ty, projection) = match *self_ty.kind() { ty::Param(_) => (true, None), ty::Alias(ty::Projection, projection) => (false, Some(projection)), _ => (false, None), @@ -483,7 +483,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let Some(steps) = autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { // Re-add the `&` - let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl }); + let ty = Ty::new_ref(self.tcx, region, ty, mutbl); // Remapping bound vars here let real_trait_pred_and_ty = real_trait_pred @@ -752,9 +752,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Get the local name of this closure. This can be inaccurate because // of the possibility of reassignment, but this should be good enough. match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => { - Some(ident.name) - } + hir::PatKind::Binding(hir::BindingMode::NONE, _, ident, None) => Some(ident.name), _ => { err.note(msg); None @@ -769,7 +767,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } // Different to previous arm because one is `&hir::Local` and the other // is `P`. - hir::Node::Local(local) => get_name(err, &local.pat.kind), + hir::Node::LetStmt(local) => get_name(err, &local.pat.kind), _ => None, } } @@ -904,7 +902,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Remove all the desugaring and macro contexts. span.remove_mark(); } - let mut expr_finder = FindExprBySpan::new(span); + let mut expr_finder = FindExprBySpan::new(span, self.tcx); let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return; }; @@ -931,7 +929,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { return; }; - let hir::Node::Local(hir::Local { ty: None, init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) = self.tcx.parent_hir_node(pat.hir_id) else { return; @@ -1099,8 +1097,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { )) } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - self.tcx.item_bounds(def_id).instantiate(self.tcx, args).iter().find_map( - |pred| { + self.tcx + .item_super_predicates(def_id) + .instantiate(self.tcx, args) + .iter() + .find_map(|pred| { if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() // args tuple will always be args[1] @@ -1114,8 +1115,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } else { None } - }, - ) + }) } ty::Dynamic(data, _, ty::Dyn) => { data.iter().find_map(|pred| { @@ -1368,7 +1368,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return false; }; let body = self.tcx.hir().body(body_id); - let mut expr_finder = FindExprBySpan::new(span); + let mut expr_finder = FindExprBySpan::new(span, self.tcx); expr_finder.visit_expr(body.value); let Some(expr) = expr_finder.result else { return false; @@ -1470,7 +1470,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Remove all the hir desugaring contexts while maintaining the macro contexts. span.remove_mark(); } - let mut expr_finder = super::FindExprBySpan::new(span); + let mut expr_finder = super::FindExprBySpan::new(span, self.tcx); let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return false; }; @@ -1561,7 +1561,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let Res::Local(hir_id) = path.res && let hir::Node::Pat(binding) = self.tcx.hir_node(hir_id) - && let hir::Node::Local(local) = self.tcx.parent_hir_node(binding.hir_id) + && let hir::Node::LetStmt(local) = self.tcx.parent_hir_node(binding.hir_id) && let None = local.ty && let Some(binding_expr) = local.init { @@ -1591,8 +1591,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .tcx .sess .source_map() - .span_extend_while(expr_span, char::is_whitespace) - .unwrap_or(expr_span) + .span_extend_while_whitespace(expr_span) .shrink_to_hi() .to(await_expr.span.shrink_to_hi()); err.span_suggestion( @@ -1879,25 +1878,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, span: Span, found_span: Option, - found: ty::PolyTraitRef<'tcx>, - expected: ty::PolyTraitRef<'tcx>, + found: ty::TraitRef<'tcx>, + expected: ty::TraitRef<'tcx>, cause: &ObligationCauseCode<'tcx>, found_node: Option>, param_env: ty::ParamEnv<'tcx>, ) -> Diag<'tcx> { pub(crate) fn build_fn_sig_ty<'tcx>( infcx: &InferCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, ) -> Ty<'tcx> { - let inputs = trait_ref.skip_binder().args.type_at(1); + let inputs = trait_ref.args.type_at(1); let sig = match inputs.kind() { - ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id()) => { + ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id) => { infcx.tcx.mk_fn_sig( *inputs, - infcx.next_ty_var(TypeVariableOrigin { - span: DUMMY_SP, - kind: TypeVariableOriginKind::MiscVariable, - }), + infcx + .next_ty_var(TypeVariableOrigin { span: DUMMY_SP, param_def_id: None }), false, hir::Unsafety::Normal, abi::Abi::Rust, @@ -1905,20 +1902,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } _ => infcx.tcx.mk_fn_sig( [inputs], - infcx.next_ty_var(TypeVariableOrigin { - span: DUMMY_SP, - kind: TypeVariableOriginKind::MiscVariable, - }), + infcx.next_ty_var(TypeVariableOrigin { span: DUMMY_SP, param_def_id: None }), false, hir::Unsafety::Normal, abi::Abi::Rust, ), }; - Ty::new_fn_ptr(infcx.tcx, trait_ref.rebind(sig)) + Ty::new_fn_ptr(infcx.tcx, ty::Binder::dummy(sig)) } - let argument_kind = match expected.skip_binder().self_ty().kind() { + let argument_kind = match expected.self_ty().kind() { ty::Closure(..) => "closure", ty::Coroutine(..) => "coroutine", _ => "function", @@ -2301,7 +2295,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { next_code = Some(&cause.derived.parent_code); } - ObligationCauseCode::DerivedObligation(derived_obligation) + ObligationCauseCode::WellFormedDerivedObligation(derived_obligation) | ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) => { let ty = derived_obligation.parent_trait_pred.skip_binder().self_ty(); debug!( @@ -2954,8 +2948,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::VariableType(hir_id) => { + if let Some(typeck_results) = &self.typeck_results + && let Some(ty) = typeck_results.node_type_opt(hir_id) + && let ty::Error(_) = ty.kind() + { + err.note(format!( + "`{predicate}` isn't satisfied, but the type of this pattern is \ + `{{type error}}`", + )); + err.downgrade_to_delayed_bug(); + } match tcx.parent_hir_node(hir_id) { - Node::Local(hir::Local { ty: Some(ty), .. }) => { + Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => { err.span_suggestion_verbose( ty.span.shrink_to_lo(), "consider borrowing here", @@ -2964,7 +2968,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err.note("all local variables must have a statically known size"); } - Node::Local(hir::Local { + Node::LetStmt(hir::LetStmt { init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }), .. }) => { @@ -3200,71 +3204,69 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }; - // Don't print the tuple of capture types - 'print: { - if !is_upvar_tys_infer_tuple { - let ty_str = tcx.short_ty_string(ty, &mut long_ty_file); - let msg = format!("required because it appears within the type `{ty_str}`"); - match ty.kind() { - ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) { - Some(ident) => err.span_note(ident.span, msg), - None => err.note(msg), - }, - ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { - // If the previous type is async fn, this is the future generated by the body of an async function. - // Avoid printing it twice (it was already printed in the `ty::Coroutine` arm below). - let is_future = tcx.ty_is_opaque_future(ty); - debug!( - ?obligated_types, - ?is_future, - "note_obligation_cause_code: check for async fn" - ); - if is_future - && obligated_types.last().is_some_and(|ty| match ty.kind() { - ty::Coroutine(last_def_id, ..) => { - tcx.coroutine_is_async(*last_def_id) - } - _ => false, - }) - { - break 'print; - } - err.span_note(tcx.def_span(def_id), msg) + if !is_upvar_tys_infer_tuple { + let ty_str = tcx.short_ty_string(ty, &mut long_ty_file); + let msg = format!("required because it appears within the type `{ty_str}`"); + match ty.kind() { + ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) { + Some(ident) => { + err.span_note(ident.span, msg); } - ty::CoroutineWitness(def_id, args) => { - use std::fmt::Write; - - // FIXME: this is kind of an unusual format for rustc, can we make it more clear? - // Maybe we should just remove this note altogether? - // FIXME: only print types which don't meet the trait requirement - let mut msg = - "required because it captures the following types: ".to_owned(); - for bty in tcx.coroutine_hidden_types(*def_id) { - let ty = bty.instantiate(tcx, args); - write!(msg, "`{ty}`, ").unwrap(); - } - err.note(msg.trim_end_matches(", ").to_string()) + None => { + err.note(msg); } - ty::Coroutine(def_id, _) => { - let sp = tcx.def_span(def_id); - - // Special-case this to say "async block" instead of `[static coroutine]`. - let kind = tcx.coroutine_kind(def_id).unwrap(); - err.span_note( - sp, - with_forced_trimmed_paths!(format!( - "required because it's used within this {kind:#}", - )), - ) + }, + ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { + // If the previous type is async fn, this is the future generated by the body of an async function. + // Avoid printing it twice (it was already printed in the `ty::Coroutine` arm below). + let is_future = tcx.ty_is_opaque_future(ty); + debug!( + ?obligated_types, + ?is_future, + "note_obligation_cause_code: check for async fn" + ); + if is_future + && obligated_types.last().is_some_and(|ty| match ty.kind() { + ty::Coroutine(last_def_id, ..) => { + tcx.coroutine_is_async(*last_def_id) + } + _ => false, + }) + { + // See comment above; skip printing twice. + } else { + err.span_note(tcx.def_span(def_id), msg); } - ty::Closure(def_id, _) => err.span_note( + } + ty::Coroutine(def_id, _) => { + let sp = tcx.def_span(def_id); + + // Special-case this to say "async block" instead of `[static coroutine]`. + let kind = tcx.coroutine_kind(def_id).unwrap(); + err.span_note( + sp, + with_forced_trimmed_paths!(format!( + "required because it's used within this {kind:#}", + )), + ); + } + ty::CoroutineWitness(..) => { + // Skip printing coroutine-witnesses, since we'll drill into + // the bad field in another derived obligation cause. + } + ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => { + err.span_note( tcx.def_span(def_id), "required because it's used within this closure", - ), - ty::Str => err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"), - _ => err.note(msg), - }; - } + ); + } + ty::Str => { + err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"); + } + _ => { + err.note(msg); + } + }; } obligated_types.push(ty); @@ -3422,7 +3424,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) }); } - ObligationCauseCode::DerivedObligation(ref data) => { + ObligationCauseCode::WellFormedDerivedObligation(ref data) => { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); let parent_predicate = parent_trait_ref; // #74711: avoid a stack overflow @@ -3510,9 +3512,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); - if tcx.sess.opts.unstable_features.is_nightly_build() { - err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable"); - } + tcx.disabled_nightly_features( + err, + Some(tcx.local_def_id_to_hir_id(body_id)), + [(String::new(), sym::trivial_bounds)], + ); } ObligationCauseCode::OpaqueReturnType(expr_info) => { if let Some((expr_ty, expr_span)) = expr_info { @@ -3831,7 +3835,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.probe(|_| { match self .at(&ObligationCause::misc(expr.span, body_id), param_env) - .eq(DefineOpaqueTypes::No, expected, actual) + // Doesn't actually matter if we define opaque types here, this is just used for + // diagnostics, and the result is never kept around. + .eq(DefineOpaqueTypes::Yes, expected, actual) { Ok(_) => (), // We ignore nested obligations here for now. Err(err) => type_diffs.push(err), @@ -3856,7 +3862,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: Res::Local(hir_id), .. } = path && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id) - && let hir::Node::Local(local) = self.tcx.parent_hir_node(binding.hir_id) + && let hir::Node::LetStmt(local) = self.tcx.parent_hir_node(binding.hir_id) && let Some(binding_expr) = local.init { // If the expression we're calling on is a binding, we want to point at the @@ -4046,7 +4052,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span, [*ty], ), - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }); let Some(generics) = node.generics() else { continue; @@ -4117,7 +4123,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { let parent = self.tcx.parent_hir_node(binding.hir_id); // We've reached the root of the method call chain... - if let hir::Node::Local(local) = parent + if let hir::Node::LetStmt(local) = parent && let Some(binding_expr) = local.init { // ...and it is a binding. Get the binding creation and continue the chain. @@ -4244,7 +4250,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { type_diffs: &[TypeError<'tcx>], span: Span, prev_ty: Ty<'tcx>, - body_id: hir::HirId, + body_id: HirId, param_env: ty::ParamEnv<'tcx>, ) -> Vec))>> { let ocx = ObligationCtxt::new(self.infcx); @@ -4257,7 +4263,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { continue; }; - let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; + let origin = TypeVariableOrigin { param_def_id: None, span }; // Make `Self` be equivalent to the type of the call chain // expression we're looking at now, so that we can tell what // for example `Iterator::Item` is at this point in the chain. @@ -4341,7 +4347,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Go through all the candidate impls to see if any of them is for // slices of `element_ty` with `mutability`. let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() { - ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => { + ty::RawPtr(t, m) | ty::Ref(_, t, m) => { if matches!(*t.kind(), ty::Slice(e) if e == element_ty) && m == mutability.unwrap_or(m) { @@ -4528,6 +4534,68 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Applicability::MachineApplicable, ); } + + fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option { + let tcx = self.infcx.tcx; + let implements_default = |ty| { + let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { + return false; + }; + self.type_implements_trait(default_trait, [ty], 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(param_env, 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(param_env, 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 ty = self.ty_kind_suggestion(param_env, ty)?; + format!("&{}{ty}", mutability.prefix_str()) + } + } + ty::Array(ty, len) if let Some(len) = len.try_eval_target_usize(tcx, param_env) => { + if len == 0 { + "[]".to_string() + } else if self.type_is_copy_modulo_regions(param_env, ty) || len == 1 { + // Can only suggest `[ty; 0]` if sz == 1 or copy + format!("[{}; {}]", self.ty_kind_suggestion(param_env, ty)?, len) + } else { + "/* value */".to_string() + } + } + ty::Tuple(tys) => format!( + "({}{})", + tys.iter() + .map(|ty| self.ty_kind_suggestion(param_env, ty)) + .collect::>>()? + .join(", "), + if tys.len() == 1 { "," } else { "" } + ), + _ => "/* value */".to_string(), + }) + } } /// Add a hint to add a missing borrow or remove an unnecessary one. @@ -4695,7 +4763,7 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> { /// Collect all the awaited expressions within the input expression. #[derive(Default)] struct AwaitsVisitor { - awaits: Vec, + awaits: Vec, } impl<'v> Visitor<'v> for AwaitsVisitor { @@ -4791,7 +4859,7 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( Some(desc) => format!(" {desc}"), None => String::new(), }; - if let ty::ImplPolarity::Positive = trait_predicate.polarity() { + if let ty::PredicatePolarity::Positive = trait_predicate.polarity() { format!( "{pre_message}the trait `{}` is not implemented for{desc} `{}`{post}", trait_predicate.print_modifiers_and_trait_path(), @@ -4838,10 +4906,7 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( let hir::IsAsync::Async(async_span) = sig.header.asyncness else { return None; }; - let Ok(async_span) = tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace()) - else { - return None; - }; + let async_span = tcx.sess.source_map().span_extend_while_whitespace(async_span); let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else { @@ -4920,7 +4985,7 @@ fn point_at_assoc_type_restriction( let hir::WherePredicate::BoundPredicate(pred) = pred else { continue; }; - let mut bounds = pred.bounds.iter().peekable(); + let mut bounds = pred.bounds.iter(); while let Some(bound) = bounds.next() { let Some(trait_ref) = bound.trait_ref() else { continue; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 4bc3ff92a6751..e50cb2af4b857 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -6,11 +6,11 @@ use crate::errors::{ AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::type_variable::TypeVariableOrigin; use crate::infer::InferCtxtExt as _; use crate::infer::{self, InferCtxt}; use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt; -use crate::traits::error_reporting::{ambiguity, ambiguity::Ambiguity::*}; +use crate::traits::error_reporting::{ambiguity, ambiguity::CandidateSource::*}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::specialize::to_pretty_impl_header; use crate::traits::NormalizeExt; @@ -21,6 +21,7 @@ use crate::traits::{ }; use core::ops::ControlFlow; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart}; use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey}; @@ -31,6 +32,7 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; +use rustc_macros::extension; use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::SignatureMismatchData; use rustc_middle::ty::abstract_const::NotConstEvaluatable; @@ -557,7 +559,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation, - } => (err_msg, Some(safe_transmute_explanation)), + } => (err_msg, safe_transmute_explanation), } } else { (err_msg, None) @@ -779,7 +781,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && self.fallback_has_occurred { let predicate = trait_predicate.map_bound(|trait_pred| { - trait_pred.with_self_ty(self.tcx, Ty::new_unit(self.tcx)) + trait_pred.with_self_ty(self.tcx, tcx.types.unit) }); let unit_obligation = obligation.with(tcx, predicate); if self.predicate_may_hold(&unit_obligation) { @@ -983,7 +985,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Already reported in the query. SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(guar)) | // Already reported. - Overflow(OverflowError::Error(guar)) => return guar, + Overflow(OverflowError::Error(guar)) => { + self.set_tainted_by_errors(guar); + return guar + }, Overflow(_) => { bug!("overflow should be handled before the `report_selection_error` path"); @@ -1127,10 +1132,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err: &mut Diag<'_>, ) -> bool { let span = obligation.cause.span; - struct V { + /// Look for the (direct) sub-expr of `?`, and return it if it's a `.` method call. + struct FindMethodSubexprOfTry { search_span: Span, } - impl<'v> Visitor<'v> for V { + impl<'v> Visitor<'v> for FindMethodSubexprOfTry { type Result = ControlFlow<&'v hir::Expr<'v>>; fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result { if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind @@ -1148,8 +1154,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id, _ => return false, }; - let ControlFlow::Break(expr) = - (V { search_span: span }).visit_body(self.tcx.hir().body(*body_id)) + let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span }) + .visit_body(self.tcx.hir().body(*body_id)) else { return false; }; @@ -1272,7 +1278,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { let parent = self.tcx.parent_hir_node(binding.hir_id); // We've reached the root of the method call chain... - if let hir::Node::Local(local) = parent + if let hir::Node::LetStmt(local) = parent && let Some(binding_expr) = local.init { // ...and it is a binding. Get the binding creation and continue the chain. @@ -1357,7 +1363,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "using function pointers as const generic parameters is forbidden", ) } - ty::RawPtr(_) => { + ty::RawPtr(_, _) => { struct_span_code_err!( self.dcx(), span, @@ -1802,6 +1808,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ty::Foreign(..) => Some(19), ty::CoroutineWitness(..) => Some(20), ty::CoroutineClosure(..) => Some(21), + ty::Pat(..) => Some(22), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } @@ -1809,9 +1816,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let strip_references = |mut t: Ty<'tcx>| -> Ty<'tcx> { loop { match t.kind() { - ty::Ref(_, inner, _) | ty::RawPtr(ty::TypeAndMut { ty: inner, .. }) => { - t = *inner - } + ty::Ref(_, inner, _) | ty::RawPtr(inner, _) => t = *inner, _ => break t, } } @@ -1907,7 +1912,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .all_impls(trait_pred.def_id()) .filter_map(|def_id| { let imp = self.tcx.impl_trait_header(def_id).unwrap(); - if imp.polarity == ty::ImplPolarity::Negative + if imp.polarity != ty::ImplPolarity::Positive || !self.tcx.is_user_visible_dep(def_id.krate) { return None; @@ -1973,6 +1978,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { for (obligation_arg, impl_arg) in std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args) { + if (obligation_arg, impl_arg).references_error() { + return false; + } if let Err(terr) = ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg) { @@ -2119,7 +2127,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }) .collect(); - impl_candidates.sort(); + impl_candidates.sort_by_key(|tr| tr.to_string()); impl_candidates.dedup(); return report(impl_candidates, err); } @@ -2145,7 +2153,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { cand }) .collect(); - impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref)); + impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref.to_string())); let mut impl_candidates: Vec<_> = impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(); impl_candidates.dedup(); @@ -2245,14 +2253,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); - let traits_with_same_path: std::collections::BTreeSet<_> = self + let traits_with_same_path: UnordSet<_> = self .tcx .all_traits() .filter(|trait_def_id| *trait_def_id != trait_ref.def_id()) - .filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path) + .map(|trait_def_id| (self.tcx.def_path_str(trait_def_id), trait_def_id)) + .filter(|(p, _)| *p == required_trait_path) .collect(); + + let traits_with_same_path = + traits_with_same_path.into_items().into_sorted_stable_ord_by_key(|(p, _)| p); let mut suggested = false; - for trait_with_same_path in traits_with_same_path { + for (_, trait_with_same_path) in traits_with_same_path { let trait_impls = get_trait_impls(trait_with_same_path); if trait_impls.is_empty() { continue; @@ -2382,7 +2394,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) }; - let mut ambiguities = ambiguity::recompute_applicable_impls( + let mut ambiguities = ambiguity::compute_applicable_impls_for_diagnostics( self.infcx, &obligation.with(self.tcx, trait_ref), ); @@ -2394,12 +2406,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if ambiguities.len() > 5 { let infcx = self.infcx; if !ambiguities.iter().all(|option| match option { - DefId(did) => infcx.fresh_args_for_item(DUMMY_SP, *did).is_empty(), + DefId(did) => infcx.tcx.generics_of(*did).count() == 0, ParamEnv(_) => true, }) { // If not all are blanket impls, we filter blanked impls out. ambiguities.retain(|option| match option { - DefId(did) => infcx.fresh_args_for_item(DUMMY_SP, *did).is_empty(), + DefId(did) => infcx.tcx.generics_of(*did).count() == 0, ParamEnv(_) => true, }); } @@ -2446,7 +2458,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) { - let mut expr_finder = FindExprBySpan::new(span); + let mut expr_finder = FindExprBySpan::new(span, self.tcx); expr_finder.visit_expr(self.tcx.hir().body(body_id).value); if let Some(hir::Expr { @@ -2698,7 +2710,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diag<'_>, - ambiguities: &[ambiguity::Ambiguity], + ambiguities: &[ambiguity::CandidateSource], predicate: ty::Predicate<'tcx>, ) { let mut spans = vec![]; @@ -2707,7 +2719,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut has_param_env = false; for ambiguity in ambiguities { match ambiguity { - ambiguity::Ambiguity::DefId(impl_def_id) => { + ambiguity::CandidateSource::DefId(impl_def_id) => { match self.tcx.span_of_impl(*impl_def_id) { Ok(span) => spans.push(span), Err(name) => { @@ -2718,7 +2730,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } } - ambiguity::Ambiguity::ParamEnv(span) => { + ambiguity::CandidateSource::ParamEnv(span) => { has_param_env = true; spans.push(*span); } @@ -2815,10 +2827,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let ty::Param(_) = *ty.kind() { let infcx = self.infcx; *self.var_map.entry(ty).or_insert_with(|| { - infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }) + infcx.next_ty_var(TypeVariableOrigin { param_def_id: None, span: DUMMY_SP }) }) } else { ty.super_fold_with(self) @@ -2930,17 +2939,28 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } _ => {} }; + // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. - let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param.def_id) - { - (s, " +") + let (span, separator, open_paren_sp) = + if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) { + (s, " +", open_paren_sp) + } else { + (param.name.ident().span.shrink_to_hi(), ":", None) + }; + + let mut suggs = vec![]; + let suggestion = format!("{separator} ?Sized"); + + if let Some(open_paren_sp) = open_paren_sp { + suggs.push((open_paren_sp, "(".to_string())); + suggs.push((span, format!("){suggestion}"))); } else { - (param.name.ident().span.shrink_to_hi(), ":") - }; - err.span_suggestion_verbose( - span, + suggs.push((span, suggestion)); + } + + err.multipart_suggestion_verbose( "consider relaxing the implicit `Sized` restriction", - format!("{separator} ?Sized"), + suggs, Applicability::MachineApplicable, ); } @@ -3063,28 +3083,33 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return GetSafeTransmuteErrorAndReason::Silent; }; + let dst = trait_ref.args.type_at(0); + let src = trait_ref.args.type_at(1); + let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); + match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( obligation.cause, src_and_dst, assume, ) { Answer::No(reason) => { - let dst = trait_ref.args.type_at(0); - let src = trait_ref.args.type_at(1); - let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); let safe_transmute_explanation = match reason { rustc_transmute::Reason::SrcIsNotYetSupported => { - format!("analyzing the transmutability of `{src}` is not yet supported.") + format!("analyzing the transmutability of `{src}` is not yet supported") } rustc_transmute::Reason::DstIsNotYetSupported => { - format!("analyzing the transmutability of `{dst}` is not yet supported.") + format!("analyzing the transmutability of `{dst}` is not yet supported") } rustc_transmute::Reason::DstIsBitIncompatible => { format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`") } + rustc_transmute::Reason::DstUninhabited => { + format!("`{dst}` is uninhabited") + } + rustc_transmute::Reason::DstMayHaveSafetyInvariants => { format!("`{dst}` may carry safety invariants") } @@ -3130,14 +3155,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { format!("`{dst}` has an unknown layout") } }; - GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation } + GetSafeTransmuteErrorAndReason::Error { + err_msg, + safe_transmute_explanation: Some(safe_transmute_explanation), + } } // Should never get a Yes at this point! We already ran it before, and did not get a Yes. Answer::Yes => span_bug!( span, "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", ), - other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"), + // Reached when a different obligation (namely `Freeze`) causes the + // transmutability analysis to fail. In this case, silence the + // transmutability error message in favor of that more specific + // error. + Answer::If(_) => { + GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None } + } } } @@ -3361,11 +3395,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn report_cyclic_signature_error( &self, obligation: &PredicateObligation<'tcx>, - found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + found_trait_ref: ty::TraitRef<'tcx>, + expected_trait_ref: ty::TraitRef<'tcx>, terr: TypeError<'tcx>, ) -> Diag<'tcx> { - let self_ty = found_trait_ref.self_ty().skip_binder(); + let self_ty = found_trait_ref.self_ty(); let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() { ( ObligationCause::dummy_with_span(self.tcx.def_span(def_id)), @@ -3375,7 +3409,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (obligation.cause.clone(), terr) }; self.report_and_explain_type_error( - TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref), + TypeTrace::trait_refs(&cause, true, expected_trait_ref, found_trait_ref), terr, ) } @@ -3415,17 +3449,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, span: Span, - found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + found_trait_ref: ty::TraitRef<'tcx>, + expected_trait_ref: ty::TraitRef<'tcx>, ) -> Result, ErrorGuaranteed> { let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref); let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref); expected_trait_ref.self_ty().error_reported()?; - - let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else { - self.dcx().bug("bound vars outside binder"); - }; + let found_trait_ty = found_trait_ref.self_ty(); let found_did = match *found_trait_ty.kind() { ty::Closure(did, _) | ty::FnDef(did, _) | ty::Coroutine(did, ..) => Some(did), @@ -3443,7 +3474,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut not_tupled = false; - let found = match found_trait_ref.skip_binder().args.type_at(1).kind() { + let found = match found_trait_ref.args.type_at(1).kind() { ty::Tuple(tys) => vec![ArgKind::empty(); tys.len()], _ => { not_tupled = true; @@ -3451,7 +3482,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }; - let expected_ty = expected_trait_ref.skip_binder().args.type_at(1); + let expected_ty = expected_trait_ref.args.type_at(1); let expected = match expected_ty.kind() { ty::Tuple(tys) => { tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect() @@ -3468,11 +3499,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // traits manually, but don't make it more confusing when it does // happen. Ok( - if Some(expected_trait_ref.def_id()) != self.tcx.lang_items().coroutine_trait() + if Some(expected_trait_ref.def_id) != self.tcx.lang_items().coroutine_trait() && not_tupled { self.report_and_explain_type_error( - TypeTrace::poly_trait_refs( + TypeTrace::trait_refs( &obligation.cause, true, expected_trait_ref, @@ -3538,12 +3569,39 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut err = self.dcx().struct_span_err(span, "unconstrained generic constant"); let const_span = self.tcx.def_span(uv.def); + + let const_ty = self.tcx.type_of(uv.def).instantiate(self.tcx, uv.args); + let cast = if const_ty != self.tcx.types.usize { " as usize" } else { "" }; + let msg = "try adding a `where` bound"; match self.tcx.sess.source_map().span_to_snippet(const_span) { - Ok(snippet) => err.help(format!( - "try adding a `where` bound using this expression: `where [(); {snippet}]:`" - )), - _ => err.help("consider adding a `where` bound using this expression"), - }; + Ok(snippet) => { + let code = format!("[(); {snippet}{cast}]:"); + let def_id = if let ObligationCauseCode::CompareImplItemObligation { + trait_item_def_id, + .. + } = obligation.cause.code() + { + trait_item_def_id.as_local() + } else { + Some(obligation.cause.body_id) + }; + if let Some(def_id) = def_id + && let Some(generics) = self.tcx.hir().get_generics(def_id) + { + err.span_suggestion_verbose( + generics.tail_span_for_predicate_suggestion(), + msg, + format!("{} {code}", generics.add_where_or_trailing_comma()), + Applicability::MaybeIncorrect, + ); + } else { + err.help(format!("{msg}: where {code}")); + }; + } + _ => { + err.help(msg); + } + }; Ok(err) } ty::ConstKind::Expr(_) => { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 2fd64f474d5ad..1f10cb715430d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -72,8 +72,8 @@ pub struct PendingPredicateObligation<'tcx> { } // `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -static_assert_size!(PendingPredicateObligation<'_>, 72); +#[cfg(target_pointer_width = "64")] +rustc_data_structures::static_assert_size!(PendingPredicateObligation<'_>, 72); impl<'tcx> FulfillmentContext<'tcx> { /// Creates a new fulfillment context. @@ -311,7 +311,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let infcx = self.selcx.infcx; - if obligation.predicate.has_projections() { + if obligation.predicate.has_aliases() { let mut obligations = Vec::new(); let predicate = normalize_with_depth_to( &mut self.selcx, @@ -429,7 +429,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // as the cause of an overflow. ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( - DefineOpaqueTypes::No, + // Only really excercised by generic_const_exprs + DefineOpaqueTypes::Yes, ct.ty(), ty, ) { @@ -571,7 +572,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) .trace(c1, c2) - .eq(DefineOpaqueTypes::No, a.args, b.args) + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + .eq(DefineOpaqueTypes::Yes, a.args, b.args) { return ProcessResult::Changed(mk_pending( new_obligations.into_obligations(), @@ -582,7 +585,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { (_, _) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::No, c1, c2) + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + .eq(DefineOpaqueTypes::Yes, c1, c2) { return ProcessResult::Changed(mk_pending( new_obligations.into_obligations(), @@ -600,7 +605,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { obligation.param_env, unevaluated, c.ty(), - Some(obligation.cause.span), + obligation.cause.span, ) { Ok(val) => Ok(val), Err(e) => { @@ -623,7 +628,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( - DefineOpaqueTypes::No, + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + DefineOpaqueTypes::Yes, c1, c2, ) { @@ -751,9 +758,9 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) { - if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate( + if let Some(key) = ProjectionCacheKey::from_poly_projection_obligation( &mut self.selcx, - project_obligation.predicate, + &project_obligation, ) { // If `predicate_must_hold_considering_regions` succeeds, then we've // evaluated all sub-obligations. We can therefore mark the 'root' diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index cc8b8f72cf359..56f8b4b9cdb82 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -41,12 +41,13 @@ use rustc_span::Span; use std::fmt::Debug; use std::ops::ControlFlow; -pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; -pub use self::coherence::{IsFirstInputType, OrphanCheckErr, OverlapResult}; +pub use self::coherence::{add_placeholder_note, orphan_check_trait_ref, overlapping_impls}; +pub use self::coherence::{InCrate, IsFirstInputType, UncoveredTyParams}; +pub use self::coherence::{OrphanCheckErr, OrphanCheckMode, OverlapResult}; pub use self::engine::{ObligationCtxt, TraitEngineExt}; pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; pub use self::normalize::NormalizeExt; -pub use self::object_safety::astconv_object_safety_violations; +pub use self::object_safety::hir_ty_lowering_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::object_safety_violations_for_assoc_item; pub use self::object_safety::ObjectSafetyViolation; @@ -61,12 +62,12 @@ pub use self::specialize::{ pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::elaborate; +pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo}; +pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; pub use self::util::{ - check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds, - transitive_bounds_that_define_assoc_item, SupertraitDefIds, + supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item, + SupertraitDefIds, }; -pub use self::util::{expand_trait_aliases, TraitAliasExpander}; -pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; pub use rustc_infer::traits::*; @@ -119,7 +120,9 @@ pub fn predicates_for_generics<'tcx>( /// Determines whether the type `ty` is known to meet `bound` and /// returns true if so. Returns false if `ty` either does not meet -/// `bound` or is not known to meet bound. +/// `bound` or is not known to meet bound (note that this is +/// conservative towards *no impl*, which is the opposite of the +/// `evaluate` methods). pub fn type_known_to_meet_bound_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -127,8 +130,50 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( def_id: DefId, ) -> bool { let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); - let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref); - infcx.predicate_must_hold_modulo_regions(&obligation) + pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) +} + +/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? +/// +/// Ping me on zulip if you want to use this method and need help with finding +/// an appropriate replacement. +#[instrument(level = "debug", skip(infcx, param_env, pred), ret)] +fn pred_known_to_hold_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pred: impl ToPredicate<'tcx>, +) -> bool { + let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); + + let result = infcx.evaluate_obligation_no_overflow(&obligation); + debug!(?result); + + if result.must_apply_modulo_regions() { + true + } else if result.may_apply() { + // Sometimes obligations are ambiguous because the recursive evaluator + // is not smart enough, so we fall back to fulfillment when we're not certain + // that an obligation holds or not. Even still, we must make sure that + // the we do no inference in the process of checking this obligation. + let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); + infcx.probe(|_| { + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligation(obligation); + + let errors = ocx.select_all_or_error(); + match errors.as_slice() { + // Only known to hold if we did no inference. + [] => infcx.resolve_vars_if_possible(goal) == goal, + + errors => { + debug!(?errors); + false + } + } + }) + } else { + false + } } #[instrument(level = "debug", skip(tcx, elaborated_env))] @@ -438,7 +483,7 @@ fn is_impossible_associated_item( type Result = ControlFlow<()>; fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { // If this is a parameter from the trait item's own generics, then bail - if let ty::Param(param) = t.kind() + if let ty::Param(param) = *t.kind() && let param_def_id = self.generics.type_param(param, self.tcx).def_id && self.tcx.parent(param_def_id) == self.trait_item_def_id { @@ -448,7 +493,7 @@ fn is_impossible_associated_item( } fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result { if let ty::ReEarlyParam(param) = r.kind() - && let param_def_id = self.generics.region_param(¶m, self.tcx).def_id + && let param_def_id = self.generics.region_param(param, self.tcx).def_id && self.tcx.parent(param_def_id) == self.trait_item_def_id { return ControlFlow::Break(()); @@ -457,7 +502,7 @@ fn is_impossible_associated_item( } fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { if let ty::ConstKind::Param(param) = ct.kind() - && let param_def_id = self.generics.const_param(¶m, self.tcx).def_id + && let param_def_id = self.generics.const_param(param, self.tcx).def_id && self.tcx.parent(param_def_id) == self.trait_item_def_id { return ControlFlow::Break(()); diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 15bfffef3cede..43f4fa8e81ca1 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -8,6 +8,7 @@ use rustc_infer::infer::at::At; use rustc_infer::infer::InferOk; use rustc_infer::traits::PredicateObligation; use rustc_infer::traits::{FulfillmentError, Normalized, Obligation, TraitEngine}; +use rustc_macros::extension; use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder}; use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt}; @@ -101,6 +102,8 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable>>( value: &T, reveal: Reveal, ) -> bool { + // This mirrors `ty::TypeFlags::HAS_ALIASES` except that we take `Reveal` into account. + let mut flags = ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_TY_WEAK | ty::TypeFlags::HAS_TY_INHERENT diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 1816b98a636c6..0e15dd275371b 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -32,11 +32,13 @@ use std::ops::ControlFlow; pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation}; -/// Returns the object safety violations that affect -/// astconv -- currently, `Self` in supertraits. This is needed +/// Returns the object safety violations that affect HIR ty lowering. +/// +/// Currently that is `Self` in supertraits. This is needed /// because `object_safety_violations` can't be used during /// type collection. -pub fn astconv_object_safety_violations( +#[instrument(level = "debug", skip(tcx))] +pub fn hir_ty_lowering_object_safety_violations( tcx: TyCtxt<'_>, trait_def_id: DefId, ) -> Vec { @@ -46,9 +48,7 @@ pub fn astconv_object_safety_violations( .filter(|spans| !spans.is_empty()) .map(ObjectSafetyViolation::SupertraitSelf) .collect(); - - debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations); - + debug!(?violations); violations } @@ -519,7 +519,7 @@ fn virtual_call_violations_for_method<'tcx>( // e.g., `Rc<()>` let unit_receiver_ty = - receiver_for_self_ty(tcx, receiver_ty, Ty::new_unit(tcx), method.def_id); + receiver_for_self_ty(tcx, receiver_ty, tcx.types.unit, method.def_id); match abi_of_ty(unit_receiver_ty) { Some(Abi::Scalar(..)) => (), @@ -584,7 +584,7 @@ fn virtual_call_violations_for_method<'tcx>( // implement auto traits if the underlying type does as well. if let ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref: pred_trait_ref, - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }) = pred.kind().skip_binder() && pred_trait_ref.self_ty() == tcx.types.self_param && tcx.trait_is_auto(pred_trait_ref.def_id) diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index c4110df45dbb4..1dd2ada3356f7 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -3,6 +3,7 @@ use crate::traits::{ObligationCause, ObligationCtxt}; use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::InferOk; +use rustc_macros::extension; use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt}; use rustc_span::def_id::LocalDefId; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 6756b5dec2318..116e17c7e432f 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2,7 +2,6 @@ use std::ops::ControlFlow; -use super::check_args_compatible; use super::specialization_graph; use super::translate_args; use super::util; @@ -19,7 +18,7 @@ use rustc_middle::traits::ImplSource; use rustc_middle::traits::ImplSourceUserDefinedData; use crate::errors::InherentProjectionNormalizationOverflow; -use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::type_variable::TypeVariableOrigin; use crate::infer::{BoundRegionConversionTime, InferOk}; use crate::traits::normalize::normalize_with_depth; use crate::traits::normalize::normalize_with_depth_to; @@ -345,7 +344,7 @@ pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( let use_cache = !selcx.is_intercrate(); let projection_ty = infcx.resolve_vars_if_possible(projection_ty); - let cache_key = ProjectionCacheKey::new(projection_ty); + let cache_key = ProjectionCacheKey::new(projection_ty, param_env); // FIXME(#20304) For now, I am caching here, which is good, but it // means we don't capture the type variables that are created in @@ -432,7 +431,7 @@ pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); - let mut result = if projected_term.has_projections() { + let mut result = if projected_term.has_aliases() { let normalized_ty = normalize_with_depth_to( selcx, param_env, @@ -508,7 +507,7 @@ pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( /// because it contains `[type error]`. Yuck! (See issue #29857 for /// one case where this arose.) fn normalize_to_error<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, + selcx: &SelectionContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::AliasTy<'tcx>, cause: ObligationCause<'tcx>, @@ -523,7 +522,7 @@ fn normalize_to_error<'a, 'tcx>( }; let tcx = selcx.infcx.tcx; let new_value = selcx.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, + param_def_id: None, span: tcx.def_span(projection_ty.def_id), }); Normalized { value: new_value, obligations: vec![trait_obligation] } @@ -596,7 +595,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args); let mut ty = selcx.infcx.resolve_vars_if_possible(ty); - if ty.has_projections() { + if ty.has_aliases() { ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations); } @@ -642,7 +641,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( ); } - match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { + match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::Yes, impl_ty, self_ty) { Ok(mut ok) => obligations.append(&mut ok.obligations), Err(_) => { tcx.dcx().span_bug( @@ -794,6 +793,9 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( let Some(clause) = clause.as_projection_clause() else { return ControlFlow::Continue(()); }; + if clause.projection_def_id() != obligation.predicate.def_id { + return ControlFlow::Continue(()); + } let is_match = selcx.infcx.probe(|_| selcx.match_projection_projections(obligation, clause, true)); @@ -1046,6 +1048,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Foreign(_) | ty::Str | ty::Array(..) + | ty::Pat(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1071,6 +1074,42 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Infer(..) | ty::Error(_) => false, } + } else if lang_items.async_destruct_trait() == Some(trait_ref.def_id) { + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Pat(..) + | ty::Never + | ty::Tuple(..) + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + + // type parameters, opaques, and unnormalized projections don't have + // a known async destructor and may need to be normalized further or rely + // on param env for async destructor projections + ty::Param(_) + | ty::Foreign(_) + | ty::Alias(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(_) + | ty::Error(_) => false, + } } else if lang_items.pointee_trait() == Some(trait_ref.def_id) { let tail = selcx.tcx().struct_tail_with_normalize( self_ty, @@ -1097,6 +1136,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Float(_) | ty::Str | ty::Array(..) + | ty::Pat(..) | ty::Slice(_) | ty::RawPtr(..) | ty::Ref(..) @@ -1484,15 +1524,20 @@ fn confirm_builtin_candidate<'cx, 'tcx>( ) -> Progress<'tcx> { let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); - let args = tcx.mk_args(&[self_ty.into()]); let lang_items = tcx.lang_items(); let item_def_id = obligation.predicate.def_id; let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); + let args = tcx.mk_args(&[self_ty.into()]); let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) { let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); assert_eq!(discriminant_def_id, item_def_id); (self_ty.discriminant_ty(tcx).into(), Vec::new()) + } else if lang_items.async_destruct_trait() == Some(trait_def_id) { + let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0]; + assert_eq!(destructor_def_id, item_def_id); + + (self_ty.async_destructor_ty(tcx, obligation.param_env).into(), Vec::new()) } else if lang_items.pointee_trait() == Some(trait_def_id) { let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); assert_eq!(metadata_def_id, item_def_id); @@ -1597,7 +1642,10 @@ fn confirm_closure_candidate<'cx, 'tcx>( // If we know the kind and upvars, use that directly. // Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay // the projection, like the `AsyncFn*` traits do. - let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() { + let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() + // Fall back to projection if upvars aren't constrained + && !args.tupled_upvars_ty().is_ty_var() + { sig.to_coroutine_given_kind_and_upvars( tcx, args.parent_args(), @@ -1726,8 +1774,11 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( let sig = args.coroutine_closure_sig().skip_binder(); let term = match item_name { - sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => { - if let Some(closure_kind) = kind_ty.to_opt_closure_kind() { + sym::CallOnceFuture | sym::CallRefFuture => { + if let Some(closure_kind) = kind_ty.to_opt_closure_kind() + // Fall back to projection if upvars aren't constrained + && !args.tupled_upvars_ty().is_ty_var() + { if !closure_kind.extends(goal_kind) { bug!("we should not be confirming if the closure kind is not met"); } @@ -1787,7 +1838,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty], ), - sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new( + sym::CallRefFuture => ty::AliasTy::new( tcx, obligation.predicate.def_id, [ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()], @@ -1803,7 +1854,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( let sig = bound_sig.skip_binder(); let term = match item_name { - sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(), + sym::CallOnceFuture | sym::CallRefFuture => sig.output(), sym::Output => { let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); let future_output_def_id = tcx @@ -1822,7 +1873,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( obligation.predicate.def_id, [self_ty, Ty::new_tup(tcx, sig.inputs())], ), - sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new( + sym::CallRefFuture => ty::AliasTy::new( tcx, obligation.predicate.def_id, [ @@ -1842,7 +1893,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( let sig = bound_sig.skip_binder(); let term = match item_name { - sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(), + sym::CallOnceFuture | sym::CallRefFuture => sig.output(), sym::Output => { let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); let future_output_def_id = tcx @@ -1859,7 +1910,7 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( sym::CallOnceFuture | sym::Output => { ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]]) } - sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new( + sym::CallRefFuture => ty::AliasTy::new( tcx, obligation.predicate.def_id, [ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()], @@ -2030,7 +2081,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( } else { ty.map_bound(|ty| ty.into()) }; - if !check_args_compatible(tcx, assoc_ty.item, args) { + if !tcx.check_args_compatible(assoc_ty.item.def_id, args) { let err = Ty::new_error_with_message( tcx, obligation.cause.span, @@ -2095,27 +2146,28 @@ fn assoc_ty_own_obligations<'cx, 'tcx>( } pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized { - fn from_poly_projection_predicate( + fn from_poly_projection_obligation( selcx: &mut SelectionContext<'cx, 'tcx>, - predicate: ty::PolyProjectionPredicate<'tcx>, + obligation: &PolyProjectionObligation<'tcx>, ) -> Option; } impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> { - fn from_poly_projection_predicate( + fn from_poly_projection_obligation( selcx: &mut SelectionContext<'cx, 'tcx>, - predicate: ty::PolyProjectionPredicate<'tcx>, + obligation: &PolyProjectionObligation<'tcx>, ) -> Option { let infcx = selcx.infcx; // We don't do cross-snapshot caching of obligations with escaping regions, // so there's no cache key to use - predicate.no_bound_vars().map(|predicate| { + obligation.predicate.no_bound_vars().map(|predicate| { ProjectionCacheKey::new( // We don't attempt to match up with a specific type-variable state // from a specific call to `opt_normalize_projection_type` - if // there's no precise match, the original cache entry is "stranded" // anyway. infcx.resolve_vars_if_possible(predicate.projection_ty), + obligation.param_env, ) }) } diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 6c8834f11f1eb..326c68e01dbf7 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -36,14 +36,14 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::FnPtr(_) | ty::Char | ty::CoroutineWitness(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Str | ty::Foreign(..) | ty::Error(_) => true, - // [T; N] and [T] have same properties as T. - ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), + // `T is PAT`, `[T; N]`, and `[T]` have same properties as T. + ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), // (T1..Tn) and closures have same properties as T1..Tn -- // check if *all* of them are trivial. @@ -222,7 +222,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( // these types never have a destructor } - ty::Array(ety, _) | ty::Slice(ety) => { + ty::Pat(ety, _) | ty::Array(ety, _) | ty::Slice(ety) => { // single-element containers, behave like their element rustc_data_structures::stack::ensure_sufficient_stack(|| { dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, *ety, constraints) diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 16ee9fadab4fc..5f2a894413529 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,4 +1,5 @@ use rustc_infer::traits::{TraitEngine, TraitEngineExt}; +use rustc_macros::extension; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index c520e699bf542..8b39c23da56b2 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -13,6 +13,7 @@ use crate::traits::{ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; +use rustc_macros::extension; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 423ed0f7105be..f7e84a46639d4 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -7,6 +7,7 @@ use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::OutlivesBound; +use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index faf218131b8b1..ae4cdb9258e29 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -159,53 +159,70 @@ where .0); } + let mut error_info = None; let mut region_constraints = QueryRegionConstraints::default(); - let (output, error_info, mut obligations, _) = - Q::fully_perform_into(self, infcx, &mut region_constraints, span).map_err(|_| { - infcx.dcx().span_delayed_bug(span, format!("error performing {self:?}")) - })?; - - // Typically, instantiating NLL query results does not - // create obligations. However, in some cases there - // are unresolved type variables, and unify them *can* - // create obligations. In that case, we have to go - // fulfill them. We do this via a (recursive) query. - while !obligations.is_empty() { - trace!("{:#?}", obligations); - let mut progress = false; - for obligation in std::mem::take(&mut obligations) { - let obligation = infcx.resolve_vars_if_possible(obligation); - match ProvePredicate::fully_perform_into( - obligation.param_env.and(ProvePredicate::new(obligation.predicate)), - infcx, - &mut region_constraints, - span, - ) { - Ok(((), _, new, certainty)) => { - obligations.extend(new); - progress = true; - if let Certainty::Ambiguous = certainty { - obligations.push(obligation); + + // HACK(type_alias_impl_trait): When moving an opaque type to hidden type mapping from the query to the current inferctxt, + // we sometimes end up with `Opaque<'a> = Opaque<'b>` instead of an actual hidden type. In that case we don't register a + // hidden type but just equate the lifetimes. Thus we need to scrape the region constraints even though we're also manually + // collecting region constraints via `region_constraints`. + let (mut output, _) = scrape_region_constraints( + infcx, + |_ocx| { + let (output, ei, mut obligations, _) = + Q::fully_perform_into(self, infcx, &mut region_constraints, span)?; + error_info = ei; + + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + while !obligations.is_empty() { + trace!("{:#?}", obligations); + let mut progress = false; + for obligation in std::mem::take(&mut obligations) { + let obligation = infcx.resolve_vars_if_possible(obligation); + match ProvePredicate::fully_perform_into( + obligation.param_env.and(ProvePredicate::new(obligation.predicate)), + infcx, + &mut region_constraints, + span, + ) { + Ok(((), _, new, certainty)) => { + obligations.extend(new); + progress = true; + if let Certainty::Ambiguous = certainty { + obligations.push(obligation); + } + } + Err(_) => obligations.push(obligation), } } - Err(_) => obligations.push(obligation), + if !progress { + infcx.dcx().span_bug( + span, + format!("ambiguity processing {obligations:?} from {self:?}"), + ); + } } - } - if !progress { - infcx - .dcx() - .span_bug(span, format!("ambiguity processing {obligations:?} from {self:?}")); - } - } - - Ok(TypeOpOutput { - output, - constraints: if region_constraints.is_empty() { - None - } else { - Some(infcx.tcx.arena.alloc(region_constraints)) + Ok(output) }, - error_info, - }) + "fully_perform", + span, + )?; + output.error_info = error_info; + if let Some(constraints) = output.constraints { + region_constraints + .member_constraints + .extend(constraints.member_constraints.iter().cloned()); + region_constraints.outlives.extend(constraints.outlives.iter().cloned()); + } + output.constraints = if region_constraints.is_empty() { + None + } else { + Some(infcx.tcx.arena.alloc(region_constraints)) + }; + Ok(output) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 3b33f6e6144a1..279d96dec728c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -15,7 +15,7 @@ where type QueryResponse = T; fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { - if !key.value.value.has_projections() { Some(key.value.value) } else { None } + if !key.value.value.has_aliases() { Some(key.value.value) } else { None } } fn perform_query( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index 07587e3741181..3e7aa52dcfeac 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -3,6 +3,7 @@ use crate::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, trivial_dropck_outlives, }; use crate::traits::ObligationCtxt; +use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 14f14ae6e2e01..63289746f5e5d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -20,14 +20,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { // such cases. if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) = key.value.predicate.kind().skip_binder() + && let Some(sized_def_id) = tcx.lang_items().sized_trait() + && trait_ref.def_id() == sized_def_id + && trait_ref.self_ty().is_trivially_sized(tcx) { - if let Some(sized_def_id) = tcx.lang_items().sized_trait() { - if trait_ref.def_id() == sized_def_id { - if trait_ref.self_ty().is_trivially_sized(tcx) { - return Some(()); - } - } - } + return Some(()); } if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) = diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 49091e53be713..40d206b92b8df 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -56,7 +56,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; // Negative trait predicates have different rules than positive trait predicates. - if obligation.polarity() == ty::ImplPolarity::Negative { + if obligation.polarity() == ty::PredicatePolarity::Negative { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; @@ -81,12 +81,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else if lang_items.discriminant_kind_trait() == Some(def_id) { // `DiscriminantKind` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); + } else if lang_items.async_destruct_trait() == Some(def_id) { + // `AsyncDestruct` is automatically implemented for every type. + candidates.vec.push(BuiltinCandidate { has_nested: false }); } else if lang_items.pointee_trait() == Some(def_id) { // `Pointee` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); } else if lang_items.sized_trait() == Some(def_id) { // Sized is never implementable by end-users, it is // always automatically computed. + + // FIXME: Consider moving this check to the top level as it + // may also be useful for predicates other than `Sized` + // Error type cannot possibly implement `Sized` (fixes #123154) + if let Err(e) = obligation.predicate.skip_binder().self_ty().error_reported() { + return Err(SelectionError::Overflow(e.into())); + } + let sized_conditions = self.sized_conditions(obligation); self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); } else if lang_items.unsize_trait() == Some(def_id) { @@ -118,6 +129,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_future_candidates(obligation, &mut candidates); } else if lang_items.iterator_trait() == Some(def_id) { self.assemble_iterator_candidates(obligation, &mut candidates); + } else if lang_items.fused_iterator_trait() == Some(def_id) { + self.assemble_fused_iterator_candidates(obligation, &mut candidates); } else if lang_items.async_iterator_trait() == Some(def_id) { self.assemble_async_iterator_candidates(obligation, &mut candidates); } else if lang_items.async_fn_kind_helper() == Some(def_id) { @@ -302,14 +315,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut SelectionCandidateSet<'tcx>, ) { let self_ty = obligation.self_ty().skip_binder(); - if let ty::Coroutine(did, ..) = self_ty.kind() { - // gen constructs get lowered to a special kind of coroutine that - // should directly `impl Iterator`. - if self.tcx().coroutine_is_gen(*did) { - debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl Iterator`. + if let ty::Coroutine(did, ..) = self_ty.kind() + && self.tcx().coroutine_is_gen(*did) + { + debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); - candidates.vec.push(IteratorCandidate); - } + candidates.vec.push(IteratorCandidate); + } + } + + fn assemble_fused_iterator_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl FusedIterator`. + if let ty::Coroutine(did, ..) = self_ty.kind() + && self.tcx().coroutine_is_gen(*did) + { + debug!(?self_ty, ?obligation, "assemble_fused_iterator_candidates",); + + candidates.vec.push(BuiltinCandidate { has_nested: false }); } } @@ -381,39 +411,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } ty::CoroutineClosure(def_id, args) => { + let args = args.as_coroutine_closure(); let is_const = self.tcx().is_const_fn_raw(def_id); - match self.infcx.closure_kind(self_ty) { - Some(closure_kind) => { - let no_borrows = match self - .infcx - .shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()) - .kind() - { - ty::Tuple(tys) => tys.is_empty(), - ty::Error(_) => false, - _ => bug!("tuple_fields called on non-tuple"), - }; - // A coroutine-closure implements `FnOnce` *always*, since it may - // always be called once. It additionally implements `Fn`/`FnMut` - // only if it has no upvars (therefore no borrows from the closure - // that would need to be represented with a lifetime) and if the - // closure kind permits it. - // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut` - // if it takes all of its upvars by copy, and none by ref. This would - // require us to record a bit more information during upvar analysis. - if no_borrows && closure_kind.extends(kind) { - candidates.vec.push(ClosureCandidate { is_const }); - } else if kind == ty::ClosureKind::FnOnce { - candidates.vec.push(ClosureCandidate { is_const }); - } + if let Some(closure_kind) = self.infcx.closure_kind(self_ty) + // Ambiguity if upvars haven't been constrained yet + && !args.tupled_upvars_ty().is_ty_var() + { + let no_borrows = match args.tupled_upvars_ty().kind() { + ty::Tuple(tys) => tys.is_empty(), + ty::Error(_) => false, + _ => bug!("tuple_fields called on non-tuple"), + }; + // A coroutine-closure implements `FnOnce` *always*, since it may + // always be called once. It additionally implements `Fn`/`FnMut` + // only if it has no upvars (therefore no borrows from the closure + // that would need to be represented with a lifetime) and if the + // closure kind permits it. + // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut` + // if it takes all of its upvars by copy, and none by ref. This would + // require us to record a bit more information during upvar analysis. + if no_borrows && closure_kind.extends(kind) { + candidates.vec.push(ClosureCandidate { is_const }); + } else if kind == ty::ClosureKind::FnOnce { + candidates.vec.push(ClosureCandidate { is_const }); } - None => { - if kind == ty::ClosureKind::FnOnce { - candidates.vec.push(ClosureCandidate { is_const }); - } else { - // This stays ambiguous until kind+upvars are determined. - candidates.ambiguous = true; - } + } else { + if kind == ty::ClosureKind::FnOnce { + candidates.vec.push(ClosureCandidate { is_const }); + } else { + // This stays ambiguous until kind+upvars are determined. + candidates.ambiguous = true; } } } @@ -651,8 +678,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Foreign(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -784,9 +812,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Float(_) | ty::Str | ty::Array(_, _) + | ty::Pat(_, _) | ty::Slice(_) | ty::Adt(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) @@ -1147,8 +1176,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); - match self_ty.skip_binder().kind() { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + match self_ty.kind() { ty::Alias(..) | ty::Dynamic(..) | ty::Error(_) @@ -1167,13 +1196,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Str - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) | ty::Never | ty::Foreign(_) | ty::Array(..) + | ty::Pat(..) | ty::Slice(_) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -1248,9 +1278,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) + | ty::Pat(_, _) | ty::FnPtr(_) | ty::Dynamic(_, _, _) | ty::Closure(..) @@ -1297,7 +1328,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + let self_ty = self.infcx.resolve_vars_if_possible(obligation.self_ty()); match self_ty.skip_binder().kind() { ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }), @@ -1310,8 +1341,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Foreign(..) | ty::Str | ty::Array(..) + | ty::Pat(..) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::Placeholder(..) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 51fc223a5d1b3..4fa2455c42de1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -14,7 +14,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, - TraitPredicate, Ty, TyCtxt, TypeVisitableExt, + TraitPredicate, Ty, TyCtxt, }; use rustc_span::def_id::DefId; @@ -28,7 +28,7 @@ use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, PolyTraitObligation, PredicateObligation, Selection, SelectionError, SignatureMismatch, TraitNotObjectSafe, - Unimplemented, + TraitObligation, Unimplemented, }; use super::BuiltinImplConditions; @@ -157,10 +157,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result>, SelectionError<'tcx>> { let tcx = self.tcx(); - let trait_predicate = self.infcx.shallow_resolve(obligation.predicate); let placeholder_trait_predicate = - self.infcx.enter_forall_and_leak_universe(trait_predicate).trait_ref; - let placeholder_self_ty = placeholder_trait_predicate.self_ty(); + self.infcx.enter_forall_and_leak_universe(obligation.predicate).trait_ref; + let placeholder_self_ty = self.infcx.shallow_resolve(placeholder_trait_predicate.self_ty()); let candidate_predicate = self .for_each_item_bound( placeholder_self_ty, @@ -267,6 +266,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.copy_clone_conditions(obligation) } else if Some(trait_def) == lang_items.clone_trait() { self.copy_clone_conditions(obligation) + } else if Some(trait_def) == lang_items.fused_iterator_trait() { + self.fused_iterator_conditions(obligation) } else { bug!("unexpected builtin trait {:?}", trait_def) }; @@ -312,12 +313,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond)) .collect(), Condition::IfTransmutable { src, dst } => { - let trait_def_id = obligation.predicate.def_id(); + let transmute_trait = obligation.predicate.def_id(); let assume_const = predicate.trait_ref.args.const_at(2); - let make_obl = |from_ty, to_ty| { - let trait_ref1 = ty::TraitRef::new( + let make_transmute_obl = |from_ty, to_ty| { + let trait_ref = ty::TraitRef::new( tcx, - trait_def_id, + transmute_trait, [ ty::GenericArg::from(to_ty), ty::GenericArg::from(from_ty), @@ -329,17 +330,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, - trait_ref1, + trait_ref, ) }; + let make_freeze_obl = |ty| { + let trait_ref = ty::TraitRef::new( + tcx, + tcx.lang_items().freeze_trait().unwrap(), + [ty::GenericArg::from(ty)], + ); + Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + trait_ref, + ) + }; + + let mut obls = vec![]; + + // If the source is a shared reference, it must be `Freeze`; + // otherwise, transmuting could lead to data races. + if src.mutability == Mutability::Not { + obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)]) + } + // If Dst is mutable, check bidirectionally. // For example, transmuting bool -> u8 is OK as long as you can't update that u8 // to be > 1, because you could later transmute the u8 back to a bool and get UB. match dst.mutability { - Mutability::Not => vec![make_obl(src.ty, dst.ty)], - Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)], + Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)), + Mutability::Mut => obls.extend([ + make_transmute_obl(src.ty, dst.ty), + make_transmute_obl(dst.ty, src.ty), + ]), } + + obls } } } @@ -392,7 +421,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result>, SelectionError<'tcx>> { debug!(?obligation, "confirm_auto_impl_candidate"); - let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty()); + let self_ty = obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); let types = self.constituent_types_for_ty(self_ty)?; Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types)) } @@ -676,17 +705,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn_host_effect: ty::Const<'tcx>, ) -> Result>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let tcx = self.tcx(); - - let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else { - // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`, - // but we do not currently. Luckily, such a bound is not - // particularly useful, so we don't expect users to write - // them often. - return Err(SelectionError::Unimplemented); - }; - let sig = self_ty.fn_sig(tcx); let trait_ref = closure_trait_ref_and_return_type( tcx, @@ -698,7 +720,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let mut nested = + self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?; let cause = obligation.derived_cause(BuiltinDerivedObligation); // Confirm the `type Output: Sized;` bound that is present on `FnOnce` @@ -746,10 +769,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -758,15 +779,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let coroutine_sig = args.as_coroutine().sig(); - // NOTE: The self-type is a coroutine type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). - let self_ty = obligation - .predicate - .self_ty() - .no_bound_vars() - .expect("unboxed closure type should not capture bound vars from the predicate"); - let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), @@ -774,7 +786,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { coroutine_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "coroutine candidate obligations"); Ok(nested) @@ -784,10 +799,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -799,11 +812,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::future_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(), + self_ty, coroutine_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "future candidate obligations"); Ok(nested) @@ -813,10 +829,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -828,11 +842,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + self_ty, gen_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -842,10 +859,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on coroutine types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { bug!("closure candidate for non-closure {:?}", obligation); }; @@ -857,11 +872,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + self_ty, gen_sig, ); - let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + let nested = self.equate_trait_refs( + obligation.with(self.tcx(), placeholder_predicate), + ty::Binder::dummy(trait_ref), + )?; debug!(?trait_ref, ?nested, "iterator candidate obligations"); Ok(nested) @@ -872,14 +890,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the args on closure types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let trait_ref = match *self_ty.kind() { - ty::Closure(_, args) => { - self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_) - } + ty::Closure(..) => self.closure_trait_ref_unnormalized( + self_ty, + obligation.predicate.def_id(), + self.tcx().consts.true_, + ), ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { ty::TraitRef::new( @@ -894,7 +913,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } }; - self.confirm_poly_trait_refs(obligation, trait_ref) + self.equate_trait_refs(obligation.with(self.tcx(), placeholder_predicate), trait_ref) } #[instrument(skip(self), level = "debug")] @@ -902,8 +921,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let tcx = self.tcx(); - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); let mut nested = vec![]; let (trait_ref, kind_ty) = match *self_ty.kind() { @@ -970,7 +991,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("expected callable type for AsyncFn candidate"), }; - nested.extend(self.confirm_poly_trait_refs(obligation, trait_ref)?); + nested.extend( + self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?, + ); let goal_kind = self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap(); @@ -1023,42 +1046,40 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// selection of the impl. Therefore, if there is a mismatch, we /// report an error to the user. #[instrument(skip(self), level = "trace")] - fn confirm_poly_trait_refs( + fn equate_trait_refs( &mut self, - obligation: &PolyTraitObligation<'tcx>, - self_ty_trait_ref: ty::PolyTraitRef<'tcx>, + obligation: TraitObligation<'tcx>, + found_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, SelectionError<'tcx>> { - let obligation_trait_ref = - self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref()); - let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, HigherRankedType, - self_ty_trait_ref, + found_trait_ref, ); // Normalize the obligation and expected trait refs together, because why not - let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } = + let Normalized { obligations: nested, value: (obligation_trait_ref, found_trait_ref) } = ensure_sufficient_stack(|| { normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - (obligation_trait_ref, self_ty_trait_ref), + (obligation.predicate.trait_ref, found_trait_ref), ) }); // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs self.infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) + .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, found_trait_ref) .map(|InferOk { mut obligations, .. }| { obligations.extend(nested); obligations }) .map_err(|terr| { SignatureMismatch(Box::new(SignatureMismatchData { - expected_trait_ref: ty::Binder::dummy(obligation_trait_ref), - found_trait_ref: ty::Binder::dummy(expected_trait_ref), + expected_trait_ref: obligation_trait_ref, + found_trait_ref, terr, })) }) @@ -1262,7 +1283,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Extract `TailField` and `TailField` from `Struct` and `Struct`, // normalizing in the process, since `type_of` returns something directly from - // astconv (which means it's un-normalized). + // HIR ty lowering (which means it's un-normalized). let source_tail = normalize_with_depth_to( self, obligation.param_env, @@ -1356,7 +1377,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None); let tcx = self.tcx(); - let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + let self_ty = obligation.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); let mut nested = vec![]; let cause = obligation.derived_cause(BuiltinDerivedObligation); @@ -1411,7 +1432,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Str - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) @@ -1423,7 +1444,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // These types are built-in, so we can fast-track by registering // nested predicates for their constituent type(s) - ty::Array(ty, _) | ty::Slice(ty) => { + ty::Array(ty, _) | ty::Slice(ty) | ty::Pat(ty, _) => { stack.push(ty); } ty::Tuple(tys) => { @@ -1438,10 +1459,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::CoroutineWitness(def_id, args) => { let tcx = self.tcx(); - stack.extend(tcx.coroutine_hidden_types(def_id).map(|bty| { - let ty = bty.instantiate(tcx, args); - debug_assert!(!ty.has_bound_regions()); - ty + stack.extend(tcx.bound_coroutine_hidden_types(def_id).map(|bty| { + self.infcx.enter_forall_and_leak_universe(bty.instantiate(tcx, args)) })) } @@ -1460,7 +1479,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.span, [nested_ty.into(), host_effect_param], ), - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }), &mut nested, ); @@ -1477,7 +1496,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If we have any other type (e.g. an ADT), just register a nested obligation // since it's either not `const Drop` (and we raise an error during selection), // or it's an ADT (and we need to check for a custom impl during selection) - _ => { + ty::Error(_) + | ty::Dynamic(..) + | ty::CoroutineClosure(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Adt(..) + | ty::Alias(ty::Opaque | ty::Weak, _) + | ty::Infer(_) + | ty::Placeholder(_) => { let predicate = self_ty.rebind(ty::TraitPredicate { trait_ref: ty::TraitRef::from_lang_item( self.tcx(), @@ -1485,7 +1512,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.span, [nested_ty.into(), host_effect_param], ), - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }); nested.push(Obligation::with_depth( diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index d10fe6a949013..18cb3184fe114 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -60,6 +60,20 @@ use rustc_middle::ty::print::with_no_trimmed_paths; mod candidate_assembly; mod confirmation; +/// Whether to consider the binder of higher ranked goals for the `leak_check` when +/// evaluating higher-ranked goals. See #119820 for more info. +/// +/// While this is a bit hacky, it is necessary to match the behavior of the new solver: +/// We eagerly instantiate binders in the new solver, outside of candidate selection, so +/// the leak check inside of candidates does not consider any bound vars from the higher +/// ranked goal. However, we do exit the binder once we're completely finished with a goal, +/// so the leak-check can be used in evaluate by causing nested higher-ranked goals to fail. +#[derive(Debug, Copy, Clone)] +enum LeakCheckHigherRankedGoal { + No, + Yes, +} + #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum IntercrateAmbiguityCause<'tcx> { DownstreamCrate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option> }, @@ -126,8 +140,6 @@ pub struct SelectionContext<'cx, 'tcx> { /// policy. In essence, canonicalized queries need their errors propagated /// rather than immediately reported because we do not have accurate spans. query_mode: TraitQueryMode, - - treat_inductive_cycle: TreatInductiveCycleAs, } // A stack that walks back up the stack frame. @@ -208,27 +220,6 @@ enum BuiltinImplConditions<'tcx> { Ambiguous, } -#[derive(Copy, Clone)] -pub enum TreatInductiveCycleAs { - /// This is the previous behavior, where `Recur` represents an inductive - /// cycle that is known not to hold. This is not forwards-compatible with - /// coinduction, and will be deprecated. This is the default behavior - /// of the old trait solver due to back-compat reasons. - Recur, - /// This is the behavior of the new trait solver, where inductive cycles - /// are treated as ambiguous and possibly holding. - Ambig, -} - -impl From for EvaluationResult { - fn from(treat: TreatInductiveCycleAs) -> EvaluationResult { - match treat { - TreatInductiveCycleAs::Ambig => EvaluatedToAmbigStackDependent, - TreatInductiveCycleAs::Recur => EvaluatedToErrStackDependent, - } - } -} - impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { @@ -236,19 +227,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { freshener: infcx.freshener(), intercrate_ambiguity_causes: None, query_mode: TraitQueryMode::Standard, - treat_inductive_cycle: TreatInductiveCycleAs::Recur, - } - } - - pub fn with_treat_inductive_cycle_as_ambig( - infcx: &'cx InferCtxt<'tcx>, - ) -> SelectionContext<'cx, 'tcx> { - // Should be executed in a context where caching is disabled, - // otherwise the cache is poisoned with the temporary result. - assert!(infcx.intercrate); - SelectionContext { - treat_inductive_cycle: TreatInductiveCycleAs::Ambig, - ..SelectionContext::new(infcx) } } @@ -420,7 +398,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut no_candidates_apply = true; for c in candidate_set.vec.iter() { - if self.evaluate_candidate(stack, c)?.may_apply() { + if self + .evaluate_candidate(stack, c, LeakCheckHigherRankedGoal::No)? + .may_apply() + { no_candidates_apply = false; break; } @@ -491,7 +472,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // is needed for specialization. Propagate overflow if it occurs. let mut candidates = candidates .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { + .map(|c| match self.evaluate_candidate(stack, &c, LeakCheckHigherRankedGoal::No) { Ok(eval) if eval.may_apply() => { Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) } @@ -581,7 +562,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PredicateObligation<'tcx>, ) -> Result { debug_assert!(!self.infcx.next_trait_solver()); - self.evaluation_probe(|this| { + self.evaluation_probe(|this, _outer_universe| { let goal = this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); let mut result = this.evaluate_predicate_recursively( @@ -590,20 +571,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { )?; // If the predicate has done any inference, then downgrade the // result to ambiguous. - if this.infcx.shallow_resolve(goal) != goal { + if this.infcx.resolve_vars_if_possible(goal) != goal { result = result.max(EvaluatedToAmbig); } Ok(result) }) } + /// Computes the evaluation result of `op`, discarding any constraints. + /// + /// This also runs for leak check to allow higher ranked region errors to impact + /// selection. By default it checks for leaks from all universes created inside of + /// `op`, but this can be overwritten if necessary. fn evaluation_probe( &mut self, - op: impl FnOnce(&mut Self) -> Result, + op: impl FnOnce(&mut Self, &mut ty::UniverseIndex) -> Result, ) -> Result { self.infcx.probe(|snapshot| -> Result { - let outer_universe = self.infcx.universe(); - let result = op(self)?; + let mut outer_universe = self.infcx.universe(); + let result = op(self, &mut outer_universe)?; match self.infcx.leak_check(outer_universe, Some(snapshot)) { Ok(()) => {} @@ -622,9 +608,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } - /// Evaluates the predicates in `predicates` recursively. Note that - /// this applies projections in the predicates, and therefore + /// Evaluates the predicates in `predicates` recursively. This may + /// guide inference. If this is not desired, run it inside of a /// is run within an inference probe. + /// `probe`. #[instrument(skip(self, stack), level = "debug")] fn evaluate_predicates_recursively<'o, I>( &mut self, @@ -756,7 +743,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack.update_reached_depth(stack_arg.1); return Ok(EvaluatedToOk); } else { - return Ok(self.treat_inductive_cycle.into()); + return Ok(EvaluatedToAmbigStackDependent); } } return Ok(EvaluatedToOk); @@ -828,7 +815,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `EvaluatedToOkModuloRegions`), and skip re-evaluating the // sub-obligations. if let Some(key) = - ProjectionCacheKey::from_poly_projection_predicate(self, data) + ProjectionCacheKey::from_poly_projection_obligation( + self, + &project_obligation, + ) { if let Some(cached_res) = self .infcx @@ -857,8 +847,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && (eval_rslt == EvaluatedToOk || eval_rslt == EvaluatedToOkModuloRegions) && let Some(key) = - ProjectionCacheKey::from_poly_projection_predicate( - self, data, + ProjectionCacheKey::from_poly_projection_obligation( + self, + &project_obligation, ) { // If the result is something that we can cache, then mark this @@ -875,7 +866,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig), - ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()), + ProjectAndUnifyResult::Recursive => Ok(EvaluatedToAmbigStackDependent), ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr), } } @@ -919,7 +910,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .at(&obligation.cause, obligation.param_env) .trace(c1, c2) - .eq(DefineOpaqueTypes::No, a.args, b.args) + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + .eq(DefineOpaqueTypes::Yes, a.args, b.args) { return self.evaluate_predicates_recursively( previous_stack, @@ -932,7 +925,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Ok(InferOk { obligations, value: () }) = self .infcx .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::No, c1, c2) + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + .eq(DefineOpaqueTypes::Yes, c1, c2) { return self.evaluate_predicates_recursively( previous_stack, @@ -949,7 +944,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, unevaluated, c.ty(), - Some(obligation.cause.span), + obligation.cause.span, ) { Ok(val) => Ok(val), Err(e) => Err(e), @@ -962,7 +957,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { match self.infcx.at(&obligation.cause, obligation.param_env).eq( - DefineOpaqueTypes::No, + // Can define opaque types as this is only reachable with + // `generic_const_exprs` + DefineOpaqueTypes::Yes, c1, c2, ) { @@ -995,7 +992,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { match self.infcx.at(&obligation.cause, obligation.param_env).eq( - DefineOpaqueTypes::No, + // Only really excercised by generic_const_exprs + DefineOpaqueTypes::Yes, ct.ty(), ty, ) { @@ -1066,7 +1064,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // so we will try to normalize the obligation and evaluate again. // we will replace it with new solver in the future. if EvaluationResult::EvaluatedToErr == result - && fresh_trait_pred.has_projections() + && fresh_trait_pred.has_aliases() && fresh_trait_pred.is_global() { let mut nested_obligations = Vec::new(); @@ -1180,7 +1178,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some(EvaluatedToOk) } else { debug!("evaluate_stack --> recursive, inductive"); - Some(self.treat_inductive_cycle.into()) + Some(EvaluatedToAmbigStackDependent) } } else { None @@ -1230,7 +1228,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } match self.candidate_from_obligation(stack) { - Ok(Some(c)) => self.evaluate_candidate(stack, &c), + Ok(Some(c)) => self.evaluate_candidate(stack, &c, LeakCheckHigherRankedGoal::Yes), Ok(None) => Ok(EvaluatedToAmbig), Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical), Err(..) => Ok(EvaluatedToErr), @@ -1255,6 +1253,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Further evaluates `candidate` to decide whether all type parameters match and whether nested /// obligations are met. Returns whether `candidate` remains viable after this further /// scrutiny. + /// + /// Depending on the value of [LeakCheckHigherRankedGoal], we may ignore the binder of the goal + /// when eagerly detecting higher ranked region errors via the `leak_check`. See that enum for + /// more info. #[instrument( level = "debug", skip(self, stack), @@ -1265,10 +1267,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, stack: &TraitObligationStack<'o, 'tcx>, candidate: &SelectionCandidate<'tcx>, + leak_check_higher_ranked_goal: LeakCheckHigherRankedGoal, ) -> Result { - let mut result = self.evaluation_probe(|this| { - let candidate = (*candidate).clone(); - match this.confirm_candidate(stack.obligation, candidate) { + let mut result = self.evaluation_probe(|this, outer_universe| { + // We eagerly instantiate higher ranked goals to prevent universe errors + // from impacting candidate selection. This matches the behavior of the new + // solver. This slightly weakens type inference. + // + // In case there are no unresolved type or const variables this + // should still not be necessary to select a unique impl as any overlap + // relying on a universe error from higher ranked goals should have resulted + // in an overlap error in coherence. + let p = self.infcx.enter_forall_and_leak_universe(stack.obligation.predicate); + let obligation = stack.obligation.with(this.tcx(), ty::Binder::dummy(p)); + match leak_check_higher_ranked_goal { + LeakCheckHigherRankedGoal::No => *outer_universe = self.infcx.universe(), + LeakCheckHigherRankedGoal::Yes => {} + } + + match this.confirm_candidate(&obligation, candidate.clone()) { Ok(selection) => { debug!(?selection); this.evaluate_predicates_recursively( @@ -1418,10 +1435,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for candidate in candidates { if let ImplCandidate(def_id) = candidate { - if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id) - || obligation.polarity() == tcx.impl_polarity(def_id) - { - result.push(candidate); + match (tcx.impl_polarity(def_id), obligation.polarity()) { + (ty::ImplPolarity::Reservation, _) + | (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => { + result.push(candidate); + } + _ => {} } } else { result.push(candidate); @@ -1477,7 +1497,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // bound regions. let trait_ref = predicate.skip_binder().trait_ref; - coherence::trait_ref_is_knowable::(self.tcx(), trait_ref, |ty| Ok(ty)).unwrap() + coherence::trait_ref_is_knowable::(self.infcx, trait_ref, |ty| Ok(ty)).unwrap() } /// Returns `true` if the global caches can be used. @@ -1617,21 +1637,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => return ControlFlow::Continue(()), }; - for bound in - self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args) - { - // HACK: On subsequent recursions, we only care about bounds that don't - // share the same type as `self_ty`. This is because for truly rigid - // projections, we will never be able to equate, e.g. `::A` - // with `<::A as Tr>::A`. - if in_parent_alias_type { - match bound.kind().skip_binder() { - ty::ClauseKind::Trait(tr) if tr.self_ty() == self_ty => continue, - ty::ClauseKind::Projection(p) if p.self_ty() == self_ty => continue, - _ => {} - } - } + // HACK: On subsequent recursions, we only care about bounds that don't + // share the same type as `self_ty`. This is because for truly rigid + // projections, we will never be able to equate, e.g. `::A` + // with `<::A as Tr>::A`. + let relevant_bounds = if in_parent_alias_type { + self.tcx().item_non_self_assumptions(alias_ty.def_id) + } else { + self.tcx().item_super_predicates(alias_ty.def_id) + }; + for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) { for_each(self, bound, idx)?; idx += 1; } @@ -1694,8 +1710,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack: &TraitObligationStack<'o, 'tcx>, where_clause_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result { - self.evaluation_probe(|this| { - match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { + self.evaluation_probe(|this, outer_universe| { + // Eagerly instantiate higher ranked goals. + // + // See the comment in `evaluate_candidate` to see why. + let p = self.infcx.enter_forall_and_leak_universe(stack.obligation.predicate); + let obligation = stack.obligation.with(this.tcx(), ty::Binder::dummy(p)); + *outer_universe = self.infcx.universe(); + match this.match_where_clause_trait_ref(&obligation, where_clause_trait_ref) { Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations), Err(()) => Ok(EvaluatedToErr), } @@ -1714,6 +1736,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { env_predicate: PolyProjectionPredicate<'tcx>, potentially_unnormalized_candidates: bool, ) -> ProjectionMatchesProjection { + debug_assert_eq!(obligation.predicate.def_id, env_predicate.projection_def_id()); + let mut nested_obligations = Vec::new(); let infer_predicate = self.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, @@ -1754,9 +1778,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // that means that we must have newly inferred something about the GAT. // We should give up in that case. if !generics.params.is_empty() - && obligation.predicate.args[generics.parent_count..] - .iter() - .any(|&p| p.has_non_region_infer() && self.infcx.shallow_resolve(p) != p) + && obligation.predicate.args[generics.parent_count..].iter().any(|&p| { + p.has_non_region_infer() && self.infcx.resolve_vars_if_possible(p) != p + }) { ProjectionMatchesProjection::Ambiguous } else { @@ -1977,7 +2001,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // any associated items and there are no where-clauses. // // We can just arbitrarily drop one of the impls. - Some(ty::ImplOverlapKind::Issue33140) => { + Some(ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects) => { assert_eq!(other.evaluation, victim.evaluation); DropVictim::Yes } @@ -2122,14 +2146,17 @@ impl<'tcx> SelectionContext<'_, 'tcx> { obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])), ), + ty::Pat(ty, _) => Where(obligation.predicate.rebind(vec![*ty])), + ty::Adt(def, args) => { - let sized_crit = def.sized_constraint(self.tcx()); - // (*) binder moved here - Where( - obligation - .predicate - .rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()), - ) + if let Some(sized_crit) = def.sized_constraint(self.tcx()) { + // (*) binder moved here + Where( + obligation.predicate.rebind(vec![sized_crit.instantiate(self.tcx(), args)]), + ) + } else { + Where(ty::Binder::dummy(Vec::new())) + } } ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None, @@ -2181,6 +2208,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Where(obligation.predicate.rebind(tys.iter().collect())) } + ty::Pat(ty, _) => { + // (*) binder moved here + Where(obligation.predicate.rebind(vec![ty])) + } + ty::Coroutine(coroutine_def_id, args) => { match self.tcx().coroutine_movability(coroutine_def_id) { hir::Movability::Static => None, @@ -2262,6 +2294,20 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } + fn fused_iterator_conditions( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + if let ty::Coroutine(did, ..) = *self_ty.kind() + && self.tcx().coroutine_is_gen(did) + { + BuiltinImplConditions::Where(ty::Binder::dummy(Vec::new())) + } else { + BuiltinImplConditions::None + } + } + /// For default impls, we need to break apart a type into its /// "constituent types" -- meaning, the types that it contains. /// @@ -2303,11 +2349,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { bug!("asked to assemble constituent types of unexpected type: {:?}", t); } - ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { - t.rebind(vec![element_ty]) - } + ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => t.rebind(vec![element_ty]), - ty::Array(element_ty, _) | ty::Slice(element_ty) => t.rebind(vec![element_ty]), + ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => t.rebind(vec![ty]), ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet @@ -2667,26 +2711,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn closure_trait_ref_unnormalized( &mut self, - obligation: &PolyTraitObligation<'tcx>, - args: GenericArgsRef<'tcx>, + self_ty: Ty<'tcx>, + fn_trait_def_id: DefId, fn_host_effect: ty::Const<'tcx>, ) -> ty::PolyTraitRef<'tcx> { + let ty::Closure(_, args) = *self_ty.kind() else { + bug!("expected closure, found {self_ty}"); + }; let closure_sig = args.as_closure().sig(); - debug!(?closure_sig); - - // NOTE: The self-type is an unboxed closure type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). - let self_ty = obligation - .predicate - .self_ty() - .no_bound_vars() - .expect("unboxed closure type should not capture bound vars from the predicate"); - closure_trait_ref_and_return_type( self.tcx(), - obligation.predicate.def_id(), + fn_trait_def_id, self_ty, closure_sig, util::TupleArgumentsFlag::No, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 27dd8f26489e7..390e711a18d95 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -239,15 +239,20 @@ fn fulfill_implication<'tcx>( let source_trait = ImplSubject::Trait(source_trait_ref); - let selcx = &mut SelectionContext::new(infcx); + let selcx = SelectionContext::new(infcx); let target_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl); let (target_trait, obligations) = - util::impl_subject_and_oblig(selcx, param_env, target_impl, target_args, error_cause); + util::impl_subject_and_oblig(&selcx, param_env, target_impl, target_args, error_cause); // do the impls unify? If not, no specialization. let Ok(InferOk { obligations: more_obligations, .. }) = infcx .at(&ObligationCause::dummy(), param_env) - .eq(DefineOpaqueTypes::No, source_trait, target_trait) + // Ok to use `Yes`, as all the generic params are already replaced by inference variables, + // which will match the opaque type no matter if it is defining or not. + // Any concrete type that would match the opaque would already be handled by coherence rules, + // and thus either be ok to match here and already have errored, or it won't match, in which + // case there is no issue anyway. + .eq(DefineOpaqueTypes::Yes, source_trait, target_trait) else { debug!("fulfill_implication: {:?} does not unify with {:?}", source_trait, target_trait); return Err(()); @@ -402,10 +407,6 @@ fn report_conflicting_impls<'tcx>( impl_span: Span, err: &mut Diag<'_, G>, ) { - if (overlap.trait_ref, overlap.self_ty).references_error() { - err.downgrade_to_delayed_bug(); - } - match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { err.span_label(span, "first implementation here"); @@ -452,12 +453,17 @@ fn report_conflicting_impls<'tcx>( overlap.trait_ref.print_trait_sugared(), overlap.self_ty.map_or_else(String::new, |ty| format!(" for type `{ty}`")), match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", + Some(FutureCompatOverlapErrorKind::OrderDepTraitObjects) => ": (E0119)", _ => "", } ) }); + // Don't report overlap errors if the header references error + if let Err(err) = (overlap.trait_ref, overlap.self_ty).error_reported() { + return Err(err); + } + match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() @@ -474,7 +480,7 @@ fn report_conflicting_impls<'tcx>( } Some(kind) => { let lint = match kind { - FutureCompatOverlapErrorKind::Issue33140 => ORDER_DEPENDENT_TRAIT_OBJECTS, + FutureCompatOverlapErrorKind::OrderDepTraitObjects => ORDER_DEPENDENT_TRAIT_OBJECTS, FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, }; tcx.node_span_lint( diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 95db9e2092fad..b6c2fcb46eb8a 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -3,6 +3,7 @@ use super::OverlapError; use crate::traits; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; +use rustc_macros::extension; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; @@ -10,7 +11,7 @@ pub use rustc_middle::traits::specialization_graph::*; #[derive(Copy, Clone, Debug)] pub enum FutureCompatOverlapErrorKind { - Issue33140, + OrderDepTraitObjects, LeakCheck, } @@ -149,10 +150,10 @@ impl<'tcx> Children { { match overlap_kind { ty::ImplOverlapKind::Permitted { marker: _ } => {} - ty::ImplOverlapKind::Issue33140 => { + ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects => { *last_lint_mut = Some(FutureCompatOverlapError { error: create_overlap_error(overlap), - kind: FutureCompatOverlapErrorKind::Issue33140, + kind: FutureCompatOverlapErrorKind::OrderDepTraitObjects, }); } } @@ -198,7 +199,7 @@ impl<'tcx> Children { } } -fn iter_children(children: &mut Children) -> impl Iterator + '_ { +fn iter_children(children: &Children) -> impl Iterator + '_ { let nonblanket = children.non_blanket_impls.iter().flat_map(|(_, v)| v.iter()); children.blanket_impls.iter().chain(nonblanket).cloned() } @@ -396,7 +397,7 @@ pub(crate) fn assoc_def( // associated type. Normally this situation // could only arise through a compiler bug -- // if the user wrote a bad item name, it - // should have failed in astconv. + // should have failed during HIR ty lowering. bug!( "No associated type `{}` for {}", tcx.item_name(assoc_def_id), diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index b89406ca02332..6778ac81aeaea 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -126,7 +126,7 @@ impl<'tcx> TypeVisitor> for Search<'tcx> { return ControlFlow::Continue(()); } - ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { + ty::Pat(..) | ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { // First check all contained types and then tell the caller to continue searching. return ty.super_visit_with(self); } diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 2f428564ae736..64ab8378abb00 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -1,6 +1,7 @@ use rustc_infer::infer::at::At; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::type_variable::TypeVariableOrigin; use rustc_infer::traits::{FulfillmentError, TraitEngine}; +use rustc_macros::extension; use rustc_middle::ty::{self, Ty}; use crate::traits::{NormalizeExt, Obligation}; @@ -19,10 +20,9 @@ impl<'tcx> At<'_, 'tcx> { return Ok(ty); }; - let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, - span: self.cause.span, - }); + let new_infer_ty = self + .infcx + .next_ty_var(TypeVariableOrigin { param_def_id: None, span: self.cause.span }); // We simply emit an `alias-eq` goal here, since that will take care of // normalizing the LHS of the projection until it is a rigid projection diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 3f433a9e919a3..b2ba7854f1888 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use super::NormalizeExt; use super::{ObligationCause, PredicateObligation, SelectionContext}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Diag; use rustc_hir::def_id::DefId; use rustc_infer::infer::{InferCtxt, InferOk}; @@ -10,7 +10,7 @@ use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_span::Span; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; pub use rustc_infer::traits::util::*; @@ -205,7 +205,7 @@ impl Iterator for SupertraitDefIds<'_> { /// returning the resulting subject and all obligations that arise. /// The obligations are closed under normalization. pub fn impl_subject_and_oblig<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, + selcx: &SelectionContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, impl_def_id: DefId, impl_args: GenericArgsRef<'tcx>, @@ -344,48 +344,6 @@ pub enum TupleArgumentsFlag { No, } -// Verify that the trait item and its implementation have compatible args lists -pub fn check_args_compatible<'tcx>( - tcx: TyCtxt<'tcx>, - assoc_item: ty::AssocItem, - args: ty::GenericArgsRef<'tcx>, -) -> bool { - fn check_args_compatible_inner<'tcx>( - tcx: TyCtxt<'tcx>, - generics: &'tcx ty::Generics, - args: &'tcx [ty::GenericArg<'tcx>], - ) -> bool { - if generics.count() != args.len() { - return false; - } - - let (parent_args, own_args) = args.split_at(generics.parent_count); - - if let Some(parent) = generics.parent - && let parent_generics = tcx.generics_of(parent) - && !check_args_compatible_inner(tcx, parent_generics, parent_args) - { - return false; - } - - for (param, arg) in std::iter::zip(&generics.params, own_args) { - match (¶m.kind, arg.unpack()) { - (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) - | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) - | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} - _ => return false, - } - } - - true - } - - let generics = tcx.generics_of(assoc_item.def_id); - // Chop off any additional args (RPITIT) args - let args = &args[0..generics.count().min(args.len())]; - check_args_compatible_inner(tcx, generics, args) -} - /// Executes `f` on `value` after replacing all escaping bound variables with placeholders /// and then replaces these placeholders with the original bound variables in the result. /// @@ -431,8 +389,8 @@ pub struct BoundVarReplacer<'me, 'tcx> { // These three maps track the bound variable that were replaced by placeholders. It might be // nice to remove these since we already have the `kind` in the placeholder; we really just need // the `var` (but we *could* bring that into scope if we were to track them as we pass them). - mapped_regions: BTreeMap, - mapped_types: BTreeMap, + mapped_regions: FxIndexMap, + mapped_types: FxIndexMap, mapped_consts: BTreeMap, // The current depth relative to *this* folding, *not* the entire normalization. In other words, // the depth of binders we've passed here. @@ -451,12 +409,13 @@ impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { value: T, ) -> ( T, - BTreeMap, - BTreeMap, + FxIndexMap, + FxIndexMap, BTreeMap, ) { - let mapped_regions: BTreeMap = BTreeMap::new(); - let mapped_types: BTreeMap = BTreeMap::new(); + let mapped_regions: FxIndexMap = + FxIndexMap::default(); + let mapped_types: FxIndexMap = FxIndexMap::default(); let mapped_consts: BTreeMap = BTreeMap::new(); let mut replacer = BoundVarReplacer { @@ -574,8 +533,8 @@ impl<'tcx> TypeFolder> for BoundVarReplacer<'_, 'tcx> { /// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. pub struct PlaceholderReplacer<'me, 'tcx> { infcx: &'me InferCtxt<'tcx>, - mapped_regions: BTreeMap, - mapped_types: BTreeMap, + mapped_regions: FxIndexMap, + mapped_types: FxIndexMap, mapped_consts: BTreeMap, universe_indices: &'me [Option], current_index: ty::DebruijnIndex, @@ -584,8 +543,8 @@ pub struct PlaceholderReplacer<'me, 'tcx> { impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { pub fn replace_placeholders>>( infcx: &'me InferCtxt<'tcx>, - mapped_regions: BTreeMap, - mapped_types: BTreeMap, + mapped_regions: FxIndexMap, + mapped_types: FxIndexMap, mapped_consts: BTreeMap, universe_indices: &'me [Option], value: T, @@ -690,7 +649,7 @@ impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - let ct = self.infcx.shallow_resolve(ct); + let ct = self.infcx.shallow_resolve_const(ct); if let ty::ConstKind::Placeholder(p) = ct.kind() { let replace_var = self.mapped_consts.get(&p); match replace_var { diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 46a68508753c0..178f3c63ef73c 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; use rustc_span::{sym, Span}; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use std::fmt::Debug; use std::ops::ControlFlow; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 64f02bfd32116..28ee76f7145c2 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -44,7 +44,7 @@ pub fn obligations<'tcx>( GenericArgKind::Const(ct) => { match ct.kind() { ty::ConstKind::Infer(_) => { - let resolved = infcx.shallow_resolve(ct); + let resolved = infcx.shallow_resolve_const(ct); if resolved == ct { // No progress. return None; @@ -363,7 +363,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Negative trait predicates don't require supertraits to hold, just // that their args are WF. - if trait_pred.polarity == ty::ImplPolarity::Negative { + if trait_pred.polarity == ty::PredicatePolarity::Negative { self.compute_negative_trait_pred(trait_ref); return; } @@ -381,7 +381,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { if let Some(parent_trait_pred) = predicate.to_opt_poly_trait_pred() { cause = cause.derived_cause( parent_trait_pred, - traits::ObligationCauseCode::DerivedObligation, + traits::ObligationCauseCode::WellFormedDerivedObligation, ); } extend_cause_with_original_assoc_item_obligation(tcx, item, &mut cause, predicate); @@ -647,6 +647,8 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { fn visit_ty(&mut self, t: as ty::Interner>::Ty) -> Self::Result { debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind()); + let tcx = self.tcx(); + match *t.kind() { ty::Bool | ty::Char @@ -679,6 +681,10 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // Note that we handle the len is implicitly checked while walking `arg`. } + ty::Pat(subty, _) => { + self.require_sized(subty, traits::MiscObligation); + } + ty::Tuple(tys) => { if let Some((_last, rest)) = tys.split_last() { for &elem in rest { @@ -687,7 +693,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } } - ty::RawPtr(_) => { + ty::RawPtr(_, _) => { // Simple cases that are WF if their type args are WF. } @@ -707,6 +713,14 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } ty::FnDef(did, args) => { + // HACK: Check the return type of function definitions for + // well-formedness to mostly fix #84533. This is still not + // perfect and there may be ways to abuse the fact that we + // ignore requirements with escaping bound vars. That's a + // more general issue however. + let fn_sig = tcx.fn_sig(did).instantiate(tcx, args); + fn_sig.output().skip_binder().visit_with(self); + let obligations = self.nominal_obligations(did, args); self.out.extend(obligations); } @@ -716,7 +730,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { let cause = self.cause(traits::ReferenceOutlivesReferent(t)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, @@ -805,12 +819,12 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // obligations that don't refer to Self and // checking those - let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; + let defer_to_coercion = tcx.features().object_safe_for_dispatch; if !defer_to_coercion { if let Some(principal) = data.principal_def_id() { self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, self.cause(traits::WellFormed(None)), self.recursion_depth, self.param_env, @@ -835,7 +849,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::Infer(_) => { let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, @@ -850,6 +864,8 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } fn visit_const(&mut self, c: as ty::Interner>::Const) -> Self::Result { + let tcx = self.tcx(); + match c.kind() { ty::ConstKind::Unevaluated(uv) => { if !c.has_escaping_bound_vars() { @@ -861,7 +877,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, @@ -873,7 +889,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, @@ -895,7 +911,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( - self.tcx(), + tcx, cause, self.recursion_depth, self.param_env, diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index b2b5c6cd9096f..51f38964415b0 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -5,6 +5,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{FulfillmentErrorCode, TraitEngineExt as _}; +use rustc_middle::bug; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; @@ -12,6 +13,7 @@ use rustc_trait_selection::traits::{ ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt, Unimplemented, }; +use tracing::debug; /// Attempts to resolve an obligation to an `ImplSource`. The result is /// a shallow `ImplSource` resolution, meaning that we do not diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index f40c3614e1c00..55abd6098ec97 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -2,6 +2,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; use rustc_middle::ty::GenericArgs; @@ -11,6 +12,7 @@ use rustc_trait_selection::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, dtorck_constraint_for_ty_inner, }; use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; +use tracing::debug; pub(crate) fn provide(p: &mut Providers) { *p = Providers { dropck_outlives, adt_dtorck_constraint, ..*p }; diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index ce22da23fffd3..c9ad096c6e9d0 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -6,6 +6,7 @@ use rustc_trait_selection::traits::query::CanonicalPredicateGoal; use rustc_trait_selection::traits::{ EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode, }; +use tracing::debug; pub(crate) fn provide(p: &mut Providers) { *p = Providers { evaluate_obligation, ..*p }; diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index e73bbf6048ece..bc5436f76f1cd 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -2,11 +2,6 @@ #![recursion_limit = "256"] -#[macro_use] -extern crate tracing; -#[macro_use] -extern crate rustc_middle; - mod codegen; mod dropck_outlives; mod evaluate_obligation; diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 0576fe0102785..c5ebc2d26a794 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -4,6 +4,7 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Normalized, ObligationCause}; +use tracing::debug; pub(crate) fn provide(p: &mut Providers) { *p = Providers { diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 92a19fb91198c..559c05eb3e784 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -10,6 +10,7 @@ use rustc_trait_selection::traits::query::{ use rustc_trait_selection::traits::{ self, FulfillmentErrorCode, ObligationCause, SelectionContext, }; +use tracing::debug; pub(crate) fn provide(p: &mut Providers) { *p = Providers { diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index b8922696e3067..3378d1c6754db 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -2,6 +2,7 @@ use super::{nfa, Byte, Nfa, Ref}; use crate::Map; use std::fmt; use std::sync::atomic::{AtomicU32, Ordering}; +use tracing::instrument; #[derive(PartialEq, Clone, Debug)] pub(crate) struct Dfa @@ -35,6 +36,7 @@ impl Transitions where R: Ref, { + #[allow(dead_code)] fn insert(&mut self, transition: Transition, state: State) { match transition { Transition::Byte(b) => { @@ -82,6 +84,7 @@ impl Dfa where R: Ref, { + #[allow(dead_code)] pub(crate) fn unit() -> Self { let transitions: Map> = Map::default(); let start = State::new(); diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index a7c60c3b49046..0377ed5d4c57a 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -65,7 +65,7 @@ pub mod rustc { use std::fmt::{self, Write}; /// A reference in the layout. - #[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)] + #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] pub struct Ref<'tcx> { pub lifetime: ty::Region<'tcx>, pub ty: Ty<'tcx>, diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs index 78fcceb5f2cb5..3c5963202c638 100644 --- a/compiler/rustc_transmute/src/layout/nfa.rs +++ b/compiler/rustc_transmute/src/layout/nfa.rs @@ -160,6 +160,7 @@ where Self { transitions, start, accepting } } + #[allow(dead_code)] pub(crate) fn edges_from(&self, start: State) -> Option<&Map, Set>> { self.transitions.get(&start) } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 9a43d67d4351e..604b68d2cd4a6 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -173,16 +173,20 @@ pub(crate) mod rustc { use super::Tree; use crate::layout::rustc::{Def, Ref}; + use rustc_middle::ty::layout::HasTyCtxt; + use rustc_middle::ty::layout::LayoutCx; use rustc_middle::ty::layout::LayoutError; - use rustc_middle::ty::util::Discr; + use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::AdtDef; - use rustc_middle::ty::GenericArgsRef; - use rustc_middle::ty::ParamEnv; - use rustc_middle::ty::VariantDef; + use rustc_middle::ty::AdtKind; + use rustc_middle::ty::List; + use rustc_middle::ty::ScalarInt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; - use rustc_target::abi::Align; - use std::alloc; + use rustc_target::abi::FieldsShape; + use rustc_target::abi::Size; + use rustc_target::abi::TyAndLayout; + use rustc_target::abi::Variants; #[derive(Debug, Copy, Clone)] pub(crate) enum Err { @@ -206,175 +210,64 @@ pub(crate) mod rustc { } } - trait LayoutExt { - fn clamp_align(&self, min_align: Align, max_align: Align) -> Self; - } - - impl LayoutExt for alloc::Layout { - fn clamp_align(&self, min_align: Align, max_align: Align) -> Self { - let min_align = min_align.bytes().try_into().unwrap(); - let max_align = max_align.bytes().try_into().unwrap(); - Self::from_size_align(self.size(), self.align().clamp(min_align, max_align)).unwrap() - } - } - - struct LayoutSummary { - total_align: Align, - total_size: usize, - discriminant_size: usize, - discriminant_align: Align, - } - - impl LayoutSummary { - fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result> { - use rustc_middle::ty::ParamEnvAnd; - use rustc_target::abi::{TyAndLayout, Variants}; - - let param_env = ParamEnv::reveal_all(); - let param_env_and_type = ParamEnvAnd { param_env, value: ty }; - let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?; - - let total_size: usize = layout.size().bytes_usize(); - let total_align: Align = layout.align().abi; - let discriminant_align: Align; - let discriminant_size: usize; - - if let Variants::Multiple { tag, .. } = layout.variants() { - discriminant_align = tag.align(&ctx).abi; - discriminant_size = tag.size(&ctx).bytes_usize(); - } else { - discriminant_align = Align::ONE; - discriminant_size = 0; - }; - - Ok(Self { total_align, total_size, discriminant_align, discriminant_size }) - } - - fn into(&self) -> alloc::Layout { - alloc::Layout::from_size_align( - self.total_size, - self.total_align.bytes().try_into().unwrap(), - ) - .unwrap() - } - } - impl<'tcx> Tree, Ref<'tcx>> { - pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result { - use rustc_middle::ty::FloatTy::*; - use rustc_middle::ty::IntTy::*; - use rustc_middle::ty::UintTy::*; + pub fn from_ty( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result { use rustc_target::abi::HasDataLayout; - if let Err(e) = ty.error_reported() { + if let Err(e) = ty_and_layout.ty.error_reported() { return Err(Err::TypeError(e)); } - let target = tcx.data_layout(); + let target = cx.tcx.data_layout(); + let pointer_size = target.pointer_size; - match ty.kind() { + match ty_and_layout.ty.kind() { ty::Bool => Ok(Self::bool()), - ty::Int(I8) | ty::Uint(U8) => Ok(Self::u8()), - ty::Int(I16) | ty::Uint(U16) => Ok(Self::number(2)), - ty::Int(I32) | ty::Uint(U32) | ty::Float(F32) => Ok(Self::number(4)), - ty::Int(I64) | ty::Uint(U64) | ty::Float(F64) => Ok(Self::number(8)), - ty::Int(I128) | ty::Uint(U128) => Ok(Self::number(16)), - ty::Int(Isize) | ty::Uint(Usize) => { - Ok(Self::number(target.pointer_size.bytes_usize())) + ty::Float(nty) => { + let width = nty.bit_width() / 8; + Ok(Self::number(width as _)) } - ty::Tuple(members) => { - if members.len() == 0 { - Ok(Tree::unit()) - } else { - Err(Err::NotYetSupported) - } + ty::Int(nty) => { + let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; + Ok(Self::number(width as _)) } - ty::Array(ty, len) => { - let len = len - .try_eval_target_usize(tcx, ParamEnv::reveal_all()) - .ok_or(Err::NotYetSupported)?; - let elt = Tree::from_ty(*ty, tcx)?; - Ok(std::iter::repeat(elt) - .take(len as usize) - .fold(Tree::unit(), |tree, elt| tree.then(elt))) + ty::Uint(nty) => { + let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; + Ok(Self::number(width as _)) } - ty::Adt(adt_def, args_ref) => { - use rustc_middle::ty::AdtKind; + ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx), - // If the layout is ill-specified, halt. - if !(adt_def.repr().c() || adt_def.repr().int.is_some()) { + ty::Array(inner_ty, len) => { + let FieldsShape::Array { stride, count } = &ty_and_layout.fields else { return Err(Err::NotYetSupported); - } + }; + let inner_ty_and_layout = cx.layout_of(*inner_ty)?; + assert_eq!(*stride, inner_ty_and_layout.size); + let elt = Tree::from_ty(inner_ty_and_layout, cx)?; + Ok(std::iter::repeat(elt) + .take(*count as usize) + .fold(Tree::unit(), |tree, elt| tree.then(elt))) + } - // Compute a summary of the type's layout. - let layout_summary = LayoutSummary::from_ty(ty, tcx)?; - - // The layout begins with this adt's visibility. - let vis = Self::def(Def::Adt(*adt_def)); - - // And is followed the layout(s) of its variants - Ok(vis.then(match adt_def.adt_kind() { - AdtKind::Struct => Self::from_repr_c_variant( - ty, - *adt_def, - args_ref, - &layout_summary, - None, - adt_def.non_enum_variant(), - tcx, - )?, - AdtKind::Enum => { - trace!(?adt_def, "treeifying enum"); - let mut tree = Tree::uninhabited(); - - for (idx, discr) in adt_def.discriminants(tcx) { - tree = tree.or(Self::from_repr_c_variant( - ty, - *adt_def, - args_ref, - &layout_summary, - Some(discr), - adt_def.variant(idx), - tcx, - )?); - } - - tree - } - AdtKind::Union => { - // is the layout well-defined? - if !adt_def.repr().c() { - return Err(Err::NotYetSupported); - } - - let ty_layout = layout_of(tcx, ty)?; - - let mut tree = Tree::uninhabited(); - - for field in adt_def.all_fields() { - let variant_ty = field.ty(tcx, args_ref); - let variant_layout = layout_of(tcx, variant_ty)?; - let padding_needed = ty_layout.size() - variant_layout.size(); - let variant = Self::def(Def::Field(field)) - .then(Self::from_ty(variant_ty, tcx)?) - .then(Self::padding(padding_needed)); - - tree = tree.or(variant); - } - - tree - } - })) + ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => { + match adt_def.adt_kind() { + AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx), + AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx), + AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx), + } } ty::Ref(lifetime, ty, mutability) => { - let layout = layout_of(tcx, *ty)?; - let align = layout.align(); - let size = layout.size(); + let ty_and_layout = cx.layout_of(*ty)?; + let align = ty_and_layout.align.abi.bytes_usize(); + let size = ty_and_layout.size.bytes_usize(); Ok(Tree::Ref(Ref { lifetime: *lifetime, ty: *ty, @@ -388,121 +281,195 @@ pub(crate) mod rustc { } } - fn from_repr_c_variant( - ty: Ty<'tcx>, - adt_def: AdtDef<'tcx>, - args_ref: GenericArgsRef<'tcx>, - layout_summary: &LayoutSummary, - discr: Option>, - variant_def: &'tcx VariantDef, - tcx: TyCtxt<'tcx>, + /// Constructs a `Tree` from a tuple. + fn from_tuple( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + members: &'tcx List>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, ) -> Result { - let mut tree = Tree::unit(); - - let repr = adt_def.repr(); - let min_align = repr.align.unwrap_or(Align::ONE); - let max_align = repr.pack.unwrap_or(Align::MAX); - - let clamp = - |align: Align| align.clamp(min_align, max_align).bytes().try_into().unwrap(); - - let variant_span = trace_span!( - "treeifying variant", - min_align = ?min_align, - max_align = ?max_align, - ) - .entered(); - - let mut variant_layout = alloc::Layout::from_size_align( - 0, - layout_summary.total_align.bytes().try_into().unwrap(), - ) - .unwrap(); - - // The layout of the variant is prefixed by the discriminant, if any. - if let Some(discr) = discr { - trace!(?discr, "treeifying discriminant"); - let discr_layout = alloc::Layout::from_size_align( - layout_summary.discriminant_size, - clamp(layout_summary.discriminant_align), - ) - .unwrap(); - trace!(?discr_layout, "computed discriminant layout"); - variant_layout = variant_layout.extend(discr_layout).unwrap().0; - tree = tree.then(Self::from_discr(discr, tcx, layout_summary.discriminant_size)); + match &ty_and_layout.fields { + FieldsShape::Primitive => { + assert_eq!(members.len(), 1); + let inner_ty = members[0]; + let inner_ty_and_layout = cx.layout_of(inner_ty)?; + assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout); + Self::from_ty(inner_ty_and_layout, cx) + } + FieldsShape::Arbitrary { offsets, .. } => { + assert_eq!(offsets.len(), members.len()); + Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx) + } + FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported), } + } - // Next come fields. - let fields_span = trace_span!("treeifying fields").entered(); - for field_def in variant_def.fields.iter() { - let field_ty = field_def.ty(tcx, args_ref); - let _span = trace_span!("treeifying field", field = ?field_ty).entered(); + /// Constructs a `Tree` from a struct. + /// + /// # Panics + /// + /// Panics if `def` is not a struct definition. + fn from_struct( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + def: AdtDef<'tcx>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result { + assert!(def.is_struct()); + let def = Def::Adt(def); + Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx) + } - // begin with the field's visibility - tree = tree.then(Self::def(Def::Field(field_def))); + /// Constructs a `Tree` from an enum. + /// + /// # Panics + /// + /// Panics if `def` is not an enum definition. + fn from_enum( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + def: AdtDef<'tcx>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result { + assert!(def.is_enum()); + let layout = ty_and_layout.layout; - // compute the field's layout characteristics - let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align); + if let Variants::Multiple { tag_field, .. } = layout.variants() { + // For enums (but not coroutines), the tag field is + // currently always the first field of the layout. + assert_eq!(*tag_field, 0); + } - // next comes the field's padding - let padding_needed = variant_layout.padding_needed_for(field_layout.align()); - if padding_needed > 0 { - tree = tree.then(Self::padding(padding_needed)); - } + let variants = def.discriminants(cx.tcx()).try_fold( + Self::uninhabited(), + |variants, (idx, ref discriminant)| { + let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx)); + let variant_def = Def::Variant(def.variant(idx)); + let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx); + let variant = Self::from_variant( + variant_def, + tag, + variant_ty_and_layout, + layout.size, + cx, + )?; + Result::::Ok(variants.or(variant)) + }, + )?; + + return Ok(Self::def(Def::Adt(def)).then(variants)); + } - // finally, the field's layout - tree = tree.then(Self::from_ty(field_ty, tcx)?); + /// Constructs a `Tree` from a 'variant-like' layout. + /// + /// A 'variant-like' layout includes those of structs and, of course, + /// enum variants. Pragmatically speaking, this method supports anything + /// with `FieldsShape::Arbitrary`. + /// + /// Note: This routine assumes that the optional `tag` is the first + /// field, and enum callers should check that `tag_field` is, in fact, + /// `0`. + fn from_variant( + def: Def<'tcx>, + tag: Option, + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + total_size: Size, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result { + // This constructor does not support non-`FieldsShape::Arbitrary` + // layouts. + let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields() + else { + return Err(Err::NotYetSupported); + }; + + // When this function is invoked with enum variants, + // `ty_and_layout.size` does not encompass the entire size of the + // enum. We rely on `total_size` for this. + assert!(ty_and_layout.size <= total_size); + + let mut size = Size::ZERO; + let mut struct_tree = Self::def(def); - // extend the variant layout with the field layout - variant_layout = variant_layout.extend(field_layout).unwrap().0; + // If a `tag` is provided, place it at the start of the layout. + if let Some(tag) = tag { + size += tag.size(); + struct_tree = struct_tree.then(Self::from_tag(tag, cx.tcx)); } - drop(fields_span); - // finally: padding - let padding_span = trace_span!("adding trailing padding").entered(); - if layout_summary.total_size > variant_layout.size() { - let padding_needed = layout_summary.total_size - variant_layout.size(); - tree = tree.then(Self::padding(padding_needed)); - }; - drop(padding_span); - drop(variant_span); - Ok(tree) + // Append the fields, in memory order, to the layout. + let inverse_memory_index = memory_index.invert_bijective_mapping(); + for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() { + // Add interfield padding. + let padding_needed = offsets[*field_idx] - size; + let padding = Self::padding(padding_needed.bytes_usize()); + + let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize()); + let field_tree = Self::from_ty(field_ty_and_layout, cx)?; + + struct_tree = struct_tree.then(padding).then(field_tree); + + size += padding_needed + field_ty_and_layout.size; + } + + // Add trailing padding. + let padding_needed = total_size - size; + let trailing_padding = Self::padding(padding_needed.bytes_usize()); + + Ok(struct_tree.then(trailing_padding)) } - pub fn from_discr(discr: Discr<'tcx>, tcx: TyCtxt<'tcx>, size: usize) -> Self { + /// Constructs a `Tree` representing the value of a enum tag. + fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self { use rustc_target::abi::Endian; - + let size = tag.size(); + let bits = tag.assert_bits(size); let bytes: [u8; 16]; let bytes = match tcx.data_layout.endian { Endian::Little => { - bytes = discr.val.to_le_bytes(); - &bytes[..size] + bytes = bits.to_le_bytes(); + &bytes[..size.bytes_usize()] } Endian::Big => { - bytes = discr.val.to_be_bytes(); - &bytes[bytes.len() - size..] + bytes = bits.to_be_bytes(); + &bytes[bytes.len() - size.bytes_usize()..] } }; Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect()) } - } - fn layout_of<'tcx>( - ctx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - ) -> Result> { - use rustc_middle::ty::ParamEnvAnd; - use rustc_target::abi::TyAndLayout; - - let param_env = ParamEnv::reveal_all(); - let param_env_and_type = ParamEnvAnd { param_env, value: ty }; - let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?; - let layout = alloc::Layout::from_size_align( - layout.size().bytes_usize(), - layout.align().abi.bytes().try_into().unwrap(), - ) - .unwrap(); - trace!(?ty, ?layout, "computed layout for type"); - Ok(layout) + /// Constructs a `Tree` from a union. + /// + /// # Panics + /// + /// Panics if `def` is not a union definition. + fn from_union( + ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>, + def: AdtDef<'tcx>, + cx: LayoutCx<'tcx, TyCtxt<'tcx>>, + ) -> Result { + assert!(def.is_union()); + + let union_layout = ty_and_layout.layout; + + // This constructor does not support non-`FieldsShape::Union` + // layouts. Fields of this shape are all placed at offset 0. + let FieldsShape::Union(fields) = union_layout.fields() else { + return Err(Err::NotYetSupported); + }; + + let fields = &def.non_enum_variant().fields; + let fields = fields.iter_enumerated().try_fold( + Self::uninhabited(), + |fields, (idx, ref field_def)| { + let field_def = Def::Field(field_def); + let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize()); + let field = Self::from_ty(field_ty_and_layout, cx)?; + let trailing_padding_needed = union_layout.size - field_ty_and_layout.size; + let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize()); + let field_and_padding = field.then(trailing_padding); + Result::::Ok(fields.or(field_and_padding)) + }, + )?; + + Ok(Self::def(Def::Adt(def)).then(fields)) + } } } diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index e871c4659b4b9..8d7d81d8f7350 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -1,9 +1,6 @@ #![feature(alloc_layout_extra)] #![feature(never_type)] -#![allow(dead_code, unused_variables)] - -#[macro_use] -extern crate tracing; +#![allow(unused_variables)] pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}; @@ -49,6 +46,8 @@ pub enum Reason { DstIsNotYetSupported, /// The layout of the destination type is bit-incompatible with the source type. DstIsBitIncompatible, + /// The destination type is uninhabited. + DstUninhabited, /// The destination type may carry safety invariants. DstMayHaveSafetyInvariants, /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized. @@ -89,6 +88,7 @@ mod rustc { use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::ValTree; + use rustc_span::DUMMY_SP; /// The source and destination types of a transmutation. #[derive(TypeVisitable, Debug, Clone, Copy)] @@ -135,7 +135,7 @@ mod rustc { use rustc_middle::ty::ScalarInt; use rustc_span::symbol::sym; - let Ok(cv) = c.eval(tcx, param_env, None) else { + let Ok(cv) = c.eval(tcx, param_env, DUMMY_SP) else { return Some(Self { alignment: true, lifetimes: true, diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index 16d15580a05b8..dee5a72c3bcca 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -1,3 +1,5 @@ +use tracing::{debug, instrument, trace}; + pub(crate) mod query_context; #[cfg(test)] mod tests; @@ -33,6 +35,9 @@ mod rustc { use super::*; use crate::layout::tree::rustc::Err; + use rustc_middle::ty::layout::LayoutCx; + use rustc_middle::ty::layout::LayoutOf; + use rustc_middle::ty::ParamEnv; use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; @@ -43,12 +48,20 @@ mod rustc { pub fn answer(self) -> Answer< as QueryContext>::Ref> { let Self { src, dst, assume, context } = self; + let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() }; + let layout_of = |ty| { + layout_cx + .layout_of(ty) + .map_err(|_| Err::NotYetSupported) + .and_then(|tl| Tree::from_ty(tl, layout_cx)) + }; + // Convert `src` and `dst` from their rustc representations, to `Tree`-based // representations. If these conversions fail, conclude that the transmutation is // unacceptable; the layouts of both the source and destination types must be // well-defined. - let src = Tree::from_ty(src, context); - let dst = Tree::from_ty(dst, context); + let src = layout_of(src); + let dst = layout_of(dst); match (src, dst) { (Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => { @@ -86,6 +99,10 @@ where // references. let src = src.prune(&|def| false); + if src.is_inhabited() && !dst.is_inhabited() { + return Answer::No(Reason::DstUninhabited); + } + trace!(?src, "pruned src"); // Remove all `Def` nodes from `dst`, additionally... diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index 54ed03d44e639..1ccb6f36c8ef5 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -5,8 +5,6 @@ pub(crate) trait QueryContext { type Def: layout::Def; type Ref: layout::Ref; type Scope: Copy; - - fn min_align(&self, reference: Self::Ref) -> usize; } #[cfg(test)] @@ -31,10 +29,6 @@ pub(crate) mod test { type Def = Def; type Ref = !; type Scope = (); - - fn min_align(&self, reference: !) -> usize { - unimplemented!() - } } } @@ -48,9 +42,5 @@ mod rustc { type Ref = layout::rustc::Ref<'tcx>; type Scope = Ty<'tcx>; - - fn min_align(&self, reference: Self::Ref) -> usize { - unimplemented!() - } } } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index a5328baadb5fc..c6b8362850663 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -102,6 +102,7 @@ fn fn_sig_for_fn_abi<'tcx>( ) } ty::CoroutineClosure(def_id, args) => { + let coroutine_ty = Ty::new_coroutine_closure(tcx, def_id, args); let sig = args.as_coroutine_closure().coroutine_closure_sig(); let bound_vars = tcx.mk_bound_variable_kinds_from_iter( sig.bound_vars().iter().chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))), @@ -111,18 +112,28 @@ fn fn_sig_for_fn_abi<'tcx>( kind: ty::BoundRegionKind::BrEnv, }; let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br); - // When this `CoroutineClosure` comes from a `ConstructCoroutineInClosureShim`, // make sure we respect the `target_kind` in that shim. // FIXME(async_closures): This shouldn't be needed, and we should be populating // a separate def-id for these bodies. - let mut kind = args.as_coroutine_closure().kind(); - if let InstanceDef::ConstructCoroutineInClosureShim { target_kind, .. } = instance.def { - kind = target_kind; - } + let mut coroutine_kind = args.as_coroutine_closure().kind(); let env_ty = - tcx.closure_env_ty(Ty::new_coroutine_closure(tcx, def_id, args), kind, env_region); + if let InstanceDef::ConstructCoroutineInClosureShim { receiver_by_ref, .. } = + instance.def + { + coroutine_kind = ty::ClosureKind::FnOnce; + + // Implementations of `FnMut` and `Fn` for coroutine-closures + // still take their receiver by (mut) ref. + if receiver_by_ref { + Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty) + } else { + coroutine_ty + } + } else { + tcx.closure_env_ty(coroutine_ty, coroutine_kind, env_region) + }; let sig = sig.skip_binder(); ty::Binder::bind_with_vars( @@ -132,7 +143,7 @@ fn fn_sig_for_fn_abi<'tcx>( tcx, args.as_coroutine_closure().parent_args(), tcx.coroutine_for_closure(def_id), - kind, + coroutine_kind, env_region, args.as_coroutine_closure().tupled_upvars_ty(), args.as_coroutine_closure().coroutine_captures_by_ref_ty(), @@ -161,7 +172,7 @@ fn fn_sig_for_fn_abi<'tcx>( // make sure we respect the `target_kind` in that shim. // FIXME(async_closures): This shouldn't be needed, and we should be populating // a separate def-id for these bodies. - if let InstanceDef::CoroutineKindShim { target_kind, .. } = instance.def { + if let InstanceDef::CoroutineKindShim { .. } = instance.def { // Grab the parent coroutine-closure. It has the same args for the purposes // of instantiation, so this will be okay to do. let ty::CoroutineClosure(_, coroutine_closure_args) = *tcx @@ -181,7 +192,7 @@ fn fn_sig_for_fn_abi<'tcx>( tcx, coroutine_closure_args.parent_args(), did, - target_kind, + ty::ClosureKind::FnOnce, tcx.lifetimes.re_erased, coroutine_closure_args.tupled_upvars_ty(), coroutine_closure_args.coroutine_captures_by_ref_ty(), @@ -311,7 +322,7 @@ fn fn_sig_for_fn_abi<'tcx>( #[inline] fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi, c_variadic: bool) -> Conv { use rustc_target::spec::abi::Abi::*; - match tcx.sess.target.adjust_abi(abi, c_variadic) { + match tcx.sess.target.adjust_abi(&tcx, abi, c_variadic) { RustIntrinsic | Rust | RustCall => Conv::Rust, // This is intentionally not using `Conv::Cold`, as that has to preserve @@ -616,7 +627,7 @@ fn fn_abi_new_uncached<'tcx>( let is_return = arg_idx.is_none(); let is_drop_target = is_drop_in_place && arg_idx == Some(0); let drop_target_pointee = is_drop_target.then(|| match ty.kind() { - ty::RawPtr(ty::TypeAndMut { ty, .. }) => *ty, + ty::RawPtr(ty, _) => *ty, _ => bug!("argument to drop_in_place is not a raw ptr: {:?}", ty), }); @@ -843,7 +854,7 @@ fn make_thin_self_ptr<'tcx>( // we now have a type like `*mut RcBox` // change its layout to that of `*mut ()`, a thin pointer, but keep the same type // this is understood as a special case elsewhere in the compiler - let unit_ptr_ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx)); + let unit_ptr_ty = Ty::new_mut_ptr(tcx, tcx.types.unit); TyAndLayout { ty: fat_pointer_ty, diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 50a8eb869b992..ba75424ec0c6d 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -1,11 +1,10 @@ use rustc_data_structures::fx::FxIndexSet; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, HirId}; -use rustc_index::IndexVec; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt, TyCtxtFeed}; +use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_span::symbol::kw; pub(crate) fn provide(providers: &mut Providers) { @@ -238,28 +237,6 @@ fn associated_types_for_impl_traits_in_associated_fn( } } -fn feed_hir(feed: &TyCtxtFeed<'_, LocalDefId>) { - feed.local_def_id_to_hir_id(HirId::make_owner(feed.def_id())); - - let node = hir::OwnerNode::AssocOpaqueTy(&hir::AssocOpaqueTy {}); - let bodies = Default::default(); - let attrs = hir::AttributeMap::EMPTY; - - let (opt_hash_including_bodies, _) = feed.tcx.hash_owner_nodes(node, &bodies, &attrs.map); - feed.opt_hir_owner_nodes(Some(feed.tcx.arena.alloc(hir::OwnerNodes { - opt_hash_including_bodies, - nodes: IndexVec::from_elem_n( - hir::ParentedNode { - parent: hir::ItemLocalId::INVALID, - node: hir::Node::AssocOpaqueTy(&hir::AssocOpaqueTy {}), - }, - 1, - ), - bodies, - }))); - feed.feed_owner_id().hir_attrs(attrs); -} - /// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated /// function from a trait, synthesize an associated type for that `impl Trait` /// that inherits properties that we infer from the method and the opaque type. @@ -281,7 +258,7 @@ fn associated_type_for_impl_trait_in_trait( let local_def_id = trait_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); - feed_hir(&trait_assoc_ty); + trait_assoc_ty.feed_hir(); // Copy span of the opaque. trait_assoc_ty.def_ident_span(Some(span)); @@ -335,7 +312,7 @@ fn associated_type_for_impl_trait_in_impl( let local_def_id = impl_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); - feed_hir(&impl_assoc_ty); + impl_assoc_ty.feed_hir(); // Copy span of the opaque. impl_assoc_ty.def_ident_span(Some(span)); diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs index 51b908881eb49..cb95239e99192 100644 --- a/compiler/rustc_ty_utils/src/common_traits.rs +++ b/compiler/rustc_ty_utils/src/common_traits.rs @@ -22,6 +22,17 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) is_item_raw(tcx, query, LangItem::Unpin) } +fn has_surface_async_drop_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> bool { + is_item_raw(tcx, query, LangItem::AsyncDrop) +} + +fn has_surface_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + is_item_raw(tcx, query, LangItem::Drop) +} + fn is_item_raw<'tcx>( tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, @@ -34,5 +45,13 @@ fn is_item_raw<'tcx>( } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers }; + *providers = Providers { + is_copy_raw, + is_sized_raw, + is_freeze_raw, + is_unpin_raw, + has_surface_async_drop_raw, + has_surface_drop_raw, + ..*providers + }; } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 2a2e53a81ed81..acbcc3918b2fb 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -82,7 +82,7 @@ fn check_binop(op: mir::BinOp) -> bool { match op { Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge - | Gt => true, + | Gt | Cmp => true, Offset => false, } } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 2816bcc888b0c..d0aa4eb2e714a 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -54,6 +54,28 @@ fn resolve_instance<'tcx>( debug!(" => trivial drop glue"); ty::InstanceDef::DropGlue(def_id, None) } + } else if Some(def_id) == tcx.lang_items().async_drop_in_place_fn() { + let ty = args.type_at(0); + + if !ty.is_async_destructor_noop(tcx, param_env) { + match *ty.kind() { + ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::Tuple(..) + | ty::Adt(..) + | ty::Dynamic(..) + | ty::Array(..) + | ty::Slice(..) => {} + // Async destructor ctor shims can only be built from ADTs. + _ => return Ok(None), + } + debug!(" => nontrivial async drop glue ctor"); + ty::InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty)) + } else { + debug!(" => trivial async drop glue ctor"); + ty::InstanceDef::AsyncDropGlueCtorShim(def_id, None) + } } else { debug!(" => free item"); // FIXME(effects): we may want to erase the effect param if that is present on this item. @@ -79,18 +101,11 @@ fn resolve_associated_item<'tcx>( let vtbl = match tcx.codegen_select_candidate((param_env, trait_ref)) { Ok(vtbl) => vtbl, - Err(CodegenObligationError::Ambiguity) => { - let reported = tcx.dcx().span_delayed_bug( - tcx.def_span(trait_item_id), - format!( - "encountered ambiguity selecting `{trait_ref:?}` during codegen, presuming due to \ - overflow or prior type error", - ), - ); - return Err(reported); - } - Err(CodegenObligationError::Unimplemented) => return Ok(None), - Err(CodegenObligationError::FulfillmentError) => return Ok(None), + Err( + CodegenObligationError::Ambiguity + | CodegenObligationError::Unimplemented + | CodegenObligationError::FulfillmentError, + ) => return Ok(None), }; // Now that we know which impl is being used, we can dispatch to @@ -282,7 +297,7 @@ fn resolve_associated_item<'tcx>( Some(Instance { def: ty::InstanceDef::ConstructCoroutineInClosureShim { coroutine_closure_def_id, - target_kind: ty::ClosureKind::FnOnce, + receiver_by_ref: target_kind != ty::ClosureKind::FnOnce, }, args, }) @@ -297,25 +312,20 @@ fn resolve_associated_item<'tcx>( { match *rcvr_args.type_at(0).kind() { ty::CoroutineClosure(coroutine_closure_def_id, args) => { - match (target_kind, args.as_coroutine_closure().kind()) { - (ClosureKind::FnOnce | ClosureKind::FnMut, ClosureKind::Fn) - | (ClosureKind::FnOnce, ClosureKind::FnMut) => { - // If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure, - // or `AsyncFnOnce` for a by-mut closure, then construct a new body that - // has the right return types. - // - // Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs - // to have its input and output types fixed (`&mut self` and returning - // `i16` coroutine kind). - Some(Instance { - def: ty::InstanceDef::ConstructCoroutineInClosureShim { - coroutine_closure_def_id, - target_kind, - }, - args, - }) - } - _ => Some(Instance::new(coroutine_closure_def_id, args)), + if target_kind == ClosureKind::FnOnce + && args.as_coroutine_closure().kind() != ClosureKind::FnOnce + { + // If we're computing `AsyncFnOnce` for a by-ref closure then + // construct a new body that has the right return types. + Some(Instance { + def: ty::InstanceDef::ConstructCoroutineInClosureShim { + coroutine_closure_def_id, + receiver_by_ref: false, + }, + args, + }) + } else { + Some(Instance::new(coroutine_closure_def_id, args)) } } ty::Closure(closure_def_id, args) => { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index e32179d56d142..902b76e8c1e26 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -8,8 +8,11 @@ use rustc_middle::ty::layout::{ IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, }; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{ + self, AdtDef, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, +}; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; +use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_target::abi::*; @@ -123,6 +126,43 @@ fn layout_of_uncached<'tcx>( debug_assert!(!ty.has_non_region_infer()); Ok(match *ty.kind() { + ty::Pat(ty, pat) => { + let layout = cx.layout_of(ty)?.layout; + let mut layout = LayoutS::clone(&layout.0); + match *pat { + ty::PatternKind::Range { start, end, include_end } => { + if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &mut layout.abi { + if let Some(start) = start { + scalar.valid_range_mut().start = start + .try_eval_bits(tcx, param_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + } + if let Some(end) = end { + let mut end = end + .try_eval_bits(tcx, param_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + if !include_end { + end = end.wrapping_sub(1); + } + scalar.valid_range_mut().end = end; + } + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(cx), + }; + + layout.largest_niche = Some(niche); + + tcx.mk_layout(layout) + } else { + bug!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") + } + } + } + } + // Basic scalars. ty::Bool => tcx.mk_layout(LayoutS::scalar( cx, @@ -156,7 +196,7 @@ fn layout_of_uncached<'tcx>( ty::Never => tcx.mk_layout(cx.layout_of_never_type()), // Potentially-wide pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA)); if !ty.is_unsafe_ptr() { data_ptr.valid_range_mut().start = 1; @@ -238,9 +278,9 @@ fn layout_of_uncached<'tcx>( // Arrays and slices. ty::Array(element, mut count) => { - if count.has_projections() { + if count.has_aliases() { count = tcx.normalize_erasing_regions(param_env, count); - if count.has_projections() { + if count.has_aliases() { return Err(error(cx, LayoutError::Unknown(ty))); } } @@ -376,7 +416,7 @@ fn layout_of_uncached<'tcx>( } // Type of the first ADT field: - let f0_ty = fields[FieldIdx::from_u32(0)].ty(tcx, args); + let f0_ty = fields[FieldIdx::ZERO].ty(tcx, args); // Heterogeneous SIMD vectors are not supported: // (should be caught by typeck) @@ -505,6 +545,40 @@ fn layout_of_uncached<'tcx>( )); } + let err_if_unsized = |field: &FieldDef, err_msg: &str| { + let field_ty = tcx.type_of(field.did); + let is_unsized = tcx + .try_instantiate_and_normalize_erasing_regions(args, cx.param_env, field_ty) + .map(|f| !f.is_sized(tcx, cx.param_env)) + .map_err(|e| { + error( + cx, + LayoutError::NormalizationFailure(field_ty.instantiate_identity(), e), + ) + })?; + + if is_unsized { + cx.tcx.dcx().span_delayed_bug(tcx.def_span(def.did()), err_msg.to_owned()); + Err(error(cx, LayoutError::Unknown(ty))) + } else { + Ok(()) + } + }; + + if def.is_struct() { + if let Some((_, fields_except_last)) = + def.non_enum_variant().fields.raw.split_last() + { + for f in fields_except_last { + err_if_unsized(f, "only the last field of a struct can be unsized")?; + } + } + } else { + for f in def.all_fields() { + err_if_unsized(f, &format!("{}s cannot have unsized fields", def.descr()))?; + } + } + let get_discriminant_type = |min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max); @@ -744,7 +818,7 @@ fn coroutine_layout<'tcx>( let tcx = cx.tcx; let instantiate_field = |ty: Ty<'tcx>| EarlyBinder::bind(ty).instantiate(tcx, args); - let Some(info) = tcx.coroutine_layout(def_id) else { + let Some(info) = tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) else { return Err(error(cx, LayoutError::Unknown(ty))); }; let (ineligible_locals, assignments) = coroutine_saved_local_eligibility(info); @@ -1007,6 +1081,7 @@ fn variant_info_for_adt<'tcx>( offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + type_name: None, } }) .collect(); @@ -1070,7 +1145,7 @@ fn variant_info_for_coroutine<'tcx>( return (vec![], None); }; - let coroutine = cx.tcx.optimized_mir(def_id).coroutine_layout().unwrap(); + let coroutine = cx.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap(); let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); let mut upvars_size = Size::ZERO; @@ -1090,6 +1165,7 @@ fn variant_info_for_coroutine<'tcx>( offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + type_name: None, } }) .collect(); @@ -1104,19 +1180,24 @@ fn variant_info_for_coroutine<'tcx>( .iter() .enumerate() .map(|(field_idx, local)| { + let field_name = coroutine.field_names[*local]; let field_layout = variant_layout.field(cx, field_idx); let offset = variant_layout.fields.offset(field_idx); // The struct is as large as the last field's end variant_size = variant_size.max(offset + field_layout.size); FieldInfo { kind: FieldKind::CoroutineLocal, - name: coroutine.field_names[*local].unwrap_or(Symbol::intern(&format!( + name: field_name.unwrap_or(Symbol::intern(&format!( ".coroutine_field{}", local.as_usize() ))), offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + // Include the type name if there is no field name, or if the name is the + // __awaitee placeholder symbol which means a child future being `.await`ed. + type_name: (field_name.is_none() || field_name == Some(sym::__awaitee)) + .then(|| Symbol::intern(&field_layout.ty.to_string())), } }) .chain(upvar_fields.iter().copied()) diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 48339e6120ad3..ee930a78e77c7 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -221,6 +221,7 @@ where | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) + | ty::Pat(..) | ty::FnPtr(..) | ty::Tuple(_) | ty::Bound(..) diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index fc16edc6d1309..d7d31a88c9b51 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -7,7 +7,6 @@ use rustc_middle::ty::util::{CheckRegions, NotUniqueParam}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; -use rustc_trait_selection::traits::check_args_compatible; use crate::errors::{DuplicateArg, NotParam}; @@ -131,7 +130,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { TaitInBodyFinder { collector: self }.visit_expr(body); } - fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) { + fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) { if !self.seen.insert(alias_ty.def_id.expect_local()) { return; } @@ -206,7 +205,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { #[instrument(skip(self), ret, level = "trace")] fn visit_ty(&mut self, t: Ty<'tcx>) { t.super_visit_with(self); - match t.kind() { + match *t.kind() { ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => { self.visit_opaque_ty(alias_ty); } @@ -250,7 +249,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { ty::GenericArgs::identity_for_item(self.tcx, parent), ); - if check_args_compatible(self.tcx, assoc, impl_args) { + if self.tcx.check_args_compatible(assoc.def_id, impl_args) { self.tcx .type_of(assoc.def_id) .instantiate(self.tcx, impl_args) @@ -280,7 +279,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { // assumption to the `param_env` of the default method. We also separately // rely on that assumption here. let ty = self.tcx.type_of(alias_ty.def_id).instantiate(self.tcx, alias_ty.args); - let ty::Alias(ty::Opaque, alias_ty) = ty.kind() else { bug!("{ty:?}") }; + let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!("{ty:?}") }; self.visit_opaque_ty(alias_ty); } } diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index bb546cee2dd80..a5ffa553c2a8a 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -12,7 +12,7 @@ pub(crate) fn provide(providers: &mut Providers) { macro_rules! rtry { ($e:expr) => { match $e { - e @ Representability::Infinite => return e, + e @ Representability::Infinite(_) => return e, Representability::Representable => {} } }; diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 63654a453ddee..19c092c5ddf3f 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -13,6 +13,7 @@ pub trait SpannedTypeVisitor<'tcx> { fn visit(&mut self, span: Span, value: impl TypeVisitable>) -> Self::Result; } +#[instrument(level = "trace", skip(tcx, visitor))] pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( tcx: TyCtxt<'tcx>, item: LocalDefId, @@ -36,7 +37,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( for (hir, ty) in hir_sig.inputs.iter().zip(ty_sig.inputs().iter()) { try_visit!(visitor.visit(hir.span, ty.map_bound(|x| *x))); } - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } @@ -54,7 +55,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( // Associated types in traits don't necessarily have a type that we can visit try_visit!(visitor.visit(ty.span, tcx.type_of(item).instantiate_identity())); } - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } @@ -76,7 +77,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( let ty = field.ty(tcx, args); try_visit!(visitor.visit(span, ty)); } - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } @@ -95,12 +96,12 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( _ => tcx.def_span(item), }; try_visit!(visitor.visit(span, tcx.type_of(item).instantiate_identity())); - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } DefKind::TraitAlias | DefKind::Trait => { - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { try_visit!(visitor.visit(span, pred)); } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 2b6b91672c3a5..fa1085c7cd79a 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -1,78 +1,58 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir::LangItem; use rustc_index::bit_set::BitSet; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitor}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitableExt, TypeVisitor}; use rustc_middle::ty::{ToPredicate, TypeSuperVisitable, TypeVisitable}; use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits; -fn sized_constraint_for_ty<'tcx>( - tcx: TyCtxt<'tcx>, - adtdef: ty::AdtDef<'tcx>, - ty: Ty<'tcx>, -) -> Vec> { +#[instrument(level = "debug", skip(tcx), ret)] +fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option> { use rustc_type_ir::TyKind::*; - let result = match ty.kind() { - Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) - | FnPtr(_) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..) | Never => { - vec![] - } - - Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | CoroutineWitness(..) => { - // these are never sized - return the target type - vec![ty] - } - - Tuple(tys) => match tys.last() { - None => vec![], - Some(&ty) => sized_constraint_for_ty(tcx, adtdef, ty), - }, - - Adt(adt, args) => { - // recursive case - let adt_tys = adt.sized_constraint(tcx); - debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys); - adt_tys - .iter_instantiated(tcx, args) - .flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty)) - .collect() - } - - Alias(..) => { - // must calculate explicitly. - // FIXME: consider special-casing always-Sized projections - vec![ty] - } - - Param(..) => { - // perf hack: if there is a `T: Sized` bound, then - // we know that `T` is Sized and do not need to check - // it on the impl. - - let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() else { return vec![ty] }; - let predicates = tcx.predicates_of(adtdef.did()).predicates; - if predicates.iter().any(|(p, _)| { - p.as_trait_clause().is_some_and(|trait_pred| { - trait_pred.def_id() == sized_trait_def_id - && trait_pred.self_ty().skip_binder() == ty - }) - }) { - vec![] - } else { - vec![ty] - } - } + match ty.kind() { + // these are always sized + Bool + | Char + | Int(..) + | Uint(..) + | Float(..) + | RawPtr(..) + | Ref(..) + | FnDef(..) + | FnPtr(..) + | Array(..) + | Closure(..) + | CoroutineClosure(..) + | Coroutine(..) + | CoroutineWitness(..) + | Never + | Dynamic(_, _, ty::DynStar) => None, + + // these are never sized + Str | Slice(..) | Dynamic(_, _, ty::Dyn) | Foreign(..) => Some(ty), + + Pat(ty, _) => sized_constraint_for_ty(tcx, *ty), + + Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)), + + // recursive case + Adt(adt, args) => adt.sized_constraint(tcx).and_then(|intermediate| { + let ty = intermediate.instantiate(tcx, args); + sized_constraint_for_ty(tcx, ty) + }), + + // these can be sized or unsized + Param(..) | Alias(..) | Error(_) => Some(ty), Placeholder(..) | Bound(..) | Infer(..) => { - bug!("unexpected type `{:?}` in sized_constraint_for_ty", ty) + bug!("unexpected type `{ty:?}` in sized_constraint_for_ty") } - }; - debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result); - result + } } fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { @@ -90,29 +70,45 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { /// /// In fact, there are only a few options for the types in the constraint: /// - an obviously-unsized type -/// - a type parameter or projection whose Sizedness can't be known -/// - a tuple of type parameters or projections, if there are multiple -/// such. -/// - an Error, if a type is infinitely sized +/// - a type parameter or projection whose sizedness can't be known +#[instrument(level = "debug", skip(tcx), ret)] fn adt_sized_constraint<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, -) -> ty::EarlyBinder<&'tcx ty::List>> { +) -> Option>> { if let Some(def_id) = def_id.as_local() { - if matches!(tcx.representability(def_id), ty::Representability::Infinite) { - return ty::EarlyBinder::bind(tcx.mk_type_list(&[Ty::new_misc_error(tcx)])); + if let ty::Representability::Infinite(_) = tcx.representability(def_id) { + return None; } } let def = tcx.adt_def(def_id); - let result = - tcx.mk_type_list_from_iter(def.variants().iter().filter_map(|v| v.tail_opt()).flat_map( - |f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did).instantiate_identity()), - )); + if !def.is_struct() { + bug!("`adt_sized_constraint` called on non-struct type: {def:?}"); + } + + let tail_def = def.non_enum_variant().tail_opt()?; + let tail_ty = tcx.type_of(tail_def.did).instantiate_identity(); - debug!("adt_sized_constraint: {:?} => {:?}", def, result); + let constraint_ty = sized_constraint_for_ty(tcx, tail_ty)?; + if let Err(guar) = constraint_ty.error_reported() { + return Some(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))); + } - ty::EarlyBinder::bind(result) + // perf hack: if there is a `constraint_ty: Sized` bound, then we know + // that the type is sized and do not need to check it on the impl. + let sized_trait_def_id = tcx.require_lang_item(LangItem::Sized, None); + let predicates = tcx.predicates_of(def.did()).predicates; + if predicates.iter().any(|(p, _)| { + p.as_trait_clause().is_some_and(|trait_pred| { + trait_pred.def_id() == sized_trait_def_id + && trait_pred.self_ty().skip_binder() == constraint_ty + }) + }) { + return None; + } + + Some(ty::EarlyBinder::bind(constraint_ty)) } /// See `ParamEnv` struct definition for details. @@ -247,37 +243,39 @@ fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamE tcx.param_env(def_id).with_reveal_all_normalized(tcx) } -/// If `def_id` is an issue 33140 hack impl, returns its self type; otherwise, returns `None`. +/// If the given trait impl enables exploiting the former order dependence of trait objects, +/// returns its self type; otherwise, returns `None`. /// -/// See [`ty::ImplOverlapKind::Issue33140`] for more details. -fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option>> { - debug!("issue33140_self_ty({:?})", def_id); - - let impl_ = tcx - .impl_trait_header(def_id) - .unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id)); +/// See [`ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects`] for more details. +#[instrument(level = "debug", skip(tcx))] +fn self_ty_of_trait_impl_enabling_order_dep_trait_object_hack( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Option>> { + let impl_ = + tcx.impl_trait_header(def_id).unwrap_or_else(|| bug!("called on inherent impl {def_id:?}")); let trait_ref = impl_.trait_ref.skip_binder(); - debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref); + debug!(?trait_ref); let is_marker_like = impl_.polarity == ty::ImplPolarity::Positive && tcx.associated_item_def_ids(trait_ref.def_id).is_empty(); // Check whether these impls would be ok for a marker trait. if !is_marker_like { - debug!("issue33140_self_ty - not marker-like!"); + debug!("not marker-like!"); return None; } // impl must be `impl Trait for dyn Marker1 + Marker2 + ...` if trait_ref.args.len() != 1 { - debug!("issue33140_self_ty - impl has args!"); + debug!("impl has args!"); return None; } let predicates = tcx.predicates_of(def_id); if predicates.parent.is_some() || !predicates.predicates.is_empty() { - debug!("issue33140_self_ty - impl has predicates {:?}!", predicates); + debug!(?predicates, "impl has predicates!"); return None; } @@ -288,10 +286,10 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option { pub value: V, pub max_universe: UniverseIndex, + // FIXME(lcnr, oli-obk): try moving this into the query inputs instead + pub defining_opaque_types: I::DefiningOpaqueTypes, pub variables: I::CanonicalVars, } @@ -44,8 +48,8 @@ impl Canonical { /// let b: Canonical)> = a.unchecked_map(|v| (v, ty)); /// ``` pub fn unchecked_map(self, map_op: impl FnOnce(V) -> W) -> Canonical { - let Canonical { max_universe, variables, value } = self; - Canonical { max_universe, variables, value: map_op(value) } + let Canonical { defining_opaque_types, max_universe, variables, value } = self; + Canonical { defining_opaque_types, max_universe, variables, value: map_op(value) } } /// Allows you to map the `value` of a canonical while keeping the same set of @@ -54,8 +58,8 @@ impl Canonical { /// **WARNING:** This function is very easy to mis-use, hence the name! See /// the comment of [Canonical::unchecked_map] for more details. pub fn unchecked_rebind(self, value: W) -> Canonical { - let Canonical { max_universe, variables, value: _ } = self; - Canonical { max_universe, variables, value } + let Canonical { defining_opaque_types, max_universe, variables, value: _ } = self; + Canonical { defining_opaque_types, max_universe, variables, value } } } @@ -63,28 +67,32 @@ impl Eq for Canonical {} impl PartialEq for Canonical { fn eq(&self, other: &Self) -> bool { - self.value == other.value - && self.max_universe == other.max_universe - && self.variables == other.variables + let Self { value, max_universe, variables, defining_opaque_types } = self; + *value == other.value + && *max_universe == other.max_universe + && *variables == other.variables + && *defining_opaque_types == other.defining_opaque_types } } impl fmt::Display for Canonical { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { value, max_universe, variables, defining_opaque_types } = self; write!( f, - "Canonical {{ value: {}, max_universe: {:?}, variables: {:?} }}", - self.value, self.max_universe, self.variables + "Canonical {{ value: {value}, max_universe: {max_universe:?}, variables: {variables:?}, defining_opaque_types: {defining_opaque_types:?} }}", ) } } impl fmt::Debug for Canonical { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { value, max_universe, variables, defining_opaque_types } = self; f.debug_struct("Canonical") - .field("value", &self.value) - .field("max_universe", &self.max_universe) - .field("variables", &self.variables) + .field("value", &value) + .field("max_universe", &max_universe) + .field("variables", &variables) + .field("defining_opaque_types", &defining_opaque_types) .finish() } } @@ -100,6 +108,7 @@ where value: self.value.try_fold_with(folder)?, max_universe: self.max_universe.try_fold_with(folder)?, variables: self.variables.try_fold_with(folder)?, + defining_opaque_types: self.defining_opaque_types, }) } } @@ -109,9 +118,11 @@ where I::CanonicalVars: TypeVisitable, { fn visit_with>(&self, folder: &mut F) -> F::Result { - try_visit!(self.value.visit_with(folder)); - try_visit!(self.max_universe.visit_with(folder)); - self.variables.visit_with(folder) + let Self { value, max_universe, variables, defining_opaque_types } = self; + try_visit!(value.visit_with(folder)); + try_visit!(max_universe.visit_with(folder)); + try_visit!(defining_opaque_types.visit_with(folder)); + variables.visit_with(folder) } } diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 0aaaad5af05d9..c1506f9252b08 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -1,5 +1,7 @@ #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +#[cfg(feature = "nightly")] +use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use std::fmt; use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; @@ -8,15 +10,7 @@ use self::ConstKind::*; /// Represents a constant in Rust. #[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - PartialOrd(bound = ""), - PartialOrd = "feature_allow_slow_enum", - Ord(bound = ""), - Ord = "feature_allow_slow_enum", - Hash(bound = "") -)] +#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""))] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum ConstKind { /// A const generic parameter. diff --git a/compiler/rustc_type_ir/src/debug.rs b/compiler/rustc_type_ir/src/debug.rs index 9c8e45b4338c6..9298360f749ac 100644 --- a/compiler/rustc_type_ir/src/debug.rs +++ b/compiler/rustc_type_ir/src/debug.rs @@ -43,6 +43,10 @@ impl InferCtxtLike for NoInfcx { fn probe_ct_var(&self, _vid: ConstVid) -> Option { None } + + fn defining_opaque_types(&self) -> ::DefiningOpaqueTypes { + Default::default() + } } pub trait DebugWithInfcx: fmt::Debug { diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index b38ef2ad84d48..6a1ac642b70f7 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -1,4 +1,4 @@ -bitflags! { +bitflags::bitflags! { /// Flags that we track on types. These flags are propagated upwards /// through the type during type construction, so that we can quickly check /// whether the type has various kinds of types in it without recursing @@ -78,14 +78,16 @@ bitflags! { /// Does this have `ConstKind::Unevaluated`? const HAS_CT_PROJECTION = 1 << 14; - /// Could this type be normalized further? - const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits() + /// Does this have `Alias` or `ConstKind::Unevaluated`? + /// + /// Rephrased, could this term be normalized further? + const HAS_ALIASES = TypeFlags::HAS_TY_PROJECTION.bits() | TypeFlags::HAS_TY_WEAK.bits() | TypeFlags::HAS_TY_OPAQUE.bits() | TypeFlags::HAS_TY_INHERENT.bits() | TypeFlags::HAS_CT_PROJECTION.bits(); - /// Is an error type/const reachable? + /// Is an error type/lifetime/const reachable? const HAS_ERROR = 1 << 15; /// Does this have any region that "appears free" in the type? diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 8d402588398e4..01bb3d73dbdf3 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -136,31 +136,21 @@ pub trait TypeFolder: FallibleTypeFolder { t.super_fold_with(self) } - fn fold_ty(&mut self, t: I::Ty) -> I::Ty - where - I::Ty: TypeSuperFoldable, - { + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { t.super_fold_with(self) } // The default region folder is a no-op because `Region` is non-recursive - // and has no `super_fold_with` method to call. That also explains the - // lack of `I::Region: TypeSuperFoldable` bound on this method. + // and has no `super_fold_with` method to call. fn fold_region(&mut self, r: I::Region) -> I::Region { r } - fn fold_const(&mut self, c: I::Const) -> I::Const - where - I::Const: TypeSuperFoldable, - { + fn fold_const(&mut self, c: I::Const) -> I::Const { c.super_fold_with(self) } - fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate - where - I::Predicate: TypeSuperFoldable, - { + fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { p.super_fold_with(self) } } @@ -185,31 +175,21 @@ pub trait FallibleTypeFolder: Sized { t.try_super_fold_with(self) } - fn try_fold_ty(&mut self, t: I::Ty) -> Result - where - I::Ty: TypeSuperFoldable, - { + fn try_fold_ty(&mut self, t: I::Ty) -> Result { t.try_super_fold_with(self) } // The default region folder is a no-op because `Region` is non-recursive - // and has no `super_fold_with` method to call. That also explains the - // lack of `I::Region: TypeSuperFoldable` bound on this method. + // and has no `super_fold_with` method to call. fn try_fold_region(&mut self, r: I::Region) -> Result { Ok(r) } - fn try_fold_const(&mut self, c: I::Const) -> Result - where - I::Const: TypeSuperFoldable, - { + fn try_fold_const(&mut self, c: I::Const) -> Result { c.try_super_fold_with(self) } - fn try_fold_predicate(&mut self, p: I::Predicate) -> Result - where - I::Predicate: TypeSuperFoldable, - { + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result { p.try_super_fold_with(self) } } @@ -234,10 +214,7 @@ where Ok(self.fold_binder(t)) } - fn try_fold_ty(&mut self, t: I::Ty) -> Result - where - I::Ty: TypeSuperFoldable, - { + fn try_fold_ty(&mut self, t: I::Ty) -> Result { Ok(self.fold_ty(t)) } @@ -245,17 +222,11 @@ where Ok(self.fold_region(r)) } - fn try_fold_const(&mut self, c: I::Const) -> Result - where - I::Const: TypeSuperFoldable, - { + fn try_fold_const(&mut self, c: I::Const) -> Result { Ok(self.fold_const(c)) } - fn try_fold_predicate(&mut self, p: I::Predicate) -> Result - where - I::Predicate: TypeSuperFoldable, - { + fn try_fold_predicate(&mut self, p: I::Predicate) -> Result { Ok(self.fold_predicate(p)) } } diff --git a/compiler/rustc_type_ir/src/infcx.rs b/compiler/rustc_type_ir/src/infcx.rs index 28b71f0ea13f8..a53287c19878b 100644 --- a/compiler/rustc_type_ir/src/infcx.rs +++ b/compiler/rustc_type_ir/src/infcx.rs @@ -37,4 +37,6 @@ pub trait InferCtxtLike { /// Resolve `ConstVid` to its inferred type, if it has been equated with a non-infer type. fn probe_ct_var(&self, vid: ConstVid) -> Option<::Const>; + + fn defining_opaque_types(&self) -> ::DefiningOpaqueTypes; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 373540de05e0e..70f7e58be1937 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -2,23 +2,25 @@ use smallvec::SmallVec; use std::fmt::Debug; use std::hash::Hash; +use crate::fold::TypeSuperFoldable; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{ new, BoundVar, BoundVars, CanonicalVarInfo, ConstKind, DebugWithInfcx, RegionKind, TyKind, UniverseIndex, }; -pub trait Interner: Sized { - type DefId: Copy + Debug + Hash + Ord; - type AdtDef: Copy + Debug + Hash + Ord; +pub trait Interner: Sized + Copy { + type DefId: Copy + Debug + Hash + Eq; + type DefiningOpaqueTypes: Copy + Debug + Hash + Default + Eq + TypeVisitable; + type AdtDef: Copy + Debug + Hash + Eq; type GenericArgs: Copy + DebugWithInfcx + Hash - + Ord + + Eq + IntoIterator; - type GenericArg: Copy + DebugWithInfcx + Hash + Ord; - type Term: Copy + Debug + Hash + Ord; + type GenericArg: Copy + DebugWithInfcx + Hash + Eq; + type Term: Copy + Debug + Hash + Eq; type Binder>: BoundVars + TypeSuperVisitable; type BoundVars: IntoIterator; @@ -30,59 +32,68 @@ pub trait Interner: Sized { type Ty: Copy + DebugWithInfcx + Hash - + Ord + + Eq + Into + IntoKind> + TypeSuperVisitable + + TypeSuperFoldable + Flags + new::Ty; - type Tys: Copy + Debug + Hash + Ord + IntoIterator; - type AliasTy: Copy + DebugWithInfcx + Hash + Ord; - type ParamTy: Copy + Debug + Hash + Ord; - type BoundTy: Copy + Debug + Hash + Ord; - type PlaceholderTy: Copy + Debug + Hash + Ord + PlaceholderLike; + type Tys: Copy + Debug + Hash + Eq + IntoIterator; + type AliasTy: Copy + DebugWithInfcx + Hash + Eq; + type ParamTy: Copy + Debug + Hash + Eq; + type BoundTy: Copy + Debug + Hash + Eq; + type PlaceholderTy: Copy + Debug + Hash + Eq + PlaceholderLike; // Things stored inside of tys - type ErrorGuaranteed: Copy + Debug + Hash + Ord; - type BoundExistentialPredicates: Copy + DebugWithInfcx + Hash + Ord; - type PolyFnSig: Copy + DebugWithInfcx + Hash + Ord; - type AllocId: Copy + Debug + Hash + Ord; + type ErrorGuaranteed: Copy + Debug + Hash + Eq; + type BoundExistentialPredicates: Copy + DebugWithInfcx + Hash + Eq; + type PolyFnSig: Copy + DebugWithInfcx + Hash + Eq; + type AllocId: Copy + Debug + Hash + Eq; + type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx; // Kinds of consts type Const: Copy + DebugWithInfcx + Hash - + Ord + + Eq + Into + IntoKind> + ConstTy + TypeSuperVisitable + + TypeSuperFoldable + Flags + new::Const; - type AliasConst: Copy + DebugWithInfcx + Hash + Ord; - type PlaceholderConst: Copy + Debug + Hash + Ord + PlaceholderLike; - type ParamConst: Copy + Debug + Hash + Ord; - type BoundConst: Copy + Debug + Hash + Ord; - type ValueConst: Copy + Debug + Hash + Ord; - type ExprConst: Copy + DebugWithInfcx + Hash + Ord; + type AliasConst: Copy + DebugWithInfcx + Hash + Eq; + type PlaceholderConst: Copy + Debug + Hash + Eq + PlaceholderLike; + type ParamConst: Copy + Debug + Hash + Eq; + type BoundConst: Copy + Debug + Hash + Eq; + type ValueConst: Copy + Debug + Hash + Eq; + type ExprConst: Copy + DebugWithInfcx + Hash + Eq; // Kinds of regions type Region: Copy + DebugWithInfcx + Hash - + Ord + + Eq + Into + IntoKind> + Flags + new::Region; - type EarlyParamRegion: Copy + Debug + Hash + Ord; - type LateParamRegion: Copy + Debug + Hash + Ord; - type BoundRegion: Copy + Debug + Hash + Ord; - type InferRegion: Copy + DebugWithInfcx + Hash + Ord; - type PlaceholderRegion: Copy + Debug + Hash + Ord + PlaceholderLike; + type EarlyParamRegion: Copy + Debug + Hash + Eq; + type LateParamRegion: Copy + Debug + Hash + Eq; + type BoundRegion: Copy + Debug + Hash + Eq; + type InferRegion: Copy + DebugWithInfcx + Hash + Eq; + type PlaceholderRegion: Copy + Debug + Hash + Eq + PlaceholderLike; // Predicates - type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; + type Predicate: Copy + + Debug + + Hash + + Eq + + TypeSuperVisitable + + TypeSuperFoldable + + Flags; type TraitPredicate: Copy + Debug + Hash + Eq; type RegionOutlivesPredicate: Copy + Debug + Hash + Eq; type TypeOutlivesPredicate: Copy + Debug + Hash + Eq; @@ -91,6 +102,7 @@ pub trait Interner: Sized { type SubtypePredicate: Copy + Debug + Hash + Eq; type CoercePredicate: Copy + Debug + Hash + Eq; type ClosureKind: Copy + Debug + Hash + Eq; + type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo]) -> Self::CanonicalVars; } diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index c01baa58ae784..ef7437c2542cf 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -8,14 +8,10 @@ #[cfg(feature = "nightly")] extern crate self as rustc_type_ir; -#[macro_use] -extern crate bitflags; -#[cfg(feature = "nightly")] -#[macro_use] -extern crate rustc_macros; - #[cfg(feature = "nightly")] use rustc_data_structures::sync::Lrc; +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; use std::fmt; use std::hash::Hash; #[cfg(not(feature = "nightly"))] @@ -314,7 +310,7 @@ rustc_index::newtype_index! { } impl UniverseIndex { - pub const ROOT: UniverseIndex = UniverseIndex::from_u32(0); + pub const ROOT: UniverseIndex = UniverseIndex::ZERO; /// Returns the "next" universe index in order -- this new index /// is considered to extend all previous universes. This @@ -346,6 +342,11 @@ impl UniverseIndex { pub fn cannot_name(self, other: UniverseIndex) -> bool { self < other } + + /// Returns `true` if `self` is the root universe, otherwise false. + pub fn is_root(self) -> bool { + self == Self::ROOT + } } impl Default for UniverseIndex { diff --git a/compiler/rustc_type_ir/src/new.rs b/compiler/rustc_type_ir/src/new.rs index e7e695e59085a..1572a641d06f0 100644 --- a/compiler/rustc_type_ir/src/new.rs +++ b/compiler/rustc_type_ir/src/new.rs @@ -6,6 +6,8 @@ pub trait Ty> { pub trait Region> { fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; + + fn new_static(interner: I) -> Self; } pub trait Const> { diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index a0759f7df7fdd..5260d9061cfc8 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -1,5 +1,7 @@ use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; use std::fmt; use crate::fold::{FallibleTypeFolder, TypeFoldable}; @@ -148,19 +150,20 @@ pub enum PredicateKind { /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous. Ambiguous, - /// The alias normalizes to `term`. Unlike `Projection`, this always fails if the alias - /// cannot be normalized in the current context. + /// This should only be used inside of the new solver for `AliasRelate` and expects + /// the `term` to be an unconstrained inference variable. /// - /// `Projection(::Assoc, ?x)` results in `?x == ::Assoc` while - /// `NormalizesTo(::Assoc, ?x)` results in `NoSolution`. - /// - /// Only used in the new solver. + /// The alias normalizes to `term`. Unlike `Projection`, this always fails if the + /// alias cannot be normalized in the current context. For the rigid alias + /// `T as Trait>::Assoc`, `Projection(::Assoc, ?x)` constrains `?x` + /// to `::Assoc` while `NormalizesTo(::Assoc, ?x)` + /// results in `NoSolution`. NormalizesTo(I::NormalizesTo), /// Separate from `ClauseKind::Projection` which is used for normalization in new solver. /// This predicate requires two terms to be equal to eachother. /// - /// Only used for new solver + /// Only used for new solver. AliasRelate(I::Term, I::Term, AliasRelationDirection), } diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index cf13f066cbf8f..d1b86b495e9d8 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -1,5 +1,7 @@ #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +#[cfg(feature = "nightly")] +use rustc_macros::{TyDecodable, TyEncodable}; use std::fmt; use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; @@ -113,15 +115,7 @@ use self::RegionKind::*; /// [2]: https://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html #[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - PartialOrd(bound = ""), - PartialOrd = "feature_allow_slow_enum", - Ord(bound = ""), - Ord = "feature_allow_slow_enum", - Hash(bound = "") -)] +#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""))] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable))] pub enum RegionKind { /// A region parameter; for example `'a` in `impl<'a> Trait for &'a ()`. @@ -223,23 +217,27 @@ impl DebugWithInfcx for RegionKind { f: &mut core::fmt::Formatter<'_>, ) -> core::fmt::Result { match this.data { - ReEarlyParam(data) => write!(f, "ReEarlyParam({data:?})"), + ReEarlyParam(data) => write!(f, "{data:?}"), ReBound(binder_id, bound_region) => { - write!(f, "ReBound({binder_id:?}, {bound_region:?})") + write!(f, "'")?; + crate::debug_bound_var(f, *binder_id, bound_region) } ReLateParam(fr) => write!(f, "{fr:?}"), - ReStatic => f.write_str("ReStatic"), + ReStatic => f.write_str("'static"), ReVar(vid) => write!(f, "{:?}", &this.wrap(vid)), - RePlaceholder(placeholder) => write!(f, "RePlaceholder({placeholder:?})"), + RePlaceholder(placeholder) => write!(f, "{placeholder:?}"), - ReErased => f.write_str("ReErased"), + // Use `'{erased}` as the output instead of `'erased` so that its more obviously distinct from + // a `ReEarlyParam` named `'erased`. Technically that would print as `'erased/#IDX` so this is + // not strictly necessary but *shrug* + ReErased => f.write_str("'{erased}"), - ReError(_) => f.write_str("ReError"), + ReError(_) => f.write_str("'{region error}"), } } } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index de8f56618d000..f2e4afecc4025 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -3,6 +3,8 @@ use rustc_ast_ir::try_visit; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] use rustc_data_structures::unify::{EqUnifyValue, UnifyKey}; +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEncodable}; use std::fmt; use crate::fold::{FallibleTypeFolder, TypeFoldable}; @@ -60,18 +62,10 @@ impl AliasKind { /// Defines the kinds of types used by the type system. /// /// Types written by the user start out as `hir::TyKind` and get -/// converted to this representation using `AstConv::ast_ty_to_ty`. +/// converted to this representation using `::lower_ty`. #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "IrTyKind")] #[derive(derivative::Derivative)] -#[derivative( - Clone(bound = ""), - Copy(bound = ""), - PartialOrd(bound = ""), - PartialOrd = "feature_allow_slow_enum", - Ord(bound = ""), - Ord = "feature_allow_slow_enum", - Hash(bound = "") -)] +#[derivative(Clone(bound = ""), Copy(bound = ""), Hash(bound = ""))] #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] pub enum TyKind { /// The primitive boolean type. Written as `bool`. @@ -108,11 +102,18 @@ pub enum TyKind { /// An array with the given length. Written as `[T; N]`. Array(I::Ty, I::Const), + /// A pattern newtype. Takes any type and restricts its valid values to its pattern. + /// This will also change the layout to take advantage of this restriction. + /// Only `Copy` and `Clone` will automatically get implemented for pattern types. + /// Auto-traits treat this as if it were an aggregate with a single nested type. + /// Only supports integer range patterns for now. + Pat(I::Ty, I::Pat), + /// The pointee of an array slice. Written as `[T]`. Slice(I::Ty), /// A raw pointer. Written as `*mut T` or `*const T` - RawPtr(TypeAndMut), + RawPtr(I::Ty, Mutability), /// A reference; a pointer with an associated lifetime. Written as /// `&'a mut T` or `&'a T`. @@ -182,9 +183,9 @@ pub enum TyKind { /// Looking at the following example, the witness for this coroutine /// may end up as something like `for<'a> [Vec, &'a Vec]`: /// - /// ```ignore UNSOLVED (ask @compiler-errors, should this error? can we just swap the yields?) + /// ``` /// #![feature(coroutines)] - /// |a| { + /// #[coroutine] static |a| { /// let x = &vec![3]; /// yield a; /// yield x[0]; @@ -228,7 +229,7 @@ pub enum TyKind { /// A placeholder type, used during higher ranked subtyping to instantiate /// bound variables. /// - /// It is conventional to render anonymous placeholer types like `!N` or `!U_N`, + /// It is conventional to render anonymous placeholder types like `!N` or `!U_N`, /// where `N` is the placeholder variable's anonymous index (which corresponds /// to the bound variable's index from the binder from which it was instantiated), /// and `U` is the universe index in which it is instantiated, or totally omitted @@ -270,7 +271,7 @@ const fn tykind_discriminant(value: &TyKind) -> usize { Str => 7, Array(_, _) => 8, Slice(_) => 9, - RawPtr(_) => 10, + RawPtr(_, _) => 10, Ref(_, _, _) => 11, FnDef(_, _) => 12, FnPtr(_) => 13, @@ -281,12 +282,13 @@ const fn tykind_discriminant(value: &TyKind) -> usize { CoroutineWitness(_, _) => 18, Never => 19, Tuple(_) => 20, - Alias(_, _) => 21, - Param(_) => 22, - Bound(_, _) => 23, - Placeholder(_) => 24, - Infer(_) => 25, - Error(_) => 26, + Pat(_, _) => 21, + Alias(_, _) => 22, + Param(_) => 23, + Bound(_, _) => 24, + Placeholder(_) => 25, + Infer(_) => 26, + Error(_) => 27, } } @@ -307,8 +309,9 @@ impl PartialEq for TyKind { (Adt(a_d, a_s), Adt(b_d, b_s)) => a_d == b_d && a_s == b_s, (Foreign(a_d), Foreign(b_d)) => a_d == b_d, (Array(a_t, a_c), Array(b_t, b_c)) => a_t == b_t && a_c == b_c, + (Pat(a_t, a_c), Pat(b_t, b_c)) => a_t == b_t && a_c == b_c, (Slice(a_t), Slice(b_t)) => a_t == b_t, - (RawPtr(a_t), RawPtr(b_t)) => a_t == b_t, + (RawPtr(a_t, a_m), RawPtr(b_t, b_m)) => a_t == b_t && a_m == b_m, (Ref(a_r, a_t, a_m), Ref(b_r, b_t, b_m)) => a_r == b_r && a_t == b_t && a_m == b_m, (FnDef(a_d, a_s), FnDef(b_d, b_s)) => a_d == b_d && a_s == b_s, (FnPtr(a_s), FnPtr(b_s)) => a_s == b_s, @@ -330,7 +333,7 @@ impl PartialEq for TyKind { _ => { debug_assert!( tykind_discriminant(self) != tykind_discriminant(other), - "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}" + "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}" ); false } @@ -370,18 +373,10 @@ impl DebugWithInfcx for TyKind { Foreign(d) => f.debug_tuple("Foreign").field(d).finish(), Str => write!(f, "str"), 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(TypeAndMut { 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 { @@ -803,8 +798,6 @@ impl DebugWithInfcx for InferTy { #[derivative( Clone(bound = ""), Copy(bound = ""), - PartialOrd(bound = ""), - Ord(bound = ""), PartialEq(bound = ""), Eq(bound = ""), Hash(bound = ""), diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 839e75dba4cbf..2f588c67f5698 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -110,6 +110,10 @@ pub trait TypeVisitor: Sized { fn visit_predicate(&mut self, p: I::Predicate) -> Self::Result { p.super_visit_with(self) } + + fn visit_clauses(&mut self, p: I::Clauses) -> Self::Result { + p.super_visit_with(self) + } } /////////////////////////////////////////////////////////////////////////// @@ -223,8 +227,8 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_vars_bound_at_or_above(ty::INNERMOST) } - fn has_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_PROJECTION) + fn has_aliases(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_ALIASES) } fn has_inherent_projections(&self) -> bool { @@ -423,6 +427,16 @@ impl TypeVisitor for HasTypeFlagsVisitor { ControlFlow::Continue(()) } } + + #[inline] + fn visit_clauses(&mut self, clauses: I::Clauses) -> Self::Result { + // Note: no `super_visit_with` call. + if clauses.flags().intersects(self.flags) { + ControlFlow::Break(FoundFlags) + } else { + ControlFlow::Continue(()) + } + } } #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -515,6 +529,15 @@ impl TypeVisitor for HasEscapingVarsVisitor { ControlFlow::Continue(()) } } + + #[inline] + fn visit_clauses(&mut self, clauses: I::Clauses) -> Self::Result { + if clauses.outer_exclusive_binder() > self.outer_index { + ControlFlow::Break(FoundEscapingVars) + } else { + ControlFlow::Continue(()) + } + } } struct HasErrorVisitor; diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index f53dbcfbd966e..231d9ba49a39c 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -8,7 +8,7 @@ use std::cell::Cell; use crate::abi::{FnAbi, Layout, LayoutShape}; use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; -use crate::mir::Body; +use crate::mir::{BinOp, Body, Place}; use crate::target::MachineInfo; use crate::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -126,11 +126,14 @@ pub trait Context { fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty; /// Returns literal value of a const as a string. - fn const_literal(&self, cnst: &Const) -> String; + fn const_pretty(&self, cnst: &Const) -> String; /// `Span` of an item fn span_of_an_item(&self, def_id: DefId) -> Span; + /// Obtain the representation of a type. + fn ty_pretty(&self, ty: Ty) -> String; + /// Obtain the representation of a type. fn ty_kind(&self, ty: Ty) -> TyKind; @@ -155,6 +158,9 @@ pub trait Context { /// Check if this is an empty DropGlue shim. fn is_empty_drop_shim(&self, def: InstanceDef) -> bool; + /// Check if this is an empty AsyncDropGlueCtor shim. + fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool; + /// Convert a non-generic crate item into an instance. /// This function will panic if the item is generic. fn mono_instance(&self, def_id: DefId) -> Instance; @@ -205,11 +211,17 @@ pub trait Context { /// Get the layout shape. fn layout_shape(&self, id: Layout) -> LayoutShape; + + /// Get a debug string representation of a place. + fn place_pretty(&self, place: &Place) -> String; + + /// Get the resulting type of binary operation. + fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt // datastructures and stable MIR datastructures -scoped_thread_local!(static TLV: Cell<*const ()>); +scoped_tls::scoped_thread_local!(static TLV: Cell<*const ()>); pub fn run(context: &dyn Context, f: F) -> Result where diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index d849c834ae00e..d9f988935ab19 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -16,8 +16,6 @@ //! //! The goal is to eventually be published on //! [crates.io](https://crates.io). -#[macro_use] -extern crate scoped_tls; use std::fmt; use std::fmt::Debug; @@ -27,7 +25,6 @@ use crate::compiler_interface::with; pub use crate::crate_def::CrateDef; pub use crate::crate_def::DefId; pub use crate::error::*; -use crate::mir::pretty::function_name; use crate::mir::Body; use crate::mir::Mutability; use crate::ty::{ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty}; @@ -148,9 +145,8 @@ impl CrateItem { with(|cx| cx.is_foreign_item(self.0)) } - pub fn dump(&self, w: &mut W) -> io::Result<()> { - writeln!(w, "{}", function_name(*self))?; - self.body().dump(w) + pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { + self.body().dump(w, &self.name()) } } diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs index c780042ff261c..408e0bafa5896 100644 --- a/compiler/stable_mir/src/mir/alloc.rs +++ b/compiler/stable_mir/src/mir/alloc.rs @@ -55,13 +55,13 @@ impl IndexedVal for AllocId { /// Utility function used to read an allocation data into a unassigned integer. pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result { let mut buf = [0u8; std::mem::size_of::()]; - match MachineInfo::target_endianess() { + match MachineInfo::target_endianness() { Endian::Little => { - bytes.read(&mut buf)?; + bytes.read_exact(&mut buf[..bytes.len()])?; Ok(u128::from_le_bytes(buf)) } Endian::Big => { - bytes.read(&mut buf[16 - bytes.len()..])?; + bytes.read_exact(&mut buf[16 - bytes.len()..])?; Ok(u128::from_be_bytes(buf)) } } @@ -70,13 +70,13 @@ pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result { /// Utility function used to read an allocation data into an assigned integer. pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result { let mut buf = [0u8; std::mem::size_of::()]; - match MachineInfo::target_endianess() { + match MachineInfo::target_endianness() { Endian::Little => { - bytes.read(&mut buf)?; + bytes.read_exact(&mut buf[..bytes.len()])?; Ok(i128::from_le_bytes(buf)) } Endian::Big => { - bytes.read(&mut buf[16 - bytes.len()..])?; + bytes.read_exact(&mut buf[16 - bytes.len()..])?; Ok(i128::from_be_bytes(buf)) } } diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index ae8e71bb950a1..e077c58031806 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,10 +1,12 @@ -use crate::mir::pretty::{function_body, pretty_statement, pretty_terminator}; +use crate::compiler_interface::with; +use crate::mir::pretty::function_body; use crate::ty::{ AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind, VariantIdx, }; use crate::{Error, Opaque, Span, Symbol}; use std::io; + /// The SMIR representation of a single function. #[derive(Clone, Debug)] pub struct Body { @@ -90,28 +92,9 @@ impl Body { self.locals.iter().enumerate() } - pub fn dump(&self, w: &mut W) -> io::Result<()> { - writeln!(w, "{}", function_body(self))?; - self.blocks - .iter() - .enumerate() - .map(|(index, block)| -> io::Result<()> { - writeln!(w, " bb{}: {{", index)?; - let _ = block - .statements - .iter() - .map(|statement| -> io::Result<()> { - writeln!(w, "{}", pretty_statement(&statement.kind))?; - Ok(()) - }) - .collect::>(); - pretty_terminator(&block.terminator.kind, w)?; - writeln!(w, "").unwrap(); - writeln!(w, " }}").unwrap(); - Ok(()) - }) - .collect::, _>>()?; - Ok(()) + /// Emit the body using the provided name for the signature. + pub fn dump(&self, w: &mut W, fn_name: &str) -> io::Result<()> { + function_body(w, self, fn_name) } pub fn spread_arg(&self) -> Option { @@ -347,6 +330,7 @@ pub enum BinOp { Ne, Ge, Gt, + Cmp, Offset, } @@ -354,39 +338,7 @@ impl BinOp { /// Return the type of this operation for the given input Ty. /// This function does not perform type checking, and it currently doesn't handle SIMD. pub fn ty(&self, lhs_ty: Ty, rhs_ty: Ty) -> Ty { - match self { - BinOp::Add - | BinOp::AddUnchecked - | BinOp::Sub - | BinOp::SubUnchecked - | BinOp::Mul - | BinOp::MulUnchecked - | BinOp::Div - | BinOp::Rem - | BinOp::BitXor - | BinOp::BitAnd - | BinOp::BitOr => { - assert_eq!(lhs_ty, rhs_ty); - assert!(lhs_ty.kind().is_primitive()); - lhs_ty - } - BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => { - assert!(lhs_ty.kind().is_primitive()); - assert!(rhs_ty.kind().is_primitive()); - lhs_ty - } - BinOp::Offset => { - assert!(lhs_ty.kind().is_raw_ptr()); - assert!(rhs_ty.kind().is_integral()); - lhs_ty - } - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { - assert_eq!(lhs_ty, rhs_ty); - let lhs_kind = lhs_ty.kind(); - assert!(lhs_kind.is_primitive() || lhs_kind.is_raw_ptr() || lhs_kind.is_fn_ptr()); - Ty::bool_ty() - } - } + with(|ctx| ctx.binop_ty(*self, lhs_ty, rhs_ty)) } } @@ -639,7 +591,7 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Ok(Ty::usize_ty()) } - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()), + Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), Rvalue::Aggregate(ak, ops) => match *ak { AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), AggregateKind::Tuple => Ok(Ty::new_tuple( @@ -650,6 +602,7 @@ impl Rvalue { AggregateKind::Coroutine(def, ref args, mov) => { Ok(Ty::new_coroutine(def, args.clone(), mov)) } + AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), }, Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), Rvalue::CopyForDeref(place) => place.ty(locals), @@ -665,6 +618,7 @@ pub enum AggregateKind { Closure(ClosureDef, GenericArgs), // FIXME(stable_mir): Movability here is redundant Coroutine(CoroutineDef, GenericArgs, Movability), + RawPtr(Ty, Mutability), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -674,7 +628,7 @@ pub enum Operand { Constant(Constant), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq)] pub struct Place { pub local: Local, /// projection out of a place (access a field, deref a pointer, etc) @@ -913,11 +867,9 @@ pub enum BorrowKind { /// Data must be immutable and is aliasable. Shared, - /// The immediately borrowed place must be immutable, but projections from - /// it don't need to be. This is used to prevent match guards from replacing - /// the scrutinee. For example, a fake borrow of `a.b` doesn't - /// conflict with a mutable borrow of `a.b.c`. - Fake, + /// An immutable, aliasable borrow that is discarded after borrow-checking. Can behave either + /// like a normal shared borrow or like a special shallow borrow (see [`FakeBorrowKind`]). + Fake(FakeBorrowKind), /// Data is mutable and not aliasable. Mut { @@ -932,7 +884,7 @@ impl BorrowKind { BorrowKind::Mut { .. } => Mutability::Mut, BorrowKind::Shared => Mutability::Not, // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. - BorrowKind::Fake => Mutability::Not, + BorrowKind::Fake(_) => Mutability::Not, } } } @@ -944,6 +896,17 @@ pub enum MutBorrowKind { ClosureCapture, } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum FakeBorrowKind { + /// A shared (deep) borrow. Data must be immutable and is aliasable. + Deep, + /// The immediately borrowed place must be immutable, but projections from + /// it don't need to be. This is used to prevent match guards from replacing + /// the scrutinee. For example, a fake borrow of `a.b` doesn't + /// conflict with a mutable borrow of `a.b.c`. + Shallow, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum Mutability { Not, @@ -985,8 +948,9 @@ pub enum PointerCoercion { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum CastKind { + // FIXME(smir-rename): rename this to PointerExposeProvenance PointerExposeAddress, - PointerFromExposedAddress, + PointerWithExposedProvenance, PointerCoercion(PointerCoercion), DynStar, IntToInt, @@ -1006,14 +970,8 @@ pub enum NullOp { AlignOf, /// Returns the offset of a field. OffsetOf(Vec<(VariantIdx, FieldIdx)>), - /// cfg!(debug_assertions), but at codegen time - UbCheck(UbKind), -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum UbKind { - LanguageUb, - LibraryUb, + /// cfg!(ub_checks), but at codegen time + UbChecks, } impl Operand { diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 38e5776c48cea..394038926f61a 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -4,6 +4,7 @@ use crate::mir::Body; use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; use crate::{with, CrateItem, DefId, Error, ItemKind, Opaque, Symbol}; use std::fmt::{Debug, Formatter}; +use std::io; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum MonoItem { @@ -40,13 +41,22 @@ impl Instance { with(|cx| cx.instance_args(self.def)) } - /// Get the body of an Instance. The body will be eagerly monomorphized. + /// Get the body of an Instance. + /// + /// The body will be eagerly monomorphized and all constants will already be evaluated. + /// + /// This method will return the intrinsic fallback body if one was defined. pub fn body(&self) -> Option { with(|context| context.instance_body(self.def)) } /// Check whether this instance has a body available. /// + /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide + /// whether to specialize the intrinsic or to use its fallback body. + /// + /// For more information on fallback body, see . + /// /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build /// the StableMIR body. pub fn has_body(&self) -> bool { @@ -147,7 +157,10 @@ impl Instance { /// When generating code for a Drop terminator, users can ignore an empty drop glue. /// These shims are only needed to generate a valid Drop call done via VTable. pub fn is_empty_shim(&self) -> bool { - self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def)) + self.kind == InstanceKind::Shim + && with(|cx| { + cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def) + }) } /// Try to constant evaluate the instance into a constant with the given type. @@ -157,6 +170,11 @@ impl Instance { pub fn try_const_eval(&self, const_ty: Ty) -> Result { with(|cx| cx.eval_instance(self.def, const_ty)) } + + /// Emit the body of this instance if it has one. + pub fn emit_mir(&self, w: &mut W) -> io::Result<()> { + if let Some(body) = self.body() { body.dump(w, &self.name()) } else { Ok(()) } + } } impl Debug for Instance { diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs index 8b7b488d312cf..bbca3965852a6 100644 --- a/compiler/stable_mir/src/mir/pretty.rs +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -1,185 +1,193 @@ -use crate::crate_def::CrateDef; -use crate::mir::{Operand, Rvalue, StatementKind, UnwindAction}; -use crate::ty::{DynKind, FloatTy, IntTy, RigidTy, TyKind, UintTy}; -use crate::{with, Body, CrateItem, Mutability}; +use crate::mir::{Operand, Place, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents}; +use crate::ty::{Const, IndexedVal, Ty}; +use crate::{with, Body, Mutability}; +use fmt::{Display, Formatter}; +use std::fmt::Debug; use std::io::Write; -use std::{io, iter}; +use std::{fmt, io, iter}; use super::{AssertMessage, BinOp, TerminatorKind}; -pub fn function_name(item: CrateItem) -> String { - let mut pretty_name = String::new(); - let body = item.body(); - pretty_name.push_str("fn "); - pretty_name.push_str(item.name().as_str()); - if body.arg_locals().is_empty() { - pretty_name.push_str("()"); - } else { - pretty_name.push_str("("); - } - body.arg_locals().iter().enumerate().for_each(|(index, local)| { - pretty_name.push_str(format!("_{}: ", index).as_str()); - pretty_name.push_str(&pretty_ty(local.ty.kind())); - }); - if !body.arg_locals().is_empty() { - pretty_name.push_str(")"); +use super::{BorrowKind, FakeBorrowKind}; + +impl Display for Ty { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + with(|ctx| write!(f, "{}", ctx.ty_pretty(*self))) } - let return_local = body.ret_local(); - pretty_name.push_str(" -> "); - pretty_name.push_str(&pretty_ty(return_local.ty.kind())); - pretty_name.push_str(" {"); - pretty_name } -pub fn function_body(body: &Body) -> String { - let mut pretty_body = String::new(); - body.inner_locals().iter().enumerate().for_each(|(index, local)| { - pretty_body.push_str(" "); - pretty_body.push_str(format!("let {}", ret_mutability(&local.mutability)).as_str()); - pretty_body.push_str(format!("_{}: ", index).as_str()); - pretty_body.push_str(format!("{}", pretty_ty(local.ty.kind())).as_str()); - pretty_body.push_str(";\n"); - }); - pretty_body.push_str("}"); - pretty_body +impl Debug for Place { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + with(|ctx| write!(f, "{}", ctx.place_pretty(self))) + } } -pub fn ret_mutability(mutability: &Mutability) -> String { - match mutability { - Mutability::Not => "".to_string(), - Mutability::Mut => "mut ".to_string(), - } +pub(crate) fn function_body(writer: &mut W, body: &Body, name: &str) -> io::Result<()> { + write!(writer, "fn {}(", name)?; + body.arg_locals() + .iter() + .enumerate() + .try_for_each(|(index, local)| write!(writer, "_{}: {}", index + 1, local.ty))?; + write!(writer, ")")?; + + let return_local = body.ret_local(); + writeln!(writer, " -> {} {{", return_local.ty)?; + + body.locals().iter().enumerate().try_for_each(|(index, local)| -> io::Result<()> { + if index == 0 || index > body.arg_count { + writeln!(writer, " let {}_{}: {};", pretty_mut(local.mutability), index, local.ty) + } else { + Ok(()) + } + })?; + + body.var_debug_info.iter().try_for_each(|info| { + let content = match &info.value { + VarDebugInfoContents::Place(place) => { + format!("{place:?}") + } + VarDebugInfoContents::Const(constant) => pretty_const(&constant.const_), + }; + writeln!(writer, " debug {} => {};", info.name, content) + })?; + + body.blocks + .iter() + .enumerate() + .map(|(index, block)| -> io::Result<()> { + writeln!(writer, " bb{}: {{", index)?; + let _ = block + .statements + .iter() + .map(|statement| -> io::Result<()> { + pretty_statement(writer, &statement.kind)?; + Ok(()) + }) + .collect::>(); + pretty_terminator(writer, &block.terminator.kind)?; + writeln!(writer, " }}").unwrap(); + Ok(()) + }) + .collect::, _>>()?; + writeln!(writer, "}}")?; + Ok(()) } -pub fn pretty_statement(statement: &StatementKind) -> String { - let mut pretty = String::new(); +fn pretty_statement(writer: &mut W, statement: &StatementKind) -> io::Result<()> { match statement { StatementKind::Assign(place, rval) => { - pretty.push_str(format!(" _{} = ", place.local).as_str()); - pretty.push_str(format!("{}", &pretty_rvalue(rval)).as_str()); + write!(writer, " {:?} = ", place)?; + pretty_rvalue(writer, rval)?; + writeln!(writer, ";") } // FIXME: Add rest of the statements - StatementKind::FakeRead(_, _) => { - return String::from("StatementKind::FakeRead:Unimplemented"); + StatementKind::FakeRead(cause, place) => { + writeln!(writer, "FakeRead({cause:?}, {place:?});") } - StatementKind::SetDiscriminant { .. } => { - return String::from("StatementKind::SetDiscriminant:Unimplemented"); + StatementKind::SetDiscriminant { place, variant_index } => { + writeln!(writer, "discriminant({place:?} = {};", variant_index.to_index()) } - StatementKind::Deinit(_) => return String::from("StatementKind::Deinit:Unimplemented"), - StatementKind::StorageLive(_) => { - return String::from("StatementKind::StorageLive:Unimplemented"); + StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"), + StatementKind::StorageLive(local) => { + writeln!(writer, "StorageLive(_{local});") } - StatementKind::StorageDead(_) => { - return String::from("StatementKind::StorageDead:Unimplemented"); + StatementKind::StorageDead(local) => { + writeln!(writer, "StorageDead(_{local});") } - StatementKind::Retag(_, _) => return String::from("StatementKind::Retag:Unimplemented"), - StatementKind::PlaceMention(_) => { - return String::from("StatementKind::PlaceMention:Unimplemented"); - } - StatementKind::AscribeUserType { .. } => { - return String::from("StatementKind::AscribeUserType:Unimplemented"); - } - StatementKind::Coverage(_) => return String::from("StatementKind::Coverage:Unimplemented"), - StatementKind::Intrinsic(_) => { - return String::from("StatementKind::Intrinsic:Unimplemented"); + StatementKind::Retag(kind, place) => writeln!(writer, "Retag({kind:?}, {place:?});"), + StatementKind::PlaceMention(place) => { + writeln!(writer, "PlaceMention({place:?};") } StatementKind::ConstEvalCounter => { - return String::from("StatementKind::ConstEvalCounter:Unimplemented"); + writeln!(writer, "ConstEvalCounter;") + } + StatementKind::Nop => writeln!(writer, "nop;"), + StatementKind::AscribeUserType { .. } + | StatementKind::Coverage(_) + | StatementKind::Intrinsic(_) => { + // FIX-ME: Make them pretty. + writeln!(writer, "{statement:?};") } - StatementKind::Nop => return String::from("StatementKind::Nop:Unimplemented"), } - pretty } -pub fn pretty_terminator(terminator: &TerminatorKind, w: &mut W) -> io::Result<()> { - write!(w, "{}", pretty_terminator_head(terminator))?; +fn pretty_terminator(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { + pretty_terminator_head(writer, terminator)?; let successors = terminator.successors(); let successor_count = successors.len(); let labels = pretty_successor_labels(terminator); let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_))); - let fmt_unwind = |fmt: &mut dyn Write| -> io::Result<()> { - write!(fmt, "unwind ")?; + let fmt_unwind = |w: &mut W| -> io::Result<()> { + write!(w, "unwind ")?; match terminator.unwind() { None | Some(UnwindAction::Cleanup(_)) => unreachable!(), - Some(UnwindAction::Continue) => write!(fmt, "continue"), - Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"), - Some(UnwindAction::Terminate) => write!(fmt, "terminate"), + Some(UnwindAction::Continue) => write!(w, "continue"), + Some(UnwindAction::Unreachable) => write!(w, "unreachable"), + Some(UnwindAction::Terminate) => write!(w, "terminate"), } }; match (successor_count, show_unwind) { - (0, false) => Ok(()), + (0, false) => {} (0, true) => { - write!(w, " -> ")?; - fmt_unwind(w)?; - Ok(()) - } - (1, false) => { - write!(w, " -> {:?}", successors[0])?; - Ok(()) + write!(writer, " -> ")?; + fmt_unwind(writer)?; } + (1, false) => write!(writer, " -> bb{:?}", successors[0])?, _ => { - write!(w, " -> [")?; + write!(writer, " -> [")?; for (i, target) in successors.iter().enumerate() { if i > 0 { - write!(w, ", ")?; + write!(writer, ", ")?; } - write!(w, "{}: bb{:?}", labels[i], target)?; + write!(writer, "{}: bb{:?}", labels[i], target)?; } if show_unwind { - write!(w, ", ")?; - fmt_unwind(w)?; + write!(writer, ", ")?; + fmt_unwind(writer)?; } - write!(w, "]") + write!(writer, "]")?; } - }?; + }; - Ok(()) + writeln!(writer, ";") } -pub fn pretty_terminator_head(terminator: &TerminatorKind) -> String { +fn pretty_terminator_head(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { use self::TerminatorKind::*; - let mut pretty = String::new(); + const INDENT: &'static str = " "; match terminator { - Goto { .. } => format!(" goto"), + Goto { .. } => write!(writer, "{INDENT}goto"), SwitchInt { discr, .. } => { - format!(" switchInt(_{})", pretty_operand(discr)) + write!(writer, "{INDENT}switchInt({})", pretty_operand(discr)) } - Resume => format!(" resume"), - Abort => format!(" abort"), - Return => format!(" return"), - Unreachable => format!(" unreachable"), - Drop { place, .. } => format!(" drop(_{:?})", place.local), + Resume => write!(writer, "{INDENT}resume"), + Abort => write!(writer, "{INDENT}abort"), + Return => write!(writer, "{INDENT}return"), + Unreachable => write!(writer, "{INDENT}unreachable"), + Drop { place, .. } => write!(writer, "{INDENT}drop({:?})", place), Call { func, args, destination, .. } => { - pretty.push_str(" "); - pretty.push_str(format!("_{} = ", destination.local).as_str()); - pretty.push_str(&pretty_operand(func)); - pretty.push_str("("); - args.iter().enumerate().for_each(|(i, arg)| { - if i > 0 { - pretty.push_str(", "); - } - pretty.push_str(&pretty_operand(arg)); - }); - pretty.push_str(")"); - pretty + write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?; + let mut args_iter = args.iter(); + args_iter.next().map_or(Ok(()), |arg| write!(writer, "{}", pretty_operand(arg)))?; + args_iter.try_for_each(|arg| write!(writer, ", {}", pretty_operand(arg)))?; + write!(writer, ")") } Assert { cond, expected, msg, target: _, unwind: _ } => { - pretty.push_str(" assert("); + write!(writer, "{INDENT}assert(")?; if !expected { - pretty.push_str("!"); + write!(writer, "!")?; } - pretty.push_str(format!("{} bool),", &pretty_operand(cond)).as_str()); - pretty.push_str(&pretty_assert_message(msg)); - pretty.push_str(")"); - pretty + write!(writer, "{}, ", &pretty_operand(cond))?; + pretty_assert_message(writer, msg)?; + write!(writer, ")") } - InlineAsm { .. } => todo!(), + InlineAsm { .. } => write!(writer, "{INDENT}InlineAsm"), } } -pub fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec { +fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec { use self::TerminatorKind::*; match terminator { Resume | Abort | Return | Unreachable => vec![], @@ -201,283 +209,175 @@ pub fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec { vec!["success".into(), "unwind".into()] } Assert { unwind: _, .. } => vec!["success".into()], - InlineAsm { .. } => todo!(), + InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()], + InlineAsm { destination: None, .. } => vec!["unwind".into()], } } -pub fn pretty_assert_message(msg: &AssertMessage) -> String { - let mut pretty = String::new(); +fn pretty_assert_message(writer: &mut W, msg: &AssertMessage) -> io::Result<()> { match msg { AssertMessage::BoundsCheck { len, index } => { let pretty_len = pretty_operand(len); let pretty_index = pretty_operand(index); - pretty.push_str(format!("\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}").as_str()); - pretty + write!( + writer, + "\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}" + ) } AssertMessage::Overflow(BinOp::Add, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Sub, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Mul, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Div, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Rem, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Shr, _, r) => { let pretty_r = pretty_operand(r); - pretty.push_str( - format!("\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}") - .as_str(), - ); - pretty + write!(writer, "\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}") } AssertMessage::Overflow(BinOp::Shl, _, r) => { let pretty_r = pretty_operand(r); - pretty.push_str( - format!("\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}") - .as_str(), - ); - pretty + write!(writer, "\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}") } AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op), AssertMessage::OverflowNeg(op) => { let pretty_op = pretty_operand(op); - pretty.push_str( - format!("\"attempt to negate `{{}}`, which would overflow\", {pretty_op}").as_str(), - ); - pretty + write!(writer, "\"attempt to negate `{{}}`, which would overflow\", {pretty_op}") } AssertMessage::DivisionByZero(op) => { let pretty_op = pretty_operand(op); - pretty.push_str(format!("\"attempt to divide `{{}}` by zero\", {pretty_op}").as_str()); - pretty + write!(writer, "\"attempt to divide `{{}}` by zero\", {pretty_op}") } AssertMessage::RemainderByZero(op) => { let pretty_op = pretty_operand(op); - pretty.push_str( - format!("\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}").as_str(), - ); - pretty + write!( + writer, + "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}" + ) } AssertMessage::MisalignedPointerDereference { required, found } => { let pretty_required = pretty_operand(required); let pretty_found = pretty_operand(found); - pretty.push_str(format!("\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}").as_str()); - pretty + write!( + writer, + "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}" + ) } AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { - msg.description().unwrap().to_string() + write!(writer, "{}", msg.description().unwrap()) } } } -pub fn pretty_operand(operand: &Operand) -> String { - let mut pretty = String::new(); +fn pretty_operand(operand: &Operand) -> String { match operand { Operand::Copy(copy) => { - pretty.push_str(""); - pretty.push_str(format!("{}", copy.local).as_str()); + format!("{:?}", copy) } Operand::Move(mv) => { - pretty.push_str("move "); - pretty.push_str(format!("_{}", mv.local).as_str()); - } - Operand::Constant(cnst) => { - pretty.push_str("const "); - pretty.push_str(with(|cx| cx.const_literal(&cnst.literal)).as_str()); + format!("move {:?}", mv) } + Operand::Constant(cnst) => pretty_const(&cnst.literal), } - pretty } -pub fn pretty_rvalue(rval: &Rvalue) -> String { - let mut pretty = String::new(); +fn pretty_const(literal: &Const) -> String { + with(|cx| cx.const_pretty(&literal)) +} + +fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { match rval { - Rvalue::AddressOf(muta, addr) => { - pretty.push_str("&raw "); - pretty.push_str(&ret_mutability(muta)); - pretty.push_str(format!("(*_{})", addr.local).as_str()); - } - Rvalue::Aggregate(aggregatekind, operands) => { - pretty.push_str(format!("{:#?}", aggregatekind).as_str()); - pretty.push_str("("); - operands.iter().enumerate().for_each(|(i, op)| { - pretty.push_str(&pretty_operand(op)); - if i != operands.len() - 1 { - pretty.push_str(", "); - } - }); - pretty.push_str(")"); + Rvalue::AddressOf(mutability, place) => { + write!(writer, "&raw {}(*{:?})", &pretty_mut(*mutability), place) } - Rvalue::BinaryOp(bin, op, op2) => { - pretty.push_str(&pretty_operand(op)); - pretty.push_str(" "); - pretty.push_str(format!("{:#?}", bin).as_str()); - pretty.push_str(" "); - pretty.push_str(&pretty_operand(op2)); + Rvalue::Aggregate(aggregate_kind, operands) => { + // FIXME: Add pretty_aggregate function that returns a pretty string + write!(writer, "{aggregate_kind:?} (")?; + let mut op_iter = operands.iter(); + op_iter.next().map_or(Ok(()), |op| write!(writer, "{}", pretty_operand(op)))?; + op_iter.try_for_each(|op| write!(writer, ", {}", pretty_operand(op)))?; + write!(writer, ")") + } + Rvalue::BinaryOp(bin, op1, op2) => { + write!(writer, "{:?}({}, {})", bin, &pretty_operand(op1), pretty_operand(op2)) } Rvalue::Cast(_, op, ty) => { - pretty.push_str(&pretty_operand(op)); - pretty.push_str(" as "); - pretty.push_str(&pretty_ty(ty.kind())); + write!(writer, "{} as {}", pretty_operand(op), ty) } Rvalue::CheckedBinaryOp(bin, op1, op2) => { - pretty.push_str(&pretty_operand(op1)); - pretty.push_str(" "); - pretty.push_str(format!("{:#?}", bin).as_str()); - pretty.push_str(" "); - pretty.push_str(&pretty_operand(op2)); + write!(writer, "Checked{:?}({}, {})", bin, &pretty_operand(op1), pretty_operand(op2)) } Rvalue::CopyForDeref(deref) => { - pretty.push_str("CopyForDeref"); - pretty.push_str(format!("{}", deref.local).as_str()); + write!(writer, "CopyForDeref({:?})", deref) } Rvalue::Discriminant(place) => { - pretty.push_str("discriminant"); - pretty.push_str(format!("{}", place.local).as_str()); + write!(writer, "discriminant({:?})", place) } Rvalue::Len(len) => { - pretty.push_str("len"); - pretty.push_str(format!("{}", len.local).as_str()); + write!(writer, "len({:?})", len) } Rvalue::Ref(_, borrowkind, place) => { - pretty.push_str("ref"); - pretty.push_str(format!("{:#?}", borrowkind).as_str()); - pretty.push_str(format!("{}", place.local).as_str()); + let kind = match borrowkind { + BorrowKind::Shared => "&", + BorrowKind::Fake(FakeBorrowKind::Deep) => "&fake ", + BorrowKind::Fake(FakeBorrowKind::Shallow) => "&fake shallow ", + BorrowKind::Mut { .. } => "&mut ", + }; + write!(writer, "{kind}{:?}", place) } Rvalue::Repeat(op, cnst) => { - pretty.push_str(&pretty_operand(op)); - pretty.push_str(" "); - pretty.push_str(&pretty_ty(cnst.ty().kind())); + write!(writer, "{} \" \" {}", &pretty_operand(op), cnst.ty()) } - Rvalue::ShallowInitBox(_, _) => (), + Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::ThreadLocalRef(item) => { - pretty.push_str("thread_local_ref"); - pretty.push_str(format!("{:#?}", item).as_str()); + write!(writer, "thread_local_ref{:?}", item) } Rvalue::NullaryOp(nul, ty) => { - pretty.push_str(format!("{:#?}", nul).as_str()); - pretty.push_str(&pretty_ty(ty.kind())); - pretty.push_str(" "); + write!(writer, "{:?} {} \" \"", nul, ty) } Rvalue::UnaryOp(un, op) => { - pretty.push_str(&pretty_operand(op)); - pretty.push_str(" "); - pretty.push_str(format!("{:#?}", un).as_str()); + write!(writer, "{} \" \" {:?}", pretty_operand(op), un) } - Rvalue::Use(op) => pretty.push_str(&pretty_operand(op)), + Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)), } - pretty } -pub fn pretty_ty(ty: TyKind) -> String { - let mut pretty = String::new(); - match ty { - TyKind::RigidTy(rigid_ty) => match rigid_ty { - RigidTy::Bool => "bool".to_string(), - RigidTy::Char => "char".to_string(), - RigidTy::Int(i) => match i { - IntTy::Isize => "isize".to_string(), - IntTy::I8 => "i8".to_string(), - IntTy::I16 => "i16".to_string(), - IntTy::I32 => "i32".to_string(), - IntTy::I64 => "i64".to_string(), - IntTy::I128 => "i128".to_string(), - }, - RigidTy::Uint(u) => match u { - UintTy::Usize => "usize".to_string(), - UintTy::U8 => "u8".to_string(), - UintTy::U16 => "u16".to_string(), - UintTy::U32 => "u32".to_string(), - UintTy::U64 => "u64".to_string(), - UintTy::U128 => "u128".to_string(), - }, - RigidTy::Float(f) => match f { - FloatTy::F32 => "f32".to_string(), - FloatTy::F64 => "f64".to_string(), - }, - RigidTy::Adt(def, _) => { - format!("{:#?}", with(|cx| cx.def_ty(def.0))) - } - RigidTy::Str => "str".to_string(), - RigidTy::Array(ty, len) => { - format!("[{}; {}]", pretty_ty(ty.kind()), with(|cx| cx.const_literal(&len))) - } - RigidTy::Slice(ty) => { - format!("[{}]", pretty_ty(ty.kind())) - } - RigidTy::RawPtr(ty, mutability) => { - pretty.push_str("*"); - match mutability { - Mutability::Not => pretty.push_str("const "), - Mutability::Mut => pretty.push_str("mut "), - } - pretty.push_str(&pretty_ty(ty.kind())); - pretty - } - RigidTy::Ref(_, ty, mutability) => match mutability { - Mutability::Not => format!("&{}", pretty_ty(ty.kind())), - Mutability::Mut => format!("&mut {}", pretty_ty(ty.kind())), - }, - RigidTy::FnDef(_, _) => format!("{:#?}", rigid_ty), - RigidTy::FnPtr(_) => format!("{:#?}", rigid_ty), - RigidTy::Closure(_, _) => format!("{:#?}", rigid_ty), - RigidTy::Coroutine(_, _, _) => format!("{:#?}", rigid_ty), - RigidTy::Dynamic(data, region, repr) => { - // FIXME: Fix binder printing, it looks ugly now - pretty.push_str("("); - match repr { - DynKind::Dyn => pretty.push_str("dyn "), - DynKind::DynStar => pretty.push_str("dyn* "), - } - pretty.push_str(format!("{:#?}", data).as_str()); - pretty.push_str(format!(" + {:#?} )", region).as_str()); - pretty - } - RigidTy::Never => "!".to_string(), - RigidTy::Tuple(tuple) => { - if tuple.is_empty() { - "()".to_string() - } else { - let mut tuple_str = String::new(); - tuple_str.push_str("("); - tuple.iter().enumerate().for_each(|(i, ty)| { - tuple_str.push_str(&pretty_ty(ty.kind())); - if i != tuple.len() - 1 { - tuple_str.push_str(", "); - } - }); - tuple_str.push_str(")"); - tuple_str - } - } - _ => format!("{:#?}", rigid_ty), - }, - TyKind::Alias(_, _) => format!("{:#?}", ty), - TyKind::Param(param_ty) => { - format!("{:#?}", param_ty.name) - } - TyKind::Bound(_, _) => format!("{:#?}", ty), +fn pretty_mut(mutability: Mutability) -> &'static str { + match mutability { + Mutability::Not => " ", + Mutability::Mut => "mut ", } } diff --git a/compiler/stable_mir/src/target.rs b/compiler/stable_mir/src/target.rs index 3a9011a2ffe15..e00a418c54039 100644 --- a/compiler/stable_mir/src/target.rs +++ b/compiler/stable_mir/src/target.rs @@ -14,7 +14,7 @@ impl MachineInfo { with(|cx| cx.target_info()) } - pub fn target_endianess() -> Endian { + pub fn target_endianness() -> Endian { with(|cx| cx.target_info().endian) } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index a337675202871..bc6fb34493a6c 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -99,6 +99,12 @@ impl Ty { } } +/// Represents a pattern in the type system +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Pattern { + Range { start: Option, end: Option, include_end: bool }, +} + /// Represents a constant in MIR or from the Type system. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Const { @@ -481,6 +487,7 @@ pub enum RigidTy { Foreign(ForeignDef), Str, Array(Ty, Const), + Pat(Ty, Pattern), Slice(Ty), RawPtr(Ty, Mutability), Ref(Region, Ty, Mutability), @@ -654,7 +661,7 @@ impl AdtDef { with(|cx| cx.def_ty(self.0)) } - /// Retrieve the type of this Adt instantiating the type with the given arguments. + /// Retrieve the type of this Adt by instantiating and normalizing it with the given arguments. /// /// This will assume the type can be instantiated with these arguments. pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { @@ -733,7 +740,7 @@ pub struct FieldDef { } impl FieldDef { - /// Retrieve the type of this field instantiating the type with the given arguments. + /// Retrieve the type of this field instantiating and normalizing it with the given arguments. /// /// This will assume the type can be instantiated with these arguments. pub fn ty_with_args(&self, args: &GenericArgs) -> Ty { @@ -1332,7 +1339,7 @@ pub enum AliasRelationDirection { #[derive(Clone, Debug, Eq, PartialEq)] pub struct TraitPredicate { pub trait_ref: TraitRef, - pub polarity: ImplPolarity, + pub polarity: PredicatePolarity, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1354,6 +1361,12 @@ pub enum ImplPolarity { Reservation, } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum PredicatePolarity { + Positive, + Negative, +} + pub trait IndexedVal { fn to_val(index: usize) -> Self; diff --git a/compiler/stable_mir/src/visitor.rs b/compiler/stable_mir/src/visitor.rs index 65e42879d618b..2d7159f87fec3 100644 --- a/compiler/stable_mir/src/visitor.rs +++ b/compiler/stable_mir/src/visitor.rs @@ -139,6 +139,7 @@ impl Visitable for RigidTy { t.visit(visitor)?; c.visit(visitor) } + RigidTy::Pat(t, _p) => t.visit(visitor), RigidTy::Slice(inner) => inner.visit(visitor), RigidTy::RawPtr(ty, _) => ty.visit(visitor), RigidTy::Ref(reg, ty, _) => { diff --git a/config.example.toml b/config.example.toml index f94553dd63f72..5c1fac7672a9a 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" @@ -517,11 +517,8 @@ #overflow-checks-std = rust.overflow-checks (boolean) # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. -# `0` - no debug info -# `1` - line tables only - sufficient to generate backtraces that include line -# information and inlined functions, set breakpoints at source code -# locations, and step through execution in a debugger. -# `2` - full debug info with variable and type information +# See https://doc.rust-lang.org/rustc/codegen-options/index.html#debuginfo for available options. +# # Can be overridden for specific subsets of Rust code (rustc, std or tools). # Debuginfo for tests run with compiletest is not controlled by this option # and needs to be enabled separately with `debuginfo-level-tests`. @@ -915,6 +912,6 @@ # Available options: fast, balanced, best #compression-profile = "fast" -# Copy the linker, DLLs, and various libraries from MinGW into the rustc toolchain. +# Copy the linker, DLLs, and various libraries from MinGW into the Rust toolchain. # Only applies when the host or target is pc-windows-gnu. #include-mingw-linker = true diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs index 638f343fb244b..0561f49c967e5 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -1,6 +1,8 @@ // Disabling on android for the time being // See https://github.com/rust-lang/rust/issues/73535#event-3477699747 #![cfg(not(target_os = "android"))] +// Disabling in Miri as these would take too long. +#![cfg(not(miri))] #![feature(btree_extract_if)] #![feature(iter_next_chunk)] #![feature(repr_simd)] diff --git a/library/alloc/benches/vec_deque_append.rs b/library/alloc/benches/vec_deque_append.rs index 5825bdc355f2d..30b6e600e5adc 100644 --- a/library/alloc/benches/vec_deque_append.rs +++ b/library/alloc/benches/vec_deque_append.rs @@ -5,6 +5,11 @@ const WARMUP_N: usize = 100; const BENCH_N: usize = 1000; fn main() { + if cfg!(miri) { + // Don't benchmark Miri... + // (Due to bootstrap quirks, this gets picked up by `x.py miri library/alloc --no-doc`.) + return; + } let a: VecDeque = (0..VECDEQUE_LEN).collect(); let b: VecDeque = (0..VECDEQUE_LEN).collect(); diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index a5d28aa5252f8..6677534eafc6e 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -51,7 +51,7 @@ extern "Rust" { #[derive(Copy, Clone, Default, Debug)] #[cfg(not(test))] // the compiler needs to know when a Box uses the global allocator vs a custom one -#[cfg_attr(not(bootstrap), lang = "global_alloc_ty")] +#[lang = "global_alloc_ty"] pub struct Global; #[cfg(test)] @@ -387,8 +387,7 @@ pub const fn handle_alloc_error(layout: Layout) -> ! { } #[cfg(not(feature = "panic_immediate_abort"))] - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - unsafe { + { core::intrinsics::const_eval_select((layout,), ct_error, rt_error) } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 304f607000b01..e5d62447eb20e 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -155,17 +155,16 @@ use core::error::Error; use core::fmt; use core::future::Future; use core::hash::{Hash, Hasher}; -use core::intrinsics::retag_box_to_raw; use core::iter::FusedIterator; use core::marker::Tuple; use core::marker::Unsize; use core::mem::{self, SizedTypeProperties}; use core::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; use core::ops::{ - CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DispatchFromDyn, Receiver, + CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver, }; use core::pin::Pin; -use core::ptr::{self, NonNull, Unique}; +use core::ptr::{self, addr_of_mut, NonNull, Unique}; use core::task::{Context, Poll}; #[cfg(not(no_global_oom_handling))] @@ -1059,7 +1058,8 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Self) -> *mut T { - Self::into_raw_with_allocator(b).0 + // Make sure Miri realizes that we transition from a noalias pointer to a raw pointer here. + unsafe { addr_of_mut!(*&mut *Self::into_raw_with_allocator(b).0) } } /// Consumes the `Box`, returning a wrapped raw pointer and the allocator. @@ -1111,16 +1111,15 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) { - // This is the transition point from `Box` to raw pointers. For Stacked Borrows, these casts - // are relevant -- if this is a global allocator Box and we just get the pointer from `b.0`, - // it will have `Unique` permission, which is not what we want from a raw pointer. We could - // fix that by going through `&mut`, but then if this is *not* a global allocator Box, we'd - // be adding uniqueness assertions that we do not want. So for Miri's sake we pass this - // pointer through an intrinsic for box-to-raw casts, which can do the right thing wrt the - // aliasing model. - let b = mem::ManuallyDrop::new(b); + let mut b = mem::ManuallyDrop::new(b); + // We carefully get the raw pointer out in a way that Miri's aliasing model understands what + // is happening: using the primitive "deref" of `Box`. In case `A` is *not* `Global`, we + // want *no* aliasing requirements here! + // In case `A` *is* `Global`, this does not quite have the right behavior; `into_raw` + // works around that. + let ptr = addr_of_mut!(**b); let alloc = unsafe { ptr::read(&b.1) }; - (unsafe { retag_box_to_raw::(b.0.as_ptr()) }, alloc) + (ptr, alloc) } #[unstable( @@ -1944,6 +1943,9 @@ impl DerefMut for Box { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for Box {} + #[unstable(feature = "receiver_trait", issue = "none")] impl Receiver for Box {} @@ -2047,18 +2049,16 @@ impl + ?Sized, A: Allocator> AsyncFnOnce #[unstable(feature = "async_fn_traits", issue = "none")] impl + ?Sized, A: Allocator> AsyncFnMut for Box { - type CallMutFuture<'a> = F::CallMutFuture<'a> where Self: 'a; + type CallRefFuture<'a> = F::CallRefFuture<'a> where Self: 'a; - extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallMutFuture<'_> { + extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallRefFuture<'_> { F::async_call_mut(self, args) } } #[unstable(feature = "async_fn_traits", issue = "none")] impl + ?Sized, A: Allocator> AsyncFn for Box { - type CallFuture<'a> = F::CallFuture<'a> where Self: 'a; - - extern "rust-call" fn async_call(&self, args: Args) -> Self::CallFuture<'_> { + extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_> { F::async_call(self, args) } } @@ -2088,11 +2088,29 @@ impl Clone for Box<[T], A> { self.to_vec_in(alloc).into_boxed_slice() } - fn clone_from(&mut self, other: &Self) { - if self.len() == other.len() { - self.clone_from_slice(&other); + /// Copies `source`'s contents into `self` without creating a new allocation, + /// so long as the two are of the same length. + /// + /// # Examples + /// + /// ``` + /// let x = Box::new([5, 6, 7]); + /// let mut y = Box::new([8, 9, 10]); + /// let yp: *const [i32] = &*y; + /// + /// y.clone_from(&x); + /// + /// // The value is the same + /// assert_eq!(x, y); + /// + /// // And no allocation occurred + /// assert_eq!(yp, &*y); + /// ``` + fn clone_from(&mut self, source: &Self) { + if self.len() == source.len() { + self.clone_from_slice(&source); } else { - *self = other.clone(); + *self = source.clone(); } } } diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 0421a12b3a952..8b145b67bf186 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -4,10 +4,14 @@ use crate::alloc::{self, Layout, LayoutError}; use core::error::Error; use core::fmt::{self, Debug, Display, Formatter}; +#[cfg(not(no_global_oom_handling))] +use core::intrinsics::const_allocate; use core::marker::PhantomData; #[cfg(not(no_global_oom_handling))] use core::marker::Unsize; -use core::mem::{self, SizedTypeProperties}; +use core::mem; +#[cfg(not(no_global_oom_handling))] +use core::mem::SizedTypeProperties; use core::ops::{Deref, DerefMut}; use core::ptr::Pointee; use core::ptr::{self, NonNull}; @@ -109,9 +113,14 @@ impl ThinBox { where T: Unsize, { - let meta = ptr::metadata(&value as &Dyn); - let ptr = WithOpaqueHeader::new(meta, value); - ThinBox { ptr, _marker: PhantomData } + if mem::size_of::() == 0 { + let ptr = WithOpaqueHeader::new_unsize_zst::(value); + ThinBox { ptr, _marker: PhantomData } + } else { + let meta = ptr::metadata(&value as &Dyn); + let ptr = WithOpaqueHeader::new(meta, value); + ThinBox { ptr, _marker: PhantomData } + } } } @@ -200,6 +209,16 @@ impl WithOpaqueHeader { Self(ptr.0) } + #[cfg(not(no_global_oom_handling))] + fn new_unsize_zst(value: T) -> Self + where + Dyn: ?Sized, + T: Unsize, + { + let ptr = WithHeader::<::Metadata>::new_unsize_zst::(value); + Self(ptr.0) + } + fn try_new(header: H, value: T) -> Result { WithHeader::try_new(header, value).map(|ptr| Self(ptr.0)) } @@ -288,6 +307,58 @@ impl WithHeader { } } + // `Dyn` is `?Sized` type like `[u32]`, and `T` is ZST type like `[u32; 0]`. + #[cfg(not(no_global_oom_handling))] + fn new_unsize_zst(value: T) -> WithHeader + where + Dyn: Pointee + ?Sized, + T: Unsize, + { + assert!(mem::size_of::() == 0); + + const fn max(a: usize, b: usize) -> usize { + if a > b { a } else { b } + } + + // Compute a pointer to the right metadata. This will point to the beginning + // of the header, past the padding, so the assigned type makes sense. + // It also ensures that the address at the end of the header is sufficiently + // aligned for T. + let alloc: &::Metadata = const { + // FIXME: just call `WithHeader::alloc_layout` with size reset to 0. + // Currently that's blocked on `Layout::extend` not being `const fn`. + + let alloc_align = + max(mem::align_of::(), mem::align_of::<::Metadata>()); + + let alloc_size = + max(mem::align_of::(), mem::size_of::<::Metadata>()); + + unsafe { + // SAFETY: align is power of two because it is the maximum of two alignments. + let alloc: *mut u8 = const_allocate(alloc_size, alloc_align); + + let metadata_offset = + alloc_size.checked_sub(mem::size_of::<::Metadata>()).unwrap(); + // SAFETY: adding offset within the allocation. + let metadata_ptr: *mut ::Metadata = + alloc.add(metadata_offset).cast(); + // SAFETY: `*metadata_ptr` is within the allocation. + metadata_ptr.write(ptr::metadata::(ptr::dangling::() as *const Dyn)); + + // SAFETY: we have just written the metadata. + &*(metadata_ptr) + } + }; + + // SAFETY: `alloc` points to `::Metadata`, so addition stays in-bounds. + let value_ptr = + unsafe { (alloc as *const ::Metadata).add(1) }.cast::().cast_mut(); + debug_assert!(value_ptr.is_aligned()); + mem::forget(value); + WithHeader(NonNull::new(value_ptr.cast()).unwrap(), PhantomData) + } + // Safety: // - Assumes that either `value` can be dereferenced, or is the // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. @@ -300,20 +371,19 @@ impl WithHeader { impl Drop for DropGuard { fn drop(&mut self) { + // All ZST are allocated statically. + if self.value_layout.size() == 0 { + return; + } + unsafe { // SAFETY: Layout must have been computable if we're in drop let (layout, value_offset) = WithHeader::::alloc_layout(self.value_layout).unwrap_unchecked(); - // Note: Don't deallocate if the layout size is zero, because the pointer - // didn't come from the allocator. - if layout.size() != 0 { - alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); - } else { - debug_assert!( - value_offset == 0 && H::IS_ZST && self.value_layout.size() == 0 - ); - } + // Since we only allocate for non-ZSTs, the layout size cannot be zero. + debug_assert!(layout.size() != 0); + alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); } } } diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 83b2678f7f52d..846b9a1404d27 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -31,7 +31,7 @@ //! // instead of a max-heap. //! impl Ord for State { //! fn cmp(&self, other: &Self) -> Ordering { -//! // Notice that the we flip the ordering on costs. +//! // Notice that we flip the ordering on costs. //! // In case of a tie we compare positions - this step is necessary //! // to make implementations of `PartialEq` and `Ord` consistent. //! other.cost.cmp(&self.cost) @@ -385,6 +385,12 @@ impl Clone for BinaryHeap { BinaryHeap { data: self.data.clone() } } + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation if possible. + /// + /// See [`Vec::clone_from()`] for more details. fn clone_from(&mut self, source: &Self) { self.data.clone_from(&source.data); } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index e99c6220e2064..3875f61efafdf 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -72,7 +72,7 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// `BTreeMap` that observed the logic error and not result in undefined behavior. This could /// include panics, incorrect results, aborts, memory leaks, and non-termination. /// -/// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::values`], or +/// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::into_iter`], [`BTreeMap::values`], or /// [`BTreeMap::keys`] produce their items in order by key, and take worst-case logarithmic and /// amortized constant time per item returned. /// @@ -415,7 +415,7 @@ impl<'a, K: 'a, V: 'a> Default for IterMut<'a, K, V> { } } -/// An owning iterator over the entries of a `BTreeMap`. +/// An owning iterator over the entries of a `BTreeMap`, sorted by key. /// /// This `struct` is created by the [`into_iter`] method on [`BTreeMap`] /// (provided by the [`IntoIterator`] trait). See its documentation for more. @@ -1637,6 +1637,7 @@ impl IntoIterator for BTreeMap { type Item = (K, V); type IntoIter = IntoIter; + /// Gets an owning iterator over the entries of the map, sorted by key. fn into_iter(self) -> IntoIter { let mut me = ManuallyDrop::new(self); if let Some(root) = me.root.take() { diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index 0a894258f469f..66eb991c6d4b8 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -354,7 +354,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { // SAFETY: There is no tree yet so no reference to it exists. let map = unsafe { self.dormant_map.awaken() }; let mut root = NodeRef::new_leaf(self.alloc.clone()); - let val_ptr = root.borrow_mut().push(self.key, value) as *mut V; + let val_ptr = root.borrow_mut().push(self.key, value); map.root = Some(root.forget_type()); map.length = 1; val_ptr diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index a85a3162451bf..5e6a26f65c41e 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -655,7 +655,7 @@ impl NodeRef { Leaf(NodeRef), Internal(NodeRef), - InternalKV(Handle, marker::KV>), + InternalKV, } impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { @@ -677,7 +677,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> visit(Position::Leaf(leaf)); match edge.next_kv() { Ok(kv) => { - visit(Position::InternalKV(kv)); + visit(Position::InternalKV); kv.right_edge() } Err(_) => return, @@ -699,7 +699,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> self.visit_nodes_in_order(|pos| match pos { Position::Leaf(node) => result += node.len(), Position::Internal(node) => result += node.len(), - Position::InternalKV(_) => (), + Position::InternalKV => (), }); result } diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 64bce0ff8c048..d230749d71231 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -32,11 +32,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> result += &format!("\n{}{:?}", indent, leaf.keys()); } navigate::Position::Internal(_) => {} - navigate::Position::InternalKV(kv) => { - let depth = self.height() - kv.into_node().height(); - let indent = " ".repeat(depth); - result += &format!("\n{}{:?}", indent, kv.into_kv().0); - } + navigate::Position::InternalKV => {} }); result } diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index ed91ae1a66e3d..b0bd6ef2d3c63 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -27,7 +27,7 @@ use crate::alloc::{Allocator, Global}; /// `BTreeSet` that observed the logic error and not result in undefined behavior. This could /// include panics, incorrect results, aborts, memory leaks, and non-termination. /// -/// Iterators returned by [`BTreeSet::iter`] produce their items in order, and take worst-case +/// Iterators returned by [`BTreeSet::iter`] and [`BTreeSet::into_iter`] produce their items in order, and take worst-case /// logarithmic and amortized constant time per item returned. /// /// [`Cell`]: core::cell::Cell @@ -116,8 +116,8 @@ impl Clone for BTreeSet { BTreeSet { map: self.map.clone() } } - fn clone_from(&mut self, other: &Self) { - self.map.clone_from(&other.map); + fn clone_from(&mut self, source: &Self) { + self.map.clone_from(&source.map); } } @@ -140,7 +140,7 @@ impl fmt::Debug for Iter<'_, T> { } } -/// An owning iterator over the items of a `BTreeSet`. +/// An owning iterator over the items of a `BTreeSet` in ascending order. /// /// This `struct` is created by the [`into_iter`] method on [`BTreeSet`] /// (provided by the [`IntoIterator`] trait). See its documentation for more. @@ -1237,7 +1237,7 @@ impl IntoIterator for BTreeSet { type Item = T; type IntoIter = IntoIter; - /// Gets an iterator for moving out the `BTreeSet`'s contents. + /// Gets an iterator for moving out the `BTreeSet`'s contents in ascending order. /// /// # Examples /// diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 6dfb82ac807a0..1c90c171a155b 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -2126,16 +2126,22 @@ impl Clone for LinkedList { list } - fn clone_from(&mut self, other: &Self) { - let mut iter_other = other.iter(); - if self.len() > other.len() { - self.split_off(other.len()); + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation of the nodes of the linked list. Additionally, + /// if the element type `T` overrides `clone_from()`, this will reuse the + /// resources of `self`'s elements as well. + fn clone_from(&mut self, source: &Self) { + let mut source_iter = source.iter(); + if self.len() > source.len() { + self.split_off(source.len()); } - for (elem, elem_other) in self.iter_mut().zip(&mut iter_other) { - elem.clone_from(elem_other); + for (elem, source_elem) in self.iter_mut().zip(&mut source_iter) { + elem.clone_from(source_elem); } - if !iter_other.is_empty() { - self.extend(iter_other.cloned()); + if !source_iter.is_empty() { + self.extend(source_iter.cloned()); } } } diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 41adc2e79dc74..4643a6bbe2ecd 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -113,9 +113,13 @@ impl Clone for VecDeque { deq } - fn clone_from(&mut self, other: &Self) { + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation if possible. + fn clone_from(&mut self, source: &Self) { self.clear(); - self.extend(other.iter().cloned()); + self.extend(source.iter().cloned()); } } @@ -1614,7 +1618,10 @@ impl VecDeque { let old_head = self.head; self.head = self.to_physical_idx(1); self.len -= 1; - Some(unsafe { self.buffer_read(old_head) }) + unsafe { + core::hint::assert_unchecked(self.len < self.capacity()); + Some(self.buffer_read(old_head)) + } } } @@ -1638,7 +1645,10 @@ impl VecDeque { None } else { self.len -= 1; - Some(unsafe { self.buffer_read(self.to_physical_idx(self.len)) }) + unsafe { + core::hint::assert_unchecked(self.len < self.capacity()); + Some(self.buffer_read(self.to_physical_idx(self.len))) + } } } @@ -2087,7 +2097,7 @@ impl VecDeque { // buffer without it being full emerge debug_assert!(self.is_full()); let old_cap = self.capacity(); - self.buf.reserve_for_push(old_cap); + self.buf.grow_one(); unsafe { self.handle_capacity_increase(old_cap); } @@ -2464,8 +2474,10 @@ impl VecDeque { /// /// let mut deque: VecDeque<_> = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into(); /// let num = 42; - /// let idx = deque.partition_point(|&x| x < num); - /// // The above is equivalent to `let idx = deque.binary_search(&num).unwrap_or_else(|x| x);` + /// let idx = deque.partition_point(|&x| x <= num); + /// // If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to + /// // `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` may allow `insert` + /// // to shift less elements. /// deque.insert(idx, num); /// assert_eq!(deque, &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); /// ``` diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index 0c7f94ccceb02..6a64eaf576bb9 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -408,7 +408,7 @@ impl CString { fn strlen(s: *const c_char) -> usize; } let len = strlen(ptr) + 1; // Including the NUL byte - let slice = slice::from_raw_parts_mut(ptr, len as usize); + let slice = slice::from_raw_parts_mut(ptr, len); CString { inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]) } } } diff --git a/library/alloc/src/lib.miri.rs b/library/alloc/src/lib.miri.rs new file mode 100644 index 0000000000000..89d7f49f55d2e --- /dev/null +++ b/library/alloc/src/lib.miri.rs @@ -0,0 +1,4 @@ +//! Grep bootstrap for `MIRI_REPLACE_LIBRS_IF_NOT_TEST` to learn what this is about. +#![no_std] +extern crate alloc as realalloc; +pub use realalloc::*; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index ca504b05a9689..b93936869b3fd 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -56,11 +56,6 @@ //! [`Rc`]: rc //! [`RefCell`]: core::cell -// To run alloc tests without x.py without ending up with two copies of alloc, Miri needs to be -// able to "empty" this crate. See . -// rustc itself never sets the feature, so this line has no effect there. -#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] -// #![allow(unused_attributes)] #![stable(feature = "alloc", since = "1.36.0")] #![doc( @@ -71,7 +66,6 @@ #![doc(cfg_hide( not(test), not(any(test, bootstrap)), - any(not(feature = "miri-test-libstd"), test, doctest), no_global_oom_handling, not(no_global_oom_handling), not(no_rc), @@ -114,14 +108,17 @@ #![feature(const_box)] #![feature(const_cow_is_borrowed)] #![feature(const_eval_select)] +#![feature(const_heap)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_write)] +#![feature(const_option)] #![feature(const_pin)] #![feature(const_refs_to_cell)] #![feature(const_size_of_val)] #![feature(const_waker)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] +#![feature(deref_pure_trait)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] #![feature(error_in_core)] @@ -129,10 +126,8 @@ #![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] -#![feature(generic_nonzero)] #![feature(hasher_prefixfree_extras)] #![feature(hint_assert_unchecked)] -#![feature(inline_const)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] @@ -142,7 +137,6 @@ #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array_transpose)] -#![feature(non_null_convenience)] #![feature(panic_internals)] #![feature(pattern)] #![feature(ptr_internals)] @@ -154,7 +148,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)] @@ -165,10 +158,9 @@ #![feature(try_trait_v2)] #![feature(try_with_capacity)] #![feature(tuple_trait)] -#![feature(unchecked_math)] #![feature(unicode_internals)] #![feature(unsize)] -#![feature(utf8_chunks)] +#![feature(vec_pop_if)] // tidy-alphabetical-end // // Language features: @@ -178,7 +170,6 @@ #![cfg_attr(test, feature(test))] #![feature(allocator_internals)] #![feature(allow_internal_unstable)] -#![feature(associated_type_bounds)] #![feature(c_unwind)] #![feature(cfg_sanitize)] #![feature(const_mut_refs)] @@ -196,7 +187,6 @@ #![feature(multiple_supertrait_upcastable)] #![feature(negative_impls)] #![feature(never_type)] -#![feature(pointer_is_aligned)] #![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] #![feature(slice_internals)] @@ -205,6 +195,7 @@ #![feature(unboxed_closures)] #![feature(unsized_fn_params)] #![feature(with_negative_coherence)] +#![rustc_preserve_ub_checks] // tidy-alphabetical-end // // Rustdoc features: diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 0ee293db73a93..1134c7f833e2b 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -114,7 +114,10 @@ impl RawVec { #[must_use] #[inline] pub fn with_capacity(capacity: usize) -> Self { - handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global)) + match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global) { + Ok(res) => res, + Err(err) => handle_error(err), + } } /// Like `with_capacity`, but guarantees the buffer is zeroed. @@ -152,7 +155,10 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { - handle_reserve(Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)) + match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc) { + Ok(res) => res, + Err(err) => handle_error(err), + } } /// Like `try_with_capacity`, but parameterized over the choice of @@ -167,7 +173,10 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { - handle_reserve(Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc)) + match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc) { + Ok(res) => res, + Err(err) => handle_error(err), + } } /// Converts the entire buffer into `Box<[MaybeUninit]>` with the specified `len`. @@ -250,6 +259,17 @@ impl RawVec { 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, 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. @@ -326,7 +346,9 @@ impl RawVec { len: usize, additional: usize, ) { - handle_reserve(slf.grow_amortized(len, additional)); + if let Err(err) = slf.grow_amortized(len, additional) { + handle_error(err); + } } if self.needs_to_grow(len, additional) { @@ -334,12 +356,14 @@ impl RawVec { } } - /// A specialized version of `reserve()` used only by the hot and - /// oft-instantiated `Vec::push()`, which does its own capacity check. + /// A specialized version of `self.reserve(len, 1)` which requires the + /// caller to ensure `len == self.capacity()`. #[cfg(not(no_global_oom_handling))] #[inline(never)] - pub fn reserve_for_push(&mut self, len: usize) { - handle_reserve(self.grow_amortized(len, 1)); + pub fn grow_one(&mut self) { + if let Err(err) = self.grow_amortized(self.cap.0, 1) { + handle_error(err); + } } /// The same as `reserve`, but returns on errors instead of panicking or aborting. @@ -373,7 +397,9 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] pub fn reserve_exact(&mut self, len: usize, additional: usize) { - handle_reserve(self.try_reserve_exact(len, additional)); + if let Err(err) = self.try_reserve_exact(len, additional) { + handle_error(err); + } } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. @@ -404,7 +430,9 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] pub fn shrink_to_fit(&mut self, cap: usize) { - handle_reserve(self.shrink(cap)); + if let Err(err) = self.shrink(cap) { + handle_error(err); + } } } @@ -559,12 +587,11 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { // Central function for reserve error handling. #[cfg(not(no_global_oom_handling))] -#[inline] -fn handle_reserve(result: Result) -> T { - match result.map_err(|e| e.kind()) { - Ok(res) => res, - Err(CapacityOverflow) => capacity_overflow(), - Err(AllocError { layout, .. }) => handle_alloc_error(layout), +#[cold] +fn handle_error(e: TryReserveError) -> ! { + match e.kind() { + CapacityOverflow => capacity_overflow(), + AllocError { layout, .. } => handle_alloc_error(layout), } } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index facfc9d208e42..c245b42c3e880 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -260,7 +260,7 @@ use core::marker::{PhantomData, Unsize}; #[cfg(not(no_global_oom_handling))] use core::mem::size_of_val; use core::mem::{self, align_of_val_raw, forget, ManuallyDrop}; -use core::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; +use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] use core::pin::Pin; @@ -884,7 +884,10 @@ impl Rc { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn pin_in(value: T, alloc: A) -> Pin { + pub fn pin_in(value: T, alloc: A) -> Pin + where + A: 'static, + { unsafe { Pin::new_unchecked(Rc::new_in(value, alloc)) } } @@ -2126,6 +2129,9 @@ impl Deref for Rc { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for Rc {} + #[unstable(feature = "receiver_trait", issue = "none")] impl Receiver for Rc {} @@ -3023,13 +3029,10 @@ impl Weak { /// [`as_ptr`]: Weak::as_ptr #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn into_raw_and_alloc(self) -> (*const T, A) - where - A: Clone, - { - let result = self.as_ptr(); - let alloc = self.alloc.clone(); - mem::forget(self); + pub fn into_raw_and_alloc(self) -> (*const T, A) { + let rc = mem::ManuallyDrop::new(self); + let result = rc.as_ptr(); + let alloc = unsafe { ptr::read(&rc.alloc) }; (result, alloc) } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 21d5dce04a0fc..ebe6f7e7caa9b 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -511,9 +511,9 @@ impl [T] { while m > 0 { // `buf.extend(buf)`: unsafe { - ptr::copy_nonoverlapping( + ptr::copy_nonoverlapping::( buf.as_ptr(), - (buf.as_mut_ptr() as *mut T).add(buf.len()), + (buf.as_mut_ptr()).add(buf.len()), buf.len(), ); // `buf` has capacity of `self.len() * n`. @@ -532,9 +532,9 @@ impl [T] { // `buf.extend(buf[0 .. rem_len])`: unsafe { // This is non-overlapping since `2^expn > rem`. - ptr::copy_nonoverlapping( + ptr::copy_nonoverlapping::( buf.as_ptr(), - (buf.as_mut_ptr() as *mut T).add(buf.len()), + (buf.as_mut_ptr()).add(buf.len()), rem_len, ); // `buf.len() + rem_len` equals to `buf.capacity()` (`= self.len() * n`). diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index ade114678b7f9..c0d292cd20886 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -53,7 +53,7 @@ pub use core::str::{RSplit, Split}; pub use core::str::{RSplitN, SplitN}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{RSplitTerminator, SplitTerminator}; -#[unstable(feature = "utf8_chunks", issue = "99543")] +#[stable(feature = "utf8_chunks", since = "1.79.0")] pub use core::str::{Utf8Chunk, Utf8Chunks}; /// Note: `str` in `Concat` is not meaningful here. diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index c4dcff1b1c49c..7c9f13e30ffb1 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -58,8 +58,6 @@ use core::ops::{self, Range, RangeBounds}; use core::ptr; use core::slice; use core::str::pattern::Pattern; -#[cfg(not(no_global_oom_handling))] -use core::str::Utf8Chunks; #[cfg(not(no_global_oom_handling))] use crate::borrow::{Cow, ToOwned}; @@ -72,9 +70,9 @@ use crate::vec::Vec; /// A UTF-8–encoded, growable string. /// -/// The `String` type is the most common string type that has ownership over the -/// contents of the string. It has a close relationship with its borrowed -/// counterpart, the primitive [`str`]. +/// `String` is the most common string type. It has ownership over the contents +/// of the string, stored in a heap-allocated buffer (see [Representation](#representation)). +/// It is closely related to its borrowed counterpart, the primitive [`str`]. /// /// # Examples /// @@ -260,7 +258,7 @@ use crate::vec::Vec; /// # Representation /// /// A `String` is made up of three components: a pointer to some bytes, a -/// length, and a capacity. The pointer points to an internal buffer `String` +/// length, and a capacity. The pointer points to the internal buffer which `String` /// uses to store its data. The length is the number of bytes currently stored /// in the buffer, and the capacity is the size of the buffer in bytes. As such, /// the length will always be less than or equal to the capacity. @@ -633,7 +631,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn from_utf8_lossy(v: &[u8]) -> Cow<'_, str> { - let mut iter = Utf8Chunks::new(v); + let mut iter = v.utf8_chunks(); let first_valid = if let Some(chunk) = iter.next() { let valid = chunk.valid(); @@ -2097,6 +2095,10 @@ impl Clone for String { String { vec: self.vec.clone() } } + /// Clones the contents of `source` into `self`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation if possible. fn clone_from(&mut self, source: &Self) { self.vec.clone_from(&source.vec); } @@ -2479,6 +2481,9 @@ impl ops::Deref for String { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl ops::DerefPure for String {} + #[stable(feature = "derefmut_for_string", since = "1.3.0")] impl ops::DerefMut for String { #[inline] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 80f0f2acc99a2..297a273d274bf 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -21,7 +21,7 @@ use core::marker::{PhantomData, Unsize}; #[cfg(not(no_global_oom_handling))] use core::mem::size_of_val; use core::mem::{self, align_of_val_raw}; -use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; +use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::Pin; use core::ptr::{self, NonNull}; @@ -233,7 +233,7 @@ macro_rules! acquire { /// let val = Arc::clone(&val); /// /// thread::spawn(move || { -/// let v = val.fetch_add(1, Ordering::SeqCst); +/// let v = val.fetch_add(1, Ordering::Relaxed); /// println!("{v:?}"); /// }); /// } @@ -807,7 +807,10 @@ impl Arc { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn pin_in(data: T, alloc: A) -> Pin> { + pub fn pin_in(data: T, alloc: A) -> Pin> + where + A: 'static, + { unsafe { Pin::new_unchecked(Arc::new_in(data, alloc)) } } @@ -815,7 +818,10 @@ impl Arc { /// fails. #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_pin_in(data: T, alloc: A) -> Result>, AllocError> { + pub fn try_pin_in(data: T, alloc: A) -> Result>, AllocError> + where + A: 'static, + { unsafe { Ok(Pin::new_unchecked(Arc::try_new_in(data, alloc)?)) } } @@ -1062,7 +1068,9 @@ impl Arc { /// /// // Create a long list and clone it /// let mut x = LinkedList::new(); - /// for i in 0..100000 { + /// let size = 100000; + /// # let size = if cfg!(miri) { 100 } else { size }; + /// for i in 0..size { /// x.push(i); // Adds i to the front of x /// } /// let y = x.clone(); @@ -2107,6 +2115,9 @@ impl Deref for Arc { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for Arc {} + #[unstable(feature = "receiver_trait", issue = "none")] impl Receiver for Arc {} diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index b40768a52b6b4..a3fa6585a37f8 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -148,7 +148,7 @@ fn raw_waker(waker: Arc) -> RawWaker { unsafe fn clone_waker(waker: *const ()) -> RawWaker { unsafe { Arc::increment_strong_count(waker as *const W) }; RawWaker::new( - waker as *const (), + waker, &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), ) } @@ -320,7 +320,7 @@ fn local_raw_waker(waker: Rc) -> RawWaker { unsafe fn clone_waker(waker: *const ()) -> RawWaker { unsafe { Rc::increment_strong_count(waker as *const W) }; RawWaker::new( - waker as *const (), + waker, &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), ) } diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 07eb91c900597..88aa1b1b0e081 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}; @@ -229,96 +229,107 @@ where I: Iterator + InPlaceCollect, ::Source: AsVecIntoIter, { - default fn from_iter(mut iterator: I) -> Self { - // See "Layout constraints" section in the module documentation. We rely on const - // optimization here since these conditions currently cannot be expressed as trait bounds - if const { !in_place_collectible::(I::MERGE_BY, I::EXPAND_BY) } { - // fallback to more generic implementations - return SpecFromIterNested::from_iter(iterator); - } - - 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.ptr, - inner.cap, - inner.buf.as_ptr() as *mut T, - inner.end as *const T, - inner.cap * mem::size_of::() / mem::size_of::(), - ) + default fn from_iter(iterator: I) -> Self { + // Select the implementation in const eval to avoid codegen of the dead branch to improve compile times. + let fun: fn(I) -> Vec = const { + // See "Layout constraints" section in the module documentation. We use const conditions here + // since these conditions currently cannot be expressed as trait bounds + if in_place_collectible::(I::MERGE_BY, I::EXPAND_BY) { + from_iter_in_place + } else { + // fallback + SpecFromIterNested::::from_iter + } }; - // 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) }; + fun(iterator) + } +} - 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()); - // 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(), - "InPlaceIterable contract violation, write pointer advanced beyond read pointer" - ); - } +fn from_iter_in_place(mut iterator: I) -> Vec +where + I: Iterator + InPlaceCollect, + ::Source: AsVecIntoIter, +{ + 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, + inner.ptr, + inner.cap, + inner.buf.cast::(), + inner.end as *const T, + inner.cap * mem::size_of::() / mem::size_of::(), + ) + }; - // The ownership of the source allocation and the new `T` values is temporarily moved into `dst_guard`. - // This is safe because - // * `forget_allocation_drop_remaining` immediately forgets the allocation - // before any panic can occur in order to avoid any double free, and then proceeds to drop - // any remaining values at the tail of the source. - // * the shrink either panics without invalidating the allocation, aborts or - // succeeds. In the last case we disarm the guard. - // - // Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce - // contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the - // module documentation why this is ok anyway. - let dst_guard = - InPlaceDstDataSrcBufDrop { ptr: dst_buf, len, src_cap, src: PhantomData:: }; - src.forget_allocation_drop_remaining(); + // 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.as_ptr() as *mut T, dst_end) + }; - // Adjust the allocation if the source had a capacity in bytes that wasn't a multiple - // of the destination type size. - // Since the discrepancy should generally be small this should only result in some - // bookkeeping updates and no memmove. - if needs_realloc::(src_cap, dst_cap) { - let alloc = Global; - debug_assert_ne!(src_cap, 0); - debug_assert_ne!(dst_cap, 0); - unsafe { - // The old allocation exists, therefore it must have a valid layout. - let src_align = mem::align_of::(); - let src_size = mem::size_of::().unchecked_mul(src_cap); - let old_layout = Layout::from_size_align_unchecked(src_size, src_align); + 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); + // 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).cast() } <= src.ptr, + "InPlaceIterable contract violation, write pointer advanced beyond read pointer" + ); + } - // The allocation must be equal or smaller for in-place iteration to be possible - // therefore the new layout must be ≤ the old one and therefore valid. - let dst_align = mem::align_of::(); - let dst_size = mem::size_of::().unchecked_mul(dst_cap); - let new_layout = Layout::from_size_align_unchecked(dst_size, dst_align); + // The ownership of the source allocation and the new `T` values is temporarily moved into `dst_guard`. + // This is safe because + // * `forget_allocation_drop_remaining` immediately forgets the allocation + // before any panic can occur in order to avoid any double free, and then proceeds to drop + // any remaining values at the tail of the source. + // * the shrink either panics without invalidating the allocation, aborts or + // succeeds. In the last case we disarm the guard. + // + // Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce + // contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the + // module documentation why this is ok anyway. + let dst_guard = + InPlaceDstDataSrcBufDrop { ptr: dst_buf, len, src_cap, src: PhantomData:: }; + src.forget_allocation_drop_remaining(); - let result = alloc.shrink( - NonNull::new_unchecked(dst_buf as *mut u8), - old_layout, - new_layout, - ); - let Ok(reallocated) = result else { handle_alloc_error(new_layout) }; - dst_buf = reallocated.as_ptr() as *mut T; - } - } else { - debug_assert_eq!(src_cap * mem::size_of::(), dst_cap * mem::size_of::()); + // Adjust the allocation if the source had a capacity in bytes that wasn't a multiple + // of the destination type size. + // Since the discrepancy should generally be small this should only result in some + // bookkeeping updates and no memmove. + if needs_realloc::(src_cap, dst_cap) { + let alloc = Global; + debug_assert_ne!(src_cap, 0); + debug_assert_ne!(dst_cap, 0); + unsafe { + // The old allocation exists, therefore it must have a valid layout. + let src_align = mem::align_of::(); + let src_size = mem::size_of::().unchecked_mul(src_cap); + let old_layout = Layout::from_size_align_unchecked(src_size, src_align); + + // The allocation must be equal or smaller for in-place iteration to be possible + // therefore the new layout must be ≤ the old one and therefore valid. + let dst_align = mem::align_of::(); + let dst_size = mem::size_of::().unchecked_mul(dst_cap); + let new_layout = Layout::from_size_align_unchecked(dst_size, dst_align); + + let result = alloc.shrink(dst_buf.cast(), old_layout, new_layout); + let Ok(reallocated) = result else { handle_alloc_error(new_layout) }; + dst_buf = reallocated.cast::(); } + } else { + debug_assert_eq!(src_cap * mem::size_of::(), dst_cap * mem::size_of::()); + } - mem::forget(dst_guard); + 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 - } + vec } fn write_in_place_with_drop( diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs index 40a540b57fc22..4050c250130bb 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 Drop for InPlaceDrop { // the source allocation - i.e. before the reallocation happened - to avoid leaking them // if some other destructor panics. pub(super) struct InPlaceDstDataSrcBufDrop { - pub(super) ptr: *mut Dest, + pub(super) ptr: NonNull, pub(super) len: usize, pub(super) src_cap: usize, pub(super) src: PhantomData, @@ -42,8 +43,8 @@ impl Drop for InPlaceDstDataSrcBufDrop { fn drop(&mut self) { unsafe { let _drop_allocation = - RawVec::::from_raw_parts_in(self.ptr.cast::(), self.src_cap, Global); - drop_in_place(core::ptr::slice_from_raw_parts_mut::(self.ptr, self.len)); + RawVec::::from_nonnull_in(self.ptr.cast::(), self.src_cap, Global); + drop_in_place(core::ptr::slice_from_raw_parts_mut::(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 dfd42ca06193a..b0226c848332c 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 { // `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 f2f42e63d6b0b..b2e22d8715a8b 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -603,6 +603,17 @@ impl Vec { 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, length: usize, capacity: usize) -> Self { + unsafe { Self::from_nonnull_in(ptr, length, capacity, Global) } + } } impl Vec { @@ -820,6 +831,22 @@ impl Vec { 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, + length: usize, + capacity: usize, + alloc: A, + ) -> Self { + unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } } + } + /// Decomposes a `Vec` into its raw components: `(pointer, length, capacity)`. /// /// Returns the raw pointer to the underlying data, the length of @@ -1462,7 +1489,7 @@ impl Vec { /// /// The removed element is replaced by the last element of the vector. /// - /// This does not preserve ordering, but is *O*(1). + /// This does not preserve ordering of the remaining elements, but is *O*(1). /// If you need to preserve the element order, use [`remove`] instead. /// /// [`remove`]: Vec::remove @@ -1541,10 +1568,13 @@ impl Vec { } let len = self.len(); + if index > len { + assert_failed(index, len); + } // space for the new element if len == self.buf.capacity() { - self.reserve(1); + self.buf.grow_one(); } unsafe { @@ -1556,10 +1586,6 @@ impl Vec { // Shift everything over to make space. (Duplicating the // `index`th element into two consecutive places.) ptr::copy(p, p.add(1), len - index); - } else if index == len { - // No elements need shifting. - } else { - assert_failed(index, len); } // Write it in, overwriting the first copy of the `index`th // element. @@ -1965,15 +1991,17 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push_back", "put", "append")] pub fn push(&mut self, value: T) { + // Inform codegen that the length does not change across grow_one(). + let len = self.len; // This will panic or abort if we would allocate > isize::MAX bytes // or if the length increment would overflow for zero-sized types. - if self.len == self.buf.capacity() { - self.buf.reserve_for_push(self.len); + if len == self.buf.capacity() { + self.buf.grow_one(); } unsafe { - let end = self.as_mut_ptr().add(self.len); + let end = self.as_mut_ptr().add(len); ptr::write(end, value); - self.len += 1; + self.len = len + 1; } } @@ -2059,6 +2087,31 @@ impl Vec { } } + /// Removes and returns the last element in a vector if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the vector + /// is empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_pop_if)] + /// + /// let mut vec = vec![1, 2, 3, 4]; + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(vec.pop_if(pred), Some(4)); + /// assert_eq!(vec, [1, 2, 3]); + /// assert_eq!(vec.pop_if(pred), None); + /// ``` + #[unstable(feature = "vec_pop_if", issue = "122741")] + pub fn pop_if(&mut self, f: F) -> Option + where + F: FnOnce(&mut T) -> bool, + { + let last = self.last_mut()?; + if f(last) { self.pop() } else { None } + } + /// Moves all the elements of `other` into `self`, leaving `other` empty. /// /// # Panics @@ -2773,6 +2826,9 @@ impl ops::DerefMut for Vec { } } +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl ops::DerefPure for Vec {} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Vec { @@ -2792,8 +2848,30 @@ impl Clone for Vec { crate::slice::to_vec(&**self, alloc) } - fn clone_from(&mut self, other: &Self) { - crate::slice::SpecCloneIntoVec::clone_into(other.as_slice(), self); + /// Overwrites the contents of `self` with a clone of the contents of `source`. + /// + /// This method is preferred over simply assigning `source.clone()` to `self`, + /// as it avoids reallocation if possible. Additionally, if the element type + /// `T` overrides `clone_from()`, this will reuse the resources of `self`'s + /// elements as well. + /// + /// # Examples + /// + /// ``` + /// let x = vec![5, 6, 7]; + /// let mut y = vec![8, 9, 10]; + /// let yp: *const i32 = y.as_ptr(); + /// + /// y.clone_from(&x); + /// + /// // The value is the same + /// assert_eq!(x, y); + /// + /// // And no reallocation occurred + /// assert_eq!(yp, y.as_ptr()); + /// ``` + fn clone_from(&mut self, source: &Self) { + crate::slice::SpecCloneIntoVec::clone_into(source.as_slice(), self); } } diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs index 33dd4139bc08b..6646ae7bccb7a 100644 --- a/library/alloc/src/vec/spec_from_iter.rs +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -51,7 +51,7 @@ impl SpecFromIter> for Vec { 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/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index e8496989bcf12..0eae4ca4b8ba3 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -13,7 +13,6 @@ #![feature(core_intrinsics)] #![feature(extract_if)] #![feature(exact_size_is_empty)] -#![feature(generic_nonzero)] #![feature(linked_list_cursors)] #![feature(map_try_insert)] #![feature(new_uninit)] @@ -22,7 +21,6 @@ #![feature(try_reserve_kind)] #![feature(try_with_capacity)] #![feature(unboxed_closures)] -#![feature(associated_type_bounds)] #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] @@ -37,12 +35,13 @@ #![feature(const_trait_impl)] #![feature(const_str_from_utf8)] #![feature(panic_update_hook)] -#![feature(pointer_is_aligned)] +#![feature(pointer_is_aligned_to)] #![feature(slice_flatten)] #![feature(thin_box)] #![feature(strict_provenance)] #![feature(drain_keep_rest)] #![feature(local_waker)] +#![feature(vec_pop_if)] #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index aa95b4e977081..71d79893e01d7 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -2643,3 +2643,74 @@ fn test_vec_from_array_ref() { fn test_vec_from_array_mut_ref() { assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]); } + +#[test] +fn test_pop_if() { + let mut v = vec![1, 2, 3, 4]; + let pred = |x: &mut i32| *x % 2 == 0; + + assert_eq!(v.pop_if(pred), Some(4)); + assert_eq!(v, [1, 2, 3]); + + assert_eq!(v.pop_if(pred), None); + assert_eq!(v, [1, 2, 3]); +} + +#[test] +fn test_pop_if_empty() { + let mut v = Vec::::new(); + assert_eq!(v.pop_if(|_| true), None); + assert!(v.is_empty()); +} + +#[test] +fn test_pop_if_mutates() { + let mut v = vec![1]; + let pred = |x: &mut i32| { + *x += 1; + false + }; + assert_eq!(v.pop_if(pred), None); + assert_eq!(v, [2]); +} + +/// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments +/// in the stdlib. Draining and extending the allocation are fairly well-tested earlier, but +/// `vec.insert(usize::MAX, val)` once slipped by! +/// +/// All code that manipulates the collection types should be tested with "trivially wrong" args. +#[test] +fn max_dont_panic() { + let mut v = vec![0]; + let _ = v.get(usize::MAX); + v.shrink_to(usize::MAX); + v.truncate(usize::MAX); +} + +#[test] +#[should_panic] +fn max_insert() { + let mut v = vec![0]; + v.insert(usize::MAX, 1); +} + +#[test] +#[should_panic] +fn max_remove() { + let mut v = vec![0]; + v.remove(usize::MAX); +} + +#[test] +#[should_panic] +fn max_splice() { + let mut v = vec![0]; + v.splice(usize::MAX.., core::iter::once(1)); +} + +#[test] +#[should_panic] +fn max_swap_remove() { + let mut v = vec![0]; + v.swap_remove(usize::MAX); +} diff --git a/library/backtrace b/library/backtrace index 6fa4b85b9962c..e15130618237e 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit 6fa4b85b9962c3e1be8c2e5cc605cd078134152b +Subproject commit e15130618237eb3e2d4b622549f9647b4c1d9ca3 diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 3dc8c84e0bfde..a02fcf504168a 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -7,7 +7,8 @@ description = "The Rust Core Library" autotests = false autobenches = false # If you update this, be sure to update it in a bunch of other places too! -# As of 2022, it was the ci/pgo.sh script and the core-no-fp-fmt-parse test. +# As of 2024, it was src/tools/opt-dist, the core-no-fp-fmt-parse test and +# the version of the prelude imported in core/lib.rs. edition = "2021" [lib] diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs index 4d14b930e4171..32d15c386cb1b 100644 --- a/library/core/benches/lib.rs +++ b/library/core/benches/lib.rs @@ -1,5 +1,7 @@ // wasm32 does not support benches (no time). #![cfg(not(target_arch = "wasm32"))] +// Disabling in Miri as these would take too long. +#![cfg(not(miri))] #![feature(flt2dec)] #![feature(test)] #![feature(trusted_random_access)] diff --git a/library/core/benches/num/int_log/mod.rs b/library/core/benches/num/int_log/mod.rs index bb61224b5baad..3807cd5d76cfc 100644 --- a/library/core/benches/num/int_log/mod.rs +++ b/library/core/benches/num/int_log/mod.rs @@ -1,7 +1,7 @@ use rand::Rng; use test::{black_box, Bencher}; -macro_rules! int_log_bench { +macro_rules! int_log10_bench { ($t:ty, $predictable:ident, $random:ident, $random_small:ident) => { #[bench] fn $predictable(bench: &mut Bencher) { @@ -51,8 +51,75 @@ macro_rules! int_log_bench { }; } -int_log_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} -int_log_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} -int_log_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} -int_log_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} -int_log_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} +int_log10_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} +int_log10_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} +int_log10_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} +int_log10_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} +int_log10_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} + +macro_rules! int_log_bench { + ($t:ty, $random:ident, $random_small:ident, $geometric:ident) => { + #[bench] + fn $random(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS); + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $random_small(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the range 0..256. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = (rng.gen::() >> rng.gen_range(0..u8::BITS)) as $t; + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $geometric(bench: &mut Bencher) { + let bases: [$t; 16] = [2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65]; + let base_and_numbers: Vec<($t, Vec<$t>)> = bases + .iter() + .map(|&b| { + let numbers = (0..=<$t>::MAX.ilog(b)).map(|exp| b.pow(exp)).collect(); + (b, numbers) + }) + .collect(); + bench.iter(|| { + for (b, numbers) in &base_and_numbers { + for &x in numbers { + black_box(black_box(x).ilog(black_box(*b))); + } + } + }); + } + }; +} + +int_log_bench! {u8, u8_log_random, u8_log_random_small, u8_log_geometric} +int_log_bench! {u16, u16_log_random, u16_log_random_small, u16_log_geometric} +int_log_bench! {u32, u32_log_random, u32_log_random_small, u32_log_geometric} +int_log_bench! {u64, u64_log_random, u64_log_random_small, u64_log_geometric} +int_log_bench! {u128, u128_log_random, u128_log_random_small, u128_log_geometric} diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index a1fff6707bd0f..8df3ace54ffe1 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -24,10 +24,7 @@ use crate::ptr; /// use std::alloc::{GlobalAlloc, Layout}; /// use std::cell::UnsafeCell; /// use std::ptr::null_mut; -/// use std::sync::atomic::{ -/// AtomicUsize, -/// Ordering::{Acquire, SeqCst}, -/// }; +/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; /// /// const ARENA_SIZE: usize = 128 * 1024; /// const MAX_SUPPORTED_ALIGN: usize = 4096; @@ -61,7 +58,7 @@ use crate::ptr; /// let mut allocated = 0; /// if self /// .remaining -/// .fetch_update(SeqCst, SeqCst, |mut remaining| { +/// .fetch_update(Relaxed, Relaxed, |mut remaining| { /// if size > remaining { /// return None; /// } @@ -81,7 +78,7 @@ use crate::ptr; /// /// fn main() { /// let _s = format!("allocating a string!"); -/// let currently = ALLOCATOR.remaining.load(Acquire); +/// let currently = ALLOCATOR.remaining.load(Relaxed); /// println!("allocated so far: {}", ARENA_SIZE - currently); /// } /// ``` diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 2a02870e30be9..0b92767c93205 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -25,7 +25,9 @@ const fn size_align() -> (usize, usize) { /// An instance of `Layout` describes a particular layout of memory. /// You build a `Layout` up as an input to give to an allocator. /// -/// All layouts have an associated size and a power-of-two alignment. +/// All layouts have an associated size and a power-of-two alignment. The size, when rounded up to +/// the nearest multiple of `align`, does not overflow isize (i.e., the rounded value will always be +/// less than or equal to `isize::MAX`). /// /// (Note that layouts are *not* required to have non-zero size, /// even though `GlobalAlloc` requires that all memory requests diff --git a/library/core/src/any.rs b/library/core/src/any.rs index a4252d0c9e03c..37cb8e7d303af 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -40,10 +40,10 @@ //! //! ## Examples //! -//! Consider a situation where we want to log out a value passed to a function. -//! We know the value we're working on implements Debug, but we don't know its +//! Consider a situation where we want to log a value passed to a function. +//! We know the value we're working on implements `Debug`, but we don't know its //! concrete type. We want to give special treatment to certain types: in this -//! case printing out the length of String values prior to their value. +//! case printing out the length of `String` values prior to their value. //! We don't know the concrete type of our value at compile time, so we need to //! use runtime reflection instead. //! @@ -51,7 +51,7 @@ //! use std::fmt::Debug; //! use std::any::Any; //! -//! // Logger function for any type that implements Debug. +//! // Logger function for any type that implements `Debug`. //! fn log(value: &T) { //! let value_any = value as &dyn Any; //! diff --git a/library/core/src/array/equality.rs b/library/core/src/array/equality.rs index bdb6599abf549..bb668d2a67309 100644 --- a/library/core/src/array/equality.rs +++ b/library/core/src/array/equality.rs @@ -1,5 +1,4 @@ use crate::cmp::BytewiseEq; -use crate::convert::TryInto; #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq<[U; N]> for [T; N] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 8b5b48c59c29b..05874ab6c4cbb 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -6,7 +6,7 @@ use crate::borrow::{Borrow, BorrowMut}; use crate::cmp::Ordering; -use crate::convert::{Infallible, TryFrom}; +use crate::convert::Infallible; use crate::error::Error; use crate::fmt; use crate::hash::{self, Hash}; @@ -512,7 +512,8 @@ impl [T; N] { /// # Examples /// /// ``` - /// #![feature(array_try_map, generic_nonzero)] + /// #![feature(array_try_map)] + /// /// let a = ["1", "2", "3"]; /// let b = a.try_map(|v| v.parse::()).unwrap().map(|v| v + 1); /// assert_eq!(b, [2, 3, 4]); @@ -522,8 +523,10 @@ impl [T; N] { /// assert!(b.is_err()); /// /// use std::num::NonZero; + /// /// let z = [1, 2, 0, 3, 4]; /// assert_eq!(z.try_map(NonZero::new), None); + /// /// let a = [1, 2, 3]; /// let b = a.try_map(NonZero::new); /// let c = b.map(|x| x.map(NonZero::get)); diff --git a/library/core/src/async_iter/async_iter.rs b/library/core/src/async_iter/async_iter.rs index 489f95bbf101d..a0ffb6d47507b 100644 --- a/library/core/src/async_iter/async_iter.rs +++ b/library/core/src/async_iter/async_iter.rs @@ -116,6 +116,7 @@ where impl Poll> { /// A helper function for internal desugaring -- produces `Ready(Some(t))`, /// which corresponds to the async iterator yielding a value. + #[doc(hidden)] #[unstable(feature = "async_gen_internals", issue = "none")] #[lang = "AsyncGenReady"] pub fn async_gen_ready(t: T) -> Self { @@ -124,6 +125,7 @@ impl Poll> { /// A helper constant for internal desugaring -- produces `Pending`, /// which corresponds to the async iterator pending on an `.await`. + #[doc(hidden)] #[unstable(feature = "async_gen_internals", issue = "none")] #[lang = "AsyncGenPending"] // FIXME(gen_blocks): This probably could be deduplicated. @@ -131,6 +133,7 @@ impl Poll> { /// A helper constant for internal desugaring -- produces `Ready(None)`, /// which corresponds to the async iterator finishing its iteration. + #[doc(hidden)] #[unstable(feature = "async_gen_internals", issue = "none")] #[lang = "AsyncGenFinished"] pub const FINISHED: Self = Poll::Ready(None); diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 0e719e169deb6..ae0436bc5209e 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1277,11 +1277,11 @@ impl Clone for RefCell { /// # Panics /// - /// Panics if `other` is currently mutably borrowed. + /// Panics if `source` is currently mutably borrowed. #[inline] #[track_caller] - fn clone_from(&mut self, other: &Self) { - self.get_mut().clone_from(&other.borrow()) + fn clone_from(&mut self, source: &Self) { + self.get_mut().clone_from(&source.borrow()) } } @@ -2071,6 +2071,7 @@ impl UnsafeCell { /// ``` #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] + // When this is const stabilized, please remove `primitive_into_inner` below. #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] pub const fn into_inner(self) -> T { self.value @@ -2217,6 +2218,47 @@ impl, U> CoerceUnsized> for UnsafeCell {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U> DispatchFromDyn> for UnsafeCell {} +// Special cases of UnsafeCell::into_inner where T is a primitive. These are +// used by Atomic*::into_inner. +// +// The real UnsafeCell::into_inner cannot be used yet in a stable const function. +// That is blocked on a "precise drop analysis" unstable const feature. +// https://github.com/rust-lang/rust/issues/73255 +macro_rules! unsafe_cell_primitive_into_inner { + ($($primitive:ident $atomic:literal)*) => { + $( + #[cfg(target_has_atomic_load_store = $atomic)] + impl UnsafeCell<$primitive> { + pub(crate) const fn primitive_into_inner(self) -> $primitive { + self.value + } + } + )* + }; +} + +unsafe_cell_primitive_into_inner! { + i8 "8" + u8 "8" + i16 "16" + u16 "16" + i32 "32" + u32 "32" + i64 "64" + u64 "64" + i128 "128" + u128 "128" + isize "ptr" + usize "ptr" +} + +#[cfg(target_has_atomic_load_store = "ptr")] +impl UnsafeCell<*mut T> { + pub(crate) const fn primitive_into_inner(self) -> *mut T { + self.value + } +} + /// [`UnsafeCell`], but [`Sync`]. /// /// This is just an `UnsafeCell`, except it implements `Sync` diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 3877a0c48cb23..a7c3dfc982d12 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -164,6 +164,42 @@ impl OnceCell { } } + /// Gets the mutable reference of the contents of the cell, + /// initializing it with `f` if the cell was empty. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell_get_mut)] + /// + /// use std::cell::OnceCell; + /// + /// let mut cell = OnceCell::new(); + /// let value = cell.get_mut_or_init(|| 92); + /// assert_eq!(*value, 92); + /// + /// *value += 2; + /// assert_eq!(*value, 94); + /// + /// let value = cell.get_mut_or_init(|| unreachable!()); + /// assert_eq!(*value, 94); + /// ``` + #[inline] + #[unstable(feature = "once_cell_get_mut", issue = "121641")] + pub fn get_mut_or_init(&mut self, f: F) -> &mut T + where + F: FnOnce() -> T, + { + match self.get_mut_or_try_init(|| Ok::(f())) { + Ok(val) => val, + } + } + /// Gets the contents of the cell, initializing it with `f` if /// the cell was empty. If the cell was empty and `f` failed, an /// error is returned. @@ -200,16 +236,55 @@ impl OnceCell { if let Some(val) = self.get() { return Ok(val); } - /// Avoid inlining the initialization closure into the common path that fetches - /// the already initialized value - #[cold] - fn outlined_call(f: F) -> Result - where - F: FnOnce() -> Result, - { - f() + self.try_init(f) + } + + /// Gets the mutable reference of the contents of the cell, initializing + /// it with `f` if the cell was empty. If the cell was empty and `f` failed, + /// an error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell_get_mut)] + /// + /// use std::cell::OnceCell; + /// + /// let mut cell: OnceCell = OnceCell::new(); + /// + /// // Failed initializers do not change the value + /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); + /// assert!(cell.get().is_none()); + /// + /// let value = cell.get_mut_or_try_init(|| "1234".parse()); + /// assert_eq!(value, Ok(&mut 1234)); + /// *value.unwrap() += 2; + /// assert_eq!(cell.get(), Some(&1236)) + /// ``` + #[unstable(feature = "once_cell_get_mut", issue = "121641")] + pub fn get_mut_or_try_init(&mut self, f: F) -> Result<&mut T, E> + where + F: FnOnce() -> Result, + { + if self.get().is_none() { + self.try_init(f)?; } - let val = outlined_call(f)?; + Ok(self.get_mut().unwrap()) + } + + // Avoid inlining the initialization closure into the common path that fetches + // the already initialized value + #[cold] + fn try_init(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + let val = f()?; // Note that *some* forms of reentrant initialization might lead to // UB (see `reentrant_init` test). I believe that just removing this // `panic`, while keeping `try_insert` would be sound, but it seems diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 70b9e89f9ea93..f0c2636307fcf 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -1,12 +1,11 @@ //! Character conversions. use crate::char::TryFromCharError; -use crate::convert::TryFrom; use crate::error::Error; use crate::fmt; -use crate::intrinsics::assert_unsafe_precondition; use crate::mem::transmute; use crate::str::FromStr; +use crate::ub_checks::assert_unsafe_precondition; /// Converts a `u32` to a `char`. See [`char::from_u32`]. #[must_use] diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 65ae483183901..a93b94867ce4c 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -927,7 +927,7 @@ impl char { #[must_use] #[inline] pub(crate) fn is_grapheme_extended(self) -> bool { - self > '\x7f' && unicode::Grapheme_Extend(self) + unicode::Grapheme_Extend(self) } /// Returns `true` if this `char` has one of the general categories for numbers. diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 12bca0b438cc3..a860c7c6aaadc 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -42,7 +42,7 @@ use crate::ascii; use crate::error::Error; use crate::escape; use crate::fmt::{self, Write}; -use crate::iter::FusedIterator; +use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; use crate::num::NonZero; pub(crate) use self::methods::EscapeDebugExtArgs; @@ -373,176 +373,229 @@ impl fmt::Display for EscapeDebug { } } -/// Returns an iterator that yields the lowercase equivalent of a `char`. -/// -/// This `struct` is created by the [`to_lowercase`] method on [`char`]. See -/// its documentation for more. -/// -/// [`to_lowercase`]: char::to_lowercase -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone)] -pub struct ToLowercase(CaseMappingIter); +macro_rules! casemappingiter_impls { + ($(#[$attr:meta])* $ITER_NAME:ident) => { + $(#[$attr])* + #[stable(feature = "rust1", since = "1.0.0")] + #[derive(Debug, Clone)] + pub struct $ITER_NAME(CaseMappingIter); + + #[stable(feature = "rust1", since = "1.0.0")] + impl Iterator for $ITER_NAME { + type Item = char; + fn next(&mut self) -> Option { + self.0.next() + } -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ToLowercase { - type Item = char; - fn next(&mut self) -> Option { - self.0.next() - } - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } -#[stable(feature = "case_mapping_double_ended", since = "1.59.0")] -impl DoubleEndedIterator for ToLowercase { - fn next_back(&mut self) -> Option { - self.0.next_back() - } -} + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.0.fold(init, fold) + } -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ToLowercase {} + fn count(self) -> usize { + self.0.count() + } -#[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] -impl ExactSizeIterator for ToLowercase {} + fn last(self) -> Option { + self.0.last() + } -/// Returns an iterator that yields the uppercase equivalent of a `char`. -/// -/// This `struct` is created by the [`to_uppercase`] method on [`char`]. See -/// its documentation for more. -/// -/// [`to_uppercase`]: char::to_uppercase -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug, Clone)] -pub struct ToUppercase(CaseMappingIter); + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_by(n) + } -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for ToUppercase { - type Item = char; - fn next(&mut self) -> Option { - self.0.next() - } - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: just forwarding requirements to caller + unsafe { self.0.__iterator_get_unchecked(idx) } + } + } -#[stable(feature = "case_mapping_double_ended", since = "1.59.0")] -impl DoubleEndedIterator for ToUppercase { - fn next_back(&mut self) -> Option { - self.0.next_back() + #[stable(feature = "case_mapping_double_ended", since = "1.59.0")] + impl DoubleEndedIterator for $ITER_NAME { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + + fn rfold(self, init: Acc, rfold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.0.rfold(init, rfold) + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_back_by(n) + } + } + + #[stable(feature = "fused", since = "1.26.0")] + impl FusedIterator for $ITER_NAME {} + + #[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] + impl ExactSizeIterator for $ITER_NAME { + fn len(&self) -> usize { + self.0.len() + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } + } + + // SAFETY: forwards to inner `array::IntoIter` + #[unstable(feature = "trusted_len", issue = "37572")] + unsafe impl TrustedLen for $ITER_NAME {} + + // SAFETY: forwards to inner `array::IntoIter` + #[doc(hidden)] + #[unstable(feature = "std_internals", issue = "none")] + unsafe impl TrustedRandomAccessNoCoerce for $ITER_NAME { + const MAY_HAVE_SIDE_EFFECT: bool = false; + } + + // SAFETY: this iter has no subtypes/supertypes + #[doc(hidden)] + #[unstable(feature = "std_internals", issue = "none")] + unsafe impl TrustedRandomAccess for $ITER_NAME {} + + #[stable(feature = "char_struct_display", since = "1.16.0")] + impl fmt::Display for $ITER_NAME { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } + } } } -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for ToUppercase {} +casemappingiter_impls! { + /// Returns an iterator that yields the lowercase equivalent of a `char`. + /// + /// This `struct` is created by the [`to_lowercase`] method on [`char`]. See + /// its documentation for more. + /// + /// [`to_lowercase`]: char::to_lowercase + ToLowercase +} -#[stable(feature = "exact_size_case_mapping_iter", since = "1.35.0")] -impl ExactSizeIterator for ToUppercase {} +casemappingiter_impls! { + /// Returns an iterator that yields the uppercase equivalent of a `char`. + /// + /// This `struct` is created by the [`to_uppercase`] method on [`char`]. See + /// its documentation for more. + /// + /// [`to_uppercase`]: char::to_uppercase + ToUppercase +} #[derive(Debug, Clone)] -enum CaseMappingIter { - Three(char, char, char), - Two(char, char), - One(char), - Zero, -} +struct CaseMappingIter(core::array::IntoIter); impl CaseMappingIter { + #[inline] fn new(chars: [char; 3]) -> CaseMappingIter { + let mut iter = chars.into_iter(); if chars[2] == '\0' { + iter.next_back(); if chars[1] == '\0' { - CaseMappingIter::One(chars[0]) // Including if chars[0] == '\0' - } else { - CaseMappingIter::Two(chars[0], chars[1]) + iter.next_back(); + + // Deliberately don't check `chars[0]`, + // as '\0' lowercases to itself } - } else { - CaseMappingIter::Three(chars[0], chars[1], chars[2]) } + CaseMappingIter(iter) } } impl Iterator for CaseMappingIter { type Item = char; + fn next(&mut self) -> Option { - match *self { - CaseMappingIter::Three(a, b, c) => { - *self = CaseMappingIter::Two(b, c); - Some(a) - } - CaseMappingIter::Two(b, c) => { - *self = CaseMappingIter::One(c); - Some(b) - } - CaseMappingIter::One(c) => { - *self = CaseMappingIter::Zero; - Some(c) - } - CaseMappingIter::Zero => None, - } + self.0.next() } fn size_hint(&self) -> (usize, Option) { - let size = match self { - CaseMappingIter::Three(..) => 3, - CaseMappingIter::Two(..) => 2, - CaseMappingIter::One(_) => 1, - CaseMappingIter::Zero => 0, - }; - (size, Some(size)) + self.0.size_hint() + } + + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.0.fold(init, fold) + } + + fn count(self) -> usize { + self.0.count() + } + + fn last(self) -> Option { + self.0.last() + } + + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_by(n) + } + + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: just forwarding requirements to caller + unsafe { self.0.__iterator_get_unchecked(idx) } } } impl DoubleEndedIterator for CaseMappingIter { fn next_back(&mut self) -> Option { - match *self { - CaseMappingIter::Three(a, b, c) => { - *self = CaseMappingIter::Two(a, b); - Some(c) - } - CaseMappingIter::Two(b, c) => { - *self = CaseMappingIter::One(b); - Some(c) - } - CaseMappingIter::One(c) => { - *self = CaseMappingIter::Zero; - Some(c) - } - CaseMappingIter::Zero => None, - } + self.0.next_back() } -} -impl fmt::Display for CaseMappingIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - CaseMappingIter::Three(a, b, c) => { - f.write_char(a)?; - f.write_char(b)?; - f.write_char(c) - } - CaseMappingIter::Two(b, c) => { - f.write_char(b)?; - f.write_char(c) - } - CaseMappingIter::One(c) => f.write_char(c), - CaseMappingIter::Zero => Ok(()), - } + fn rfold(self, init: Acc, rfold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.0.rfold(init, rfold) + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_back_by(n) } } -#[stable(feature = "char_struct_display", since = "1.16.0")] -impl fmt::Display for ToLowercase { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) +impl ExactSizeIterator for CaseMappingIter { + fn len(&self) -> usize { + self.0.len() + } + + fn is_empty(&self) -> bool { + self.0.is_empty() } } -#[stable(feature = "char_struct_display", since = "1.16.0")] -impl fmt::Display for ToUppercase { +impl FusedIterator for CaseMappingIter {} + +// SAFETY: forwards to inner `array::IntoIter` +unsafe impl TrustedLen for CaseMappingIter {} + +// SAFETY: forwards to inner `array::IntoIter` +unsafe impl TrustedRandomAccessNoCoerce for CaseMappingIter { + const MAY_HAVE_SIDE_EFFECT: bool = false; +} + +// SAFETY: `CaseMappingIter` has no subtypes/supertypes +unsafe impl TrustedRandomAccess for CaseMappingIter {} + +impl fmt::Display for CaseMappingIter { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) + for c in self.0.clone() { + f.write_char(c)?; + } + Ok(()) } } diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index ba86334f9505c..d448c5338fc46 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -227,7 +227,7 @@ mod impls { impl_clone! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 - f32 f64 + f16 f32 f64 f128 bool char } diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index a2f07814726ac..fc6022ab75357 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -376,6 +376,10 @@ pub struct AssertParamIsEq { /// ``` #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[stable(feature = "rust1", since = "1.0.0")] +// This is a lang item only so that `BinOp::Cmp` in MIR can return it. +// It has no special behaviour, but does require that the three variants +// `Less`/`Equal`/`Greater` remain `-1_i8`/`0_i8`/`+1_i8` respectively. +#[lang = "Ordering"] #[repr(i8)] pub enum Ordering { /// An ordering where a compared value is less than another. @@ -684,8 +688,8 @@ impl Clone for Reverse { } #[inline] - fn clone_from(&mut self, other: &Self) { - self.0.clone_from(&other.0) + fn clone_from(&mut self, source: &Self) { + self.0.clone_from(&source.0) } } @@ -848,6 +852,7 @@ pub trait Ord: Eq + PartialOrd { #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] + #[rustc_diagnostic_item = "cmp_ord_max"] fn max(self, other: Self) -> Self where Self: Sized, @@ -868,6 +873,7 @@ pub trait Ord: Eq + PartialOrd { #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] + #[rustc_diagnostic_item = "cmp_ord_min"] fn min(self, other: Self) -> Self where Self: Sized, @@ -1154,6 +1160,7 @@ pub trait PartialOrd: PartialEq { /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "cmp_partialord_cmp"] fn partial_cmp(&self, other: &Rhs) -> Option; /// This method tests less than (for `self` and `other`) and is used by the `<` operator. @@ -1168,6 +1175,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "cmp_partialord_lt"] fn lt(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Less)) } @@ -1185,6 +1193,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "cmp_partialord_le"] fn le(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Less | Equal)) } @@ -1201,6 +1210,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "cmp_partialord_gt"] fn gt(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Greater)) } @@ -1218,6 +1228,7 @@ pub trait PartialOrd: PartialEq { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "cmp_partialord_ge"] fn ge(&self, other: &Rhs) -> bool { matches!(self.partial_cmp(other), Some(Greater | Equal)) } @@ -1486,7 +1497,7 @@ 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 } macro_rules! eq_impl { @@ -1539,7 +1550,7 @@ mod impls { } } - partial_ord_impl! { f32 f64 } + partial_ord_impl! { f16 f32 f64 f128 } macro_rules! ord_impl { ($($t:ty)*) => ($( @@ -1547,7 +1558,7 @@ mod impls { impl PartialOrd for $t { #[inline] fn partial_cmp(&self, other: &$t) -> Option { - Some(self.cmp(other)) + Some(crate::intrinsics::three_way_compare(*self, *other)) } #[inline(always)] fn lt(&self, other: &$t) -> bool { (*self) < (*other) } @@ -1563,11 +1574,7 @@ mod impls { impl Ord for $t { #[inline] fn cmp(&self, other: &$t) -> Ordering { - // The order here is important to generate more optimal assembly. - // See for more info. - if *self < *other { Less } - else if *self == *other { Equal } - else { Greater } + crate::intrinsics::three_way_compare(*self, *other) } } )*) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 63fd23ea9a9d3..432e55e8c9a4c 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -396,7 +396,7 @@ pub trait AsMut { /// For example, take this code: /// /// ``` -/// # #![cfg_attr(not(bootstrap), allow(non_local_definitions))] +/// # #![allow(non_local_definitions)] /// struct Wrapper(Vec); /// impl From> for Vec { /// fn from(w: Wrapper) -> Vec { diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 46a9006c14665..935ead2699a64 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -1,4 +1,3 @@ -use super::TryFrom; use crate::num::TryFromIntError; mod private { @@ -35,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 @@ -164,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 +// +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) => { @@ -323,7 +329,6 @@ impl_try_from_lower_bounded!(isize => usize); #[cfg(target_pointer_width = "16")] mod ptr_try_from_impls { use super::TryFromIntError; - use crate::convert::TryFrom; impl_try_from_upper_bounded!(usize => u8); impl_try_from_unbounded!(usize => u16, u32, u64, u128); @@ -346,7 +351,6 @@ mod ptr_try_from_impls { #[cfg(target_pointer_width = "32")] mod ptr_try_from_impls { use super::TryFromIntError; - use crate::convert::TryFrom; impl_try_from_upper_bounded!(usize => u8, u16); impl_try_from_unbounded!(usize => u32, u64, u128); @@ -372,7 +376,6 @@ mod ptr_try_from_impls { #[cfg(target_pointer_width = "64")] mod ptr_try_from_impls { use super::TryFromIntError; - use crate::convert::TryFrom; impl_try_from_upper_bounded!(usize => u8, u16, u32); impl_try_from_unbounded!(usize => u64, u128); diff --git a/library/core/src/default.rs b/library/core/src/default.rs index a1303fcd82158..4524b352ec817 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -71,6 +71,8 @@ use crate::ascii::Char as AsciiChar; /// /// You cannot use the `#[default]` attribute on non-unit or non-exhaustive variants. /// +/// The `#[default]` attribute was stabilized in Rust 1.62.0. +/// /// ## How can I implement `Default`? /// /// Provide an implementation for the `default()` method that returns the value of @@ -176,5 +178,7 @@ default_impl! { i32, 0, "Returns the default value of `0`" } default_impl! { i64, 0, "Returns the default value of `0`" } default_impl! { i128, 0, "Returns the default value of `0`" } +default_impl! { f16, 0.0f16, "Returns the default value of `0.0`" } default_impl! { f32, 0.0f32, "Returns the default value of `0.0`" } default_impl! { f64, 0.0f64, "Returns the default value of `0.0`" } +default_impl! { f128, 0.0f128, "Returns the default value of `0.0`" } diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 30debbffec1aa..aefb30463d33b 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -438,13 +438,7 @@ impl CStr { unsafe { &*(bytes as *const [u8] as *const CStr) } } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: The const and runtime versions have identical behavior - // unless the safety contract of `from_bytes_with_nul_unchecked` is - // violated, which is UB. - unsafe { - intrinsics::const_eval_select((bytes,), const_impl, rt_impl) - } + intrinsics::const_eval_select((bytes,), const_impl, rt_impl) } /// Returns the inner pointer to this C string. @@ -523,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(); @@ -536,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 = "1.79.0")] #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")] pub const fn count_bytes(&self) -> usize { self.inner.len() - 1 @@ -759,11 +751,7 @@ const unsafe fn const_strlen(ptr: *const c_char) -> usize { unsafe { strlen(s) } } - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: the two functions always provide equivalent functionality - unsafe { - intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt) - } + intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt) } /// An iterator over the bytes of a [`CStr`], without the nul terminator. diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 3627e844222ac..27dacbb23d958 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -213,7 +213,7 @@ impl fmt::Debug for c_void { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), + all(target_arch = "aarch64", target_vendor = "apple"), target_family = "wasm", target_os = "uefi", windows, @@ -241,7 +241,7 @@ pub struct VaListImpl<'f> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), + all(target_arch = "aarch64", target_vendor = "apple"), target_family = "wasm", target_os = "uefi", windows, @@ -265,7 +265,7 @@ impl<'f> fmt::Debug for VaListImpl<'f> { /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf #[cfg(all( target_arch = "aarch64", - not(any(target_os = "macos", target_os = "ios", target_os = "tvos")), + not(target_vendor = "apple"), not(target_os = "uefi"), not(windows), ))] @@ -362,10 +362,7 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - all( - target_arch = "aarch64", - any(target_os = "macos", target_os = "ios", target_os = "tvos") - ), + all(target_arch = "aarch64", target_vendor = "apple"), target_family = "wasm", target_os = "uefi", windows, @@ -379,10 +376,7 @@ pub struct VaList<'a, 'f: 'a> { target_arch = "s390x", target_arch = "x86_64" ), - any( - not(target_arch = "aarch64"), - not(any(target_os = "macos", target_os = "ios", target_os = "tvos")) - ), + any(not(target_arch = "aarch64"), not(target_vendor = "apple")), not(target_family = "wasm"), not(target_os = "uefi"), not(windows), @@ -399,7 +393,7 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), + all(target_arch = "aarch64", target_vendor = "apple"), target_family = "wasm", target_os = "uefi", windows, @@ -425,10 +419,7 @@ impl<'f> VaListImpl<'f> { target_arch = "s390x", target_arch = "x86_64" ), - any( - not(target_arch = "aarch64"), - not(any(target_os = "macos", target_os = "ios", target_os = "tvos")) - ), + any(not(target_arch = "aarch64"), not(target_vendor = "apple")), not(target_family = "wasm"), not(target_os = "uefi"), not(windows), @@ -609,3 +600,13 @@ extern "rust-intrinsic" { #[rustc_nounwind] fn va_arg(ap: &mut VaListImpl<'_>) -> T; } + +// Link the MSVC default lib +#[cfg(all(windows, target_env = "msvc"))] +#[link( + name = "/defaultlib:msvcrt", + modifiers = "+verbatim", + cfg(not(target_feature = "crt-static")) +)] +#[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] +extern "C" {} diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 3bbf5d8770bd2..7f23d3c09567c 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 4016167d05cd0..ce0643a3f5ef5 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -201,7 +201,7 @@ pub trait Write { impl SpecWriteFmt for &mut W { #[inline] default fn spec_write_fmt(mut self, args: Arguments<'_>) -> Result { - if let Some(s) = args.as_const_str() { + if let Some(s) = args.as_statically_known_str() { self.write_str(s) } else { write(&mut self, args) @@ -212,7 +212,7 @@ pub trait Write { impl SpecWriteFmt for &mut W { #[inline] fn spec_write_fmt(self, args: Arguments<'_>) -> Result { - if let Some(s) = args.as_const_str() { + if let Some(s) = args.as_statically_known_str() { self.write_str(s) } else { write(self, args) @@ -442,7 +442,7 @@ impl<'a> Arguments<'a> { /// Same as [`Arguments::as_str`], but will only return `Some(s)` if it can be determined at compile time. #[must_use] #[inline] - fn as_const_str(&self) -> Option<&'static str> { + fn as_statically_known_str(&self) -> Option<&'static str> { let s = self.as_str(); if core::intrinsics::is_val_statically_known(s.is_some()) { s } else { None } } @@ -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 { @@ -1617,7 +1623,11 @@ impl<'a> Formatter<'a> { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result { - if let Some(s) = fmt.as_const_str() { self.buf.write_str(s) } else { write(self.buf, fmt) } + if let Some(s) = fmt.as_statically_known_str() { + self.buf.write_str(s) + } else { + write(self.buf, fmt) + } } /// Flags for formatting @@ -2308,7 +2318,7 @@ impl Write for Formatter<'_> { #[inline] fn write_fmt(&mut self, args: Arguments<'_>) -> Result { - if let Some(s) = args.as_const_str() { + if let Some(s) = args.as_statically_known_str() { self.buf.write_str(s) } else { write(self.buf, args) @@ -2434,8 +2444,8 @@ impl Display for char { #[stable(feature = "rust1", since = "1.0.0")] impl Pointer for *const T { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - // Cast is needed here because `.expose_addr()` requires `T: Sized`. - pointer_fmt_inner((*self as *const ()).expose_addr(), f) + // Cast is needed here because `.expose_provenance()` requires `T: Sized`. + pointer_fmt_inner((*self as *const ()).expose_provenance(), f) } } diff --git a/library/core/src/fmt/nofloat.rs b/library/core/src/fmt/nofloat.rs index cfb94cd9de530..a36e7efcd95c7 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 5bf221b429fac..92626feabf3d7 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 placeholder 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 { - // 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/future/async_drop.rs b/library/core/src/future/async_drop.rs new file mode 100644 index 0000000000000..0eb8d7bb32899 --- /dev/null +++ b/library/core/src/future/async_drop.rs @@ -0,0 +1,271 @@ +#![unstable(feature = "async_drop", issue = "none")] + +use crate::fmt; +use crate::future::{Future, IntoFuture}; +use crate::intrinsics::discriminant_value; +use crate::marker::{DiscriminantKind, PhantomPinned}; +use crate::mem::MaybeUninit; +use crate::pin::Pin; +use crate::task::{ready, Context, Poll}; + +/// Asynchronously drops a value by running `AsyncDrop::async_drop` +/// on a value and its fields recursively. +#[unstable(feature = "async_drop", issue = "none")] +pub fn async_drop(value: T) -> AsyncDropOwning { + AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned } +} + +/// A future returned by the [`async_drop`]. +#[unstable(feature = "async_drop", issue = "none")] +pub struct AsyncDropOwning { + value: MaybeUninit, + dtor: Option>, + _pinned: PhantomPinned, +} + +#[unstable(feature = "async_drop", issue = "none")] +impl fmt::Debug for AsyncDropOwning { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AsyncDropOwning").finish_non_exhaustive() + } +} + +#[unstable(feature = "async_drop", issue = "none")] +impl Future for AsyncDropOwning { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: Self is pinned thus it is ok to store references to self + unsafe { + let this = self.get_unchecked_mut(); + let dtor = Pin::new_unchecked( + this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())), + ); + // AsyncDestuctors are idempotent so Self gets idempotency as well + dtor.poll(cx) + } + } +} + +#[lang = "async_drop_in_place"] +#[allow(unconditional_recursion)] +// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed? +unsafe fn async_drop_in_place_raw( + to_drop: *mut T, +) -> ::AsyncDestructor { + // Code here does not matter - this is replaced by the + // real async drop glue constructor by the compiler. + + // SAFETY: see comment above + unsafe { async_drop_in_place_raw(to_drop) } +} + +/// Creates the asynchronous destructor of the pointed-to value. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes. +/// +/// * `to_drop` must be properly aligned, even if `T` has size 0. +/// +/// * `to_drop` must be nonnull, even if `T` has size 0. +/// +/// * The value `to_drop` points to must be valid for async dropping, +/// which may mean it must uphold additional invariants. These +/// invariants depend on the type of the value being dropped. For +/// instance, when dropping a Box, the box's pointer to the heap must +/// be valid. +/// +/// * While `async_drop_in_place` is executing or the returned async +/// destructor is alive, the only way to access parts of `to_drop` +/// is through the `self: Pin<&mut Self>` references supplied to +/// the `AsyncDrop::async_drop` methods that `async_drop_in_place` +/// or `AsyncDropInPlace::poll` invokes. This usually means the +/// returned future stores the `to_drop` pointer and user is required +/// to guarantee that dropped value doesn't move. +/// +#[unstable(feature = "async_drop", issue = "none")] +pub unsafe fn async_drop_in_place(to_drop: *mut T) -> AsyncDropInPlace { + // SAFETY: `async_drop_in_place_raw` has the same safety requirements + unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) } +} + +/// A future returned by the [`async_drop_in_place`]. +#[unstable(feature = "async_drop", issue = "none")] +pub struct AsyncDropInPlace(::AsyncDestructor); + +#[unstable(feature = "async_drop", issue = "none")] +impl fmt::Debug for AsyncDropInPlace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AsyncDropInPlace").finish_non_exhaustive() + } +} + +#[unstable(feature = "async_drop", issue = "none")] +impl Future for AsyncDropInPlace { + type Output = (); + + #[inline(always)] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: This code simply forwards poll call to the inner future + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx) + } +} + +// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as +// with Drop impls +/// Custom code within the asynchronous destructor. +#[unstable(feature = "async_drop", issue = "none")] +#[lang = "async_drop"] +pub trait AsyncDrop { + /// A future returned by the [`AsyncDrop::async_drop`] to be part + /// of the async destructor. + #[unstable(feature = "async_drop", issue = "none")] + type Dropper<'a>: Future + where + Self: 'a; + + /// Constructs the asynchronous destructor for this type. + #[unstable(feature = "async_drop", issue = "none")] + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>; +} + +#[lang = "async_destruct"] +#[rustc_deny_explicit_impl(implement_via_object = false)] +trait AsyncDestruct { + type AsyncDestructor: Future; +} + +/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify +/// generation of the code for `async_drop_in_place_raw` +#[lang = "surface_async_drop_in_place"] +async unsafe fn surface_async_drop_in_place(ptr: *mut T) { + // SAFETY: We call this from async drop `async_drop_in_place_raw` + // which has the same safety requirements + unsafe { ::async_drop(Pin::new_unchecked(&mut *ptr)).await } +} + +/// Basically calls `Drop::drop` with pointer. Used to simplify generation +/// of the code for `async_drop_in_place_raw` +#[allow(drop_bounds)] +#[lang = "async_drop_surface_drop_in_place"] +async unsafe fn surface_drop_in_place(ptr: *mut T) { + // SAFETY: We call this from async drop `async_drop_in_place_raw` + // which has the same safety requirements + unsafe { crate::ops::fallback_surface_drop(&mut *ptr) } +} + +/// Wraps a future to continue outputing `Poll::Ready(())` once after +/// wrapped future completes by returning `Poll::Ready(())` on poll. This +/// is useful for constructing async destructors to guarantee this +/// "fuse" property +struct Fuse { + inner: Option, +} + +#[lang = "async_drop_fuse"] +fn fuse(inner: T) -> Fuse { + Fuse { inner: Some(inner) } +} + +impl Future for Fuse +where + T: Future, +{ + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: pin projection into `self.inner` + unsafe { + let this = self.get_unchecked_mut(); + if let Some(inner) = &mut this.inner { + ready!(Pin::new_unchecked(inner).poll(cx)); + this.inner = None; + } + } + Poll::Ready(()) + } +} + +/// Async destructor for arrays and slices. +#[lang = "async_drop_slice"] +async unsafe fn slice(s: *mut [T]) { + let len = s.len(); + let ptr = s.as_mut_ptr(); + for i in 0..len { + // SAFETY: we iterate over elements of `s` slice + unsafe { async_drop_in_place_raw(ptr.add(i)).await } + } +} + +/// Construct a chain of two futures, which awaits them sequentially as +/// a future. +#[lang = "async_drop_chain"] +async fn chain(first: F, last: G) +where + F: IntoFuture, + G: IntoFuture, +{ + first.await; + last.await; +} + +/// Basically a lazy version of `async_drop_in_place`. Returns a future +/// that would call `AsyncDrop::async_drop` on a first poll. +/// +/// # Safety +/// +/// Same as `async_drop_in_place` except is lazy to avoid creating +/// multiple mutable refernces. +#[lang = "async_drop_defer"] +async unsafe fn defer(to_drop: *mut T) { + // SAFETY: same safety requirements as `async_drop_in_place` + unsafe { async_drop_in_place(to_drop) }.await +} + +/// If `T`'s discriminant is equal to the stored one then awaits `M` +/// otherwise awaits the `O`. +/// +/// # Safety +/// +/// User should carefully manage returned future, since it would +/// try creating an immutable referece from `this` and get pointee's +/// discriminant. +// FIXME(zetanumbers): Send and Sync impls +#[lang = "async_drop_either"] +async unsafe fn either, M: IntoFuture, T>( + other: O, + matched: M, + this: *mut T, + discr: ::Discriminant, +) { + // SAFETY: Guaranteed by the safety section of this funtion's documentation + if unsafe { discriminant_value(&*this) } == discr { + drop(other); + matched.await + } else { + drop(matched); + other.await + } +} + +/// Used for noop async destructors. We don't use [`core::future::Ready`] +/// because it panics after its second poll, which could be potentially +/// bad if that would happen during the cleanup. +#[derive(Clone, Copy)] +struct Noop; + +#[lang = "async_drop_noop"] +fn noop() -> Noop { + Noop +} + +impl Future for Noop { + type Output = (); + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Ready(()) + } +} diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index 6dd3069034d85..f965afc8a5937 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -81,7 +81,7 @@ pub trait Future { /// An implementation of `poll` should strive to return quickly, and should /// not block. Returning quickly prevents unnecessarily clogging up /// threads or event loops. If it is known ahead of time that a call to - /// `poll` may end up taking awhile, the work should be offloaded to a + /// `poll` may end up taking a while, the work should be offloaded to a /// thread pool (or something similar) to ensure that `poll` can return /// quickly. /// diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 0f77a2d83433f..873cccc7e96fd 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -12,6 +12,7 @@ use crate::ptr::NonNull; use crate::task::Context; +mod async_drop; mod future; mod into_future; mod join; @@ -36,6 +37,9 @@ pub use ready::{ready, Ready}; #[stable(feature = "future_poll_fn", since = "1.64.0")] pub use poll_fn::{poll_fn, PollFn}; +#[unstable(feature = "async_drop", issue = "none")] +pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace}; + /// This type is needed because: /// /// a) Coroutines cannot implement `for<'a, 'b> Coroutine<&'a mut Context<'b>>`, so we need to pass diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index bfdd28a7399fd..1c93a7b28fd35 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -454,7 +454,7 @@ pub trait Hasher { /// ``` /// #![feature(hasher_prefixfree_extras)] /// # // Stubs to make the `impl` below pass the compiler - /// # #![cfg_attr(not(bootstrap), allow(non_local_definitions))] + /// # #![allow(non_local_definitions)] /// # struct MyCollection(Option); /// # impl MyCollection { /// # fn len(&self) -> usize { todo!() } @@ -752,6 +752,18 @@ pub trait BuildHasher { #[stable(since = "1.7.0", feature = "build_hasher")] pub struct BuildHasherDefault(marker::PhantomData H>); +impl BuildHasherDefault { + /// Creates a new BuildHasherDefault for Hasher `H`. + #[unstable( + feature = "build_hasher_default_const_new", + issue = "123197", + reason = "recently added" + )] + pub const fn new() -> Self { + BuildHasherDefault(marker::PhantomData) + } +} + #[stable(since = "1.9.0", feature = "core_impl_debug")] impl fmt::Debug for BuildHasherDefault { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -778,7 +790,7 @@ impl Clone for BuildHasherDefault { #[stable(since = "1.7.0", feature = "build_hasher")] impl Default for BuildHasherDefault { fn default() -> BuildHasherDefault { - BuildHasherDefault(marker::PhantomData) + Self::new() } } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index ffe059bf65cad..6e2d88c6b8337 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -4,6 +4,7 @@ //! Hints may be compile time or runtime. use crate::intrinsics; +use crate::ub_checks; /// Informs the compiler that the site which is calling this function is not /// reachable, possibly enabling further optimizations. @@ -98,7 +99,7 @@ use crate::intrinsics; #[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unreachable_unchecked() -> ! { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "hint::unreachable_unchecked must never be reached", () => false @@ -148,7 +149,7 @@ pub const unsafe fn unreachable_unchecked() -> ! { pub const unsafe fn assert_unchecked(cond: bool) { // SAFETY: The caller promised `cond` is true. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "hint::assert_unchecked must never be called when the condition is false", (cond: bool = cond) => cond, @@ -236,7 +237,7 @@ pub fn spin_loop() { crate::arch::riscv64::pause(); } - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] { // SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets. unsafe { crate::arch::aarch64::__isb(crate::arch::aarch64::SY) }; diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 86b9a39d68a67..3318a8ffa5548 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -66,6 +66,8 @@ use crate::marker::DiscriminantKind; use crate::marker::Tuple; use crate::mem::align_of; +use crate::ptr; +use crate::ub_checks; pub mod mir; pub mod simd; @@ -84,9 +86,6 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { unsafe { crate::ptr::drop_in_place(to_drop) } } -#[cfg(bootstrap)] -pub use self::r#try as catch_unwind; - extern "rust-intrinsic" { // N.B., these intrinsics take raw pointers because they mutate aliased // memory, which is not valid for either `&` or `&mut`. @@ -965,8 +964,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_assume", since = "1.77.0")] #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(bootstrap, inline)] +#[rustc_intrinsic] pub const unsafe fn assume(b: bool) { if !b { // SAFETY: the caller must guarantee the argument is never `false` @@ -987,9 +985,8 @@ pub const unsafe fn assume(b: bool) { /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_likely", issue = "none")] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] +#[rustc_intrinsic] #[rustc_nounwind] -#[cfg_attr(bootstrap, inline)] pub const fn likely(b: bool) -> bool { b } @@ -1007,9 +1004,8 @@ pub const fn likely(b: bool) -> bool { /// This intrinsic does not have a stable counterpart. #[rustc_const_unstable(feature = "const_likely", issue = "none")] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] +#[rustc_intrinsic] #[rustc_nounwind] -#[cfg_attr(bootstrap, inline)] pub const fn unlikely(b: bool) -> bool { b } @@ -1132,7 +1128,7 @@ extern "rust-intrinsic" { /// any safety invariants. /// /// Consider using [`core::panic::Location::caller`] instead. - #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] + #[rustc_const_stable(feature = "const_caller_location", since = "1.79.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn caller_location() -> &'static crate::panic::Location<'static>; @@ -1169,14 +1165,6 @@ extern "rust-intrinsic" { /// may lead to unexpected and unstable compilation results. This makes `transmute` **incredibly /// unsafe**. `transmute` should be the absolute last resort. /// - /// Transmuting pointers *to* integers in a `const` context is [undefined behavior][ub], - /// unless the pointer was originally created *from* an integer. - /// (That includes this function specifically, integer-to-pointer casts, and helpers like [`invalid`][crate::ptr::dangling], - /// but also semantically-equivalent conversions such as punning through `repr(C)` union fields.) - /// Any attempt to use the resulting value for integer operations will abort const-evaluation. - /// (And even outside `const`, such transmutation is touching on many unspecified aspects of the - /// Rust memory model and should be avoided. See below for alternatives.) - /// /// Because `transmute` is a by-value operation, alignment of the *transmuted values /// themselves* is not a concern. As with any other function, the compiler already ensures /// both `Src` and `Dst` are properly aligned. However, when transmuting values that *point @@ -1187,6 +1175,39 @@ extern "rust-intrinsic" { /// /// [ub]: ../../reference/behavior-considered-undefined.html /// + /// # Transmutation between pointers and integers + /// + /// Special care has to be taken when transmuting between pointers and integers, e.g. + /// transmuting between `*const ()` and `usize`. + /// + /// Transmuting *pointers to integers* in a `const` context is [undefined behavior][ub], unless + /// the pointer was originally created *from* an integer. (That includes this function + /// specifically, integer-to-pointer casts, and helpers like [`dangling`][crate::ptr::dangling], + /// but also semantically-equivalent conversions such as punning through `repr(C)` union + /// fields.) Any attempt to use the resulting value for integer operations will abort + /// const-evaluation. (And even outside `const`, such transmutation is touching on many + /// unspecified aspects of the Rust memory model and should be avoided. See below for + /// alternatives.) + /// + /// Transmuting *integers to pointers* is a largely unspecified operation. It is likely *not* + /// equivalent to an `as` cast. Doing non-zero-sized memory accesses with a pointer constructed + /// this way is currently considered undefined behavior. + /// + /// All this also applies when the integer is nested inside an array, tuple, struct, or enum. + /// However, `MaybeUninit` is not considered an integer type for the purpose of this + /// section. Transmuting `*const ()` to `MaybeUninit` is fine---but then calling + /// `assume_init()` on that result is considered as completing the pointer-to-integer transmute + /// and thus runs into the issues discussed above. + /// + /// In particular, doing a pointer-to-integer-to-pointer roundtrip via `transmute` is *not* a + /// lossless process. If you want to round-trip a pointer through an integer in a way that you + /// can get back the original pointer, you need to use `as` casts, or replace the integer type + /// by `MaybeUninit<$int>` (and never call `assume_init()`). If you are looking for a way to + /// store data of arbitrary type, also use `MaybeUninit` (that will also handle uninitialized + /// memory due to padding). If you specifically need to store something that is "either an + /// integer or a pointer", use `*mut ()`: integers can be converted to pointers and back without + /// any loss (via `as` casts or via `transmute`). + /// /// # Examples /// /// There are a few things that `transmute` is really useful for. @@ -1335,7 +1356,7 @@ extern "rust-intrinsic" { /// let v_clone = v_orig.clone(); /// /// // This is the suggested, safe way. - /// // It does copy the entire vector, though, into a new array. + /// // It may copy the entire vector into a new one though, but also may not. /// let v_collected = v_clone.into_iter() /// .map(Some) /// .collect::>>(); @@ -1919,7 +1940,6 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] #[rustc_safe_intrinsic] - #[cfg(not(bootstrap))] pub fn fadd_algebraic(a: T, b: T) -> T; /// Float subtraction that allows optimizations based on algebraic rules. @@ -1927,7 +1947,6 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] #[rustc_safe_intrinsic] - #[cfg(not(bootstrap))] pub fn fsub_algebraic(a: T, b: T) -> T; /// Float multiplication that allows optimizations based on algebraic rules. @@ -1935,7 +1954,6 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] #[rustc_safe_intrinsic] - #[cfg(not(bootstrap))] pub fn fmul_algebraic(a: T, b: T) -> T; /// Float division that allows optimizations based on algebraic rules. @@ -1943,7 +1961,6 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] #[rustc_safe_intrinsic] - #[cfg(not(bootstrap))] pub fn fdiv_algebraic(a: T, b: T) -> T; /// Float remainder that allows optimizations based on algebraic rules. @@ -1951,7 +1968,6 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] #[rustc_safe_intrinsic] - #[cfg(not(bootstrap))] pub fn frem_algebraic(a: T, b: T) -> T; /// Convert with LLVM’s fptoui/fptosi, which may return undef for values out of range @@ -1974,7 +1990,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_ctpop", since = "1.40.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] - pub fn ctpop(x: T) -> T; + pub fn ctpop(x: T) -> u32; /// Returns the number of leading unset bits (zeroes) in an integer type `T`. /// @@ -2015,7 +2031,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_ctlz", since = "1.40.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] - pub fn ctlz(x: T) -> T; + pub fn ctlz(x: T) -> u32; /// Like `ctlz`, but extra-unsafe as it returns `undef` when /// given an `x` with value `0`. @@ -2036,7 +2052,7 @@ extern "rust-intrinsic" { /// ``` #[rustc_const_stable(feature = "constctlz", since = "1.50.0")] #[rustc_nounwind] - pub fn ctlz_nonzero(x: T) -> T; + pub fn ctlz_nonzero(x: T) -> u32; /// Returns the number of trailing unset bits (zeroes) in an integer type `T`. /// @@ -2077,7 +2093,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_cttz", since = "1.40.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] - pub fn cttz(x: T) -> T; + pub fn cttz(x: T) -> u32; /// Like `cttz`, but extra-unsafe as it returns `undef` when /// given an `x` with value `0`. @@ -2098,7 +2114,7 @@ extern "rust-intrinsic" { /// ``` #[rustc_const_stable(feature = "const_cttz_nonzero", since = "1.53.0")] #[rustc_nounwind] - pub fn cttz_nonzero(x: T) -> T; + pub fn cttz_nonzero(x: T) -> u32; /// Reverses the bytes in an integer type `T`. /// @@ -2130,6 +2146,17 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn bitreverse(x: T) -> T; + /// Does a three-way comparison between the two integer arguments. + /// + /// This is included as an intrinsic as it's useful to let it be one thing + /// in MIR, rather than the multiple checks and switches that make its IR + /// large and difficult to optimize. + /// + /// The stabilized version of this intrinsic is [`Ord::cmp`]. + #[rustc_const_unstable(feature = "const_three_way_compare", issue = "none")] + #[rustc_safe_intrinsic] + pub fn three_way_compare(lhs: T, rhs: T) -> crate::cmp::Ordering; + /// Performs checked integer addition. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2210,7 +2237,7 @@ extern "rust-intrinsic" { /// [`u32::checked_shl`] #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] #[rustc_nounwind] - pub fn unchecked_shl(x: T, y: T) -> T; + pub fn unchecked_shl(x: T, y: U) -> T; /// Performs an unchecked right shift, resulting in undefined behavior when /// `y < 0` or `y >= N`, where N is the width of T in bits. /// @@ -2219,29 +2246,32 @@ extern "rust-intrinsic" { /// [`u32::checked_shr`] #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] #[rustc_nounwind] - pub fn unchecked_shr(x: T, y: T) -> T; + pub fn unchecked_shr(x: T, y: U) -> T; /// Returns the result of an unchecked addition, resulting in /// undefined behavior when `x + y > T::MAX` or `x + y < T::MIN`. /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + /// The stable counterpart of this intrinsic is `unchecked_add` on the various + /// integer types, such as [`u16::unchecked_add`] and [`i64::unchecked_add`]. + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[rustc_nounwind] pub fn unchecked_add(x: T, y: T) -> T; /// Returns the result of an unchecked subtraction, resulting in /// undefined behavior when `x - y > T::MAX` or `x - y < T::MIN`. /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + /// The stable counterpart of this intrinsic is `unchecked_sub` on the various + /// integer types, such as [`u16::unchecked_sub`] and [`i64::unchecked_sub`]. + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[rustc_nounwind] pub fn unchecked_sub(x: T, y: T) -> T; /// Returns the result of an unchecked multiplication, resulting in /// undefined behavior when `x * y > T::MAX` or `x * y < T::MIN`. /// - /// This intrinsic does not have a stable counterpart. - #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] + /// The stable counterpart of this intrinsic is `unchecked_mul` on the various + /// integer types, such as [`u16::unchecked_mul`] and [`i64::unchecked_mul`]. + #[rustc_const_stable(feature = "unchecked_math", since = "1.79.0")] #[rustc_nounwind] pub fn unchecked_mul(x: T, y: T) -> T; @@ -2258,7 +2288,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] - pub fn rotate_left(x: T, y: T) -> T; + pub fn rotate_left(x: T, shift: u32) -> T; /// Performs rotate right. /// @@ -2273,7 +2303,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")] #[rustc_safe_intrinsic] #[rustc_nounwind] - pub fn rotate_right(x: T, y: T) -> T; + pub fn rotate_right(x: T, shift: u32) -> T; /// Returns (a + b) mod 2N, where N is the width of T in bits. /// @@ -2407,14 +2437,8 @@ extern "rust-intrinsic" { /// /// The stable version of this intrinsic is `std::panic::catch_unwind`. #[rustc_nounwind] - #[cfg(not(bootstrap))] pub fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32; - /// For bootstrap only, see `catch_unwind`. - #[rustc_nounwind] - #[cfg(bootstrap)] - pub fn r#try(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32; - /// Emits a `!nontemporal` store according to LLVM (see their docs). /// Probably will never become stable. /// @@ -2433,21 +2457,23 @@ extern "rust-intrinsic" { #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")] #[rustc_nounwind] pub fn ptr_offset_from_unsigned(ptr: *const T, base: *const T) -> usize; +} - /// See documentation of `<*const T>::guaranteed_eq` for details. - /// Returns `2` if the result is unknown. - /// Returns `1` if the pointers are guaranteed equal - /// Returns `0` if the pointers are guaranteed inequal - /// - /// Note that, unlike most intrinsics, this is safe to call; - /// it does not require an `unsafe` block. - /// Therefore, implementations must not require the user to uphold - /// any safety invariants. - #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8; +/// See documentation of `<*const T>::guaranteed_eq` for details. +/// Returns `2` if the result is unknown. +/// Returns `1` if the pointers are guaranteed equal +/// Returns `0` if the pointers are guaranteed inequal +#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[rustc_do_not_const_check] +#[inline] +pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { + (ptr == other) as u8 +} +extern "rust-intrinsic" { /// Determines whether the raw bytes of the two values are equal. /// /// This is particularly handy for arrays, since it allows things like just @@ -2498,82 +2524,74 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn black_box(dummy: T) -> T; +} - #[rustc_nounwind] - #[cfg(bootstrap)] - pub fn vtable_size(ptr: *const ()) -> usize; - - /// `ptr` must point to a vtable. - /// The intrinsic will return the alignment stored in that vtable. - #[rustc_nounwind] - pub fn vtable_align(ptr: *const ()) -> usize; - - /// Selects which function to call depending on the context. - /// - /// If this function is evaluated at compile-time, then a call to this - /// intrinsic will be replaced with a call to `called_in_const`. It gets - /// replaced with a call to `called_at_rt` otherwise. - /// - /// This function is safe to call, but note the stability concerns below. - /// - /// # Type Requirements - /// - /// The two functions must be both function items. They cannot be function - /// pointers or closures. The first function must be a `const fn`. - /// - /// `arg` will be the tupled arguments that will be passed to either one of - /// the two functions, therefore, both functions must accept the same type of - /// arguments. Both functions must return RET. - /// - /// # Stability concerns - /// - /// Rust has not yet decided that `const fn` are allowed to tell whether - /// they run at compile-time or at runtime. Therefore, when using this - /// intrinsic anywhere that can be reached from stable, it is crucial that - /// the end-to-end behavior of the stable `const fn` is the same for both - /// modes of execution. (Here, Undefined Behavior is considered "the same" - /// as any other behavior, so if the function exhibits UB at runtime then - /// it may do whatever it wants at compile-time.) - /// - /// Here is an example of how this could cause a problem: - /// ```no_run - /// #![feature(const_eval_select)] - /// #![feature(core_intrinsics)] - /// # #![allow(internal_features)] - /// # #![cfg_attr(bootstrap, allow(unused))] - /// use std::intrinsics::const_eval_select; - /// - /// // Standard library - /// # #[cfg(not(bootstrap))] - /// pub const fn inconsistent() -> i32 { - /// fn runtime() -> i32 { 1 } - /// const fn compiletime() -> i32 { 2 } - /// - // // ⚠ This code violates the required equivalence of `compiletime` - /// // and `runtime`. - /// const_eval_select((), compiletime, runtime) - /// } - /// # #[cfg(bootstrap)] - /// # pub const fn inconsistent() -> i32 { 0 } - /// - /// // User Crate - /// const X: i32 = inconsistent(); - /// let x = inconsistent(); - /// assert_eq!(x, X); - /// ``` - /// - /// Currently such an assertion would always succeed; until Rust decides - /// otherwise, that principle should not be violated. - #[rustc_const_unstable(feature = "const_eval_select", issue = "none")] - #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)] - pub fn const_eval_select( - arg: ARG, - called_in_const: F, - called_at_rt: G, - ) -> RET - where - G: FnOnce, - F: FnOnce; +/// Selects which function to call depending on the context. +/// +/// If this function is evaluated at compile-time, then a call to this +/// intrinsic will be replaced with a call to `called_in_const`. It gets +/// replaced with a call to `called_at_rt` otherwise. +/// +/// This function is safe to call, but note the stability concerns below. +/// +/// # Type Requirements +/// +/// The two functions must be both function items. They cannot be function +/// pointers or closures. The first function must be a `const fn`. +/// +/// `arg` will be the tupled arguments that will be passed to either one of +/// the two functions, therefore, both functions must accept the same type of +/// arguments. Both functions must return RET. +/// +/// # Stability concerns +/// +/// Rust has not yet decided that `const fn` are allowed to tell whether +/// they run at compile-time or at runtime. Therefore, when using this +/// intrinsic anywhere that can be reached from stable, it is crucial that +/// the end-to-end behavior of the stable `const fn` is the same for both +/// modes of execution. (Here, Undefined Behavior is considered "the same" +/// as any other behavior, so if the function exhibits UB at runtime then +/// it may do whatever it wants at compile-time.) +/// +/// Here is an example of how this could cause a problem: +/// ```no_run +/// #![feature(const_eval_select)] +/// #![feature(core_intrinsics)] +/// # #![allow(internal_features)] +/// use std::intrinsics::const_eval_select; +/// +/// // Standard library +/// pub const fn inconsistent() -> i32 { +/// fn runtime() -> i32 { 1 } +/// const fn compiletime() -> i32 { 2 } +/// +// // ⚠ This code violates the required equivalence of `compiletime` +/// // and `runtime`. +/// const_eval_select((), compiletime, runtime) +/// } +/// +/// // User Crate +/// const X: i32 = inconsistent(); +/// let x = inconsistent(); +/// assert_eq!(x, X); +/// ``` +/// +/// Currently such an assertion would always succeed; until Rust decides +/// otherwise, that principle should not be violated. +#[rustc_const_unstable(feature = "const_eval_select", issue = "none")] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn const_eval_select( + _arg: ARG, + _called_in_const: F, + _called_at_rt: G, +) -> RET +where + G: FnOnce, + F: FnOnce, +{ + unreachable!() } /// Returns whether the argument's value is statically known at @@ -2615,51 +2633,92 @@ extern "rust-intrinsic" { /// particular value, ever. However, the compiler will generally make it /// return `true` only if the value of the argument is actually known. /// -/// When calling this in a `const fn`, both paths must be semantically -/// equivalent, that is, the result of the `true` branch and the `false` -/// branch must return the same value and have the same side-effects *no -/// matter what*. +/// # Stability concerns +/// +/// While it is safe to call, this intrinsic may behave differently in +/// a `const` context than otherwise. See the [`const_eval_select`] +/// documentation for an explanation of the issues this can cause. Unlike +/// `const_eval_select`, this intrinsic isn't guaranteed to behave +/// deterministically even in a `const` context. +/// +/// # Type Requirements +/// +/// `T` must be either a `bool`, a `char`, a primitive numeric type (e.g. `f32`, +/// but not `NonZeroISize`), or any thin pointer (e.g. `*mut String`). +/// Any other argument types *may* cause a compiler error. +/// +/// ## Pointers +/// +/// When the input is a pointer, only the pointer itself is +/// ever considered. The pointee has no effect. Currently, these functions +/// behave identically: +/// +/// ``` +/// #![feature(is_val_statically_known)] +/// #![feature(core_intrinsics)] +/// # #![allow(internal_features)] +/// #![feature(strict_provenance)] +/// use std::intrinsics::is_val_statically_known; +/// +/// fn foo(x: &i32) -> bool { +/// is_val_statically_known(x) +/// } +/// +/// fn bar(x: &i32) -> bool { +/// is_val_statically_known( +/// (x as *const i32).addr() +/// ) +/// } +/// # _ = foo(&5_i32); +/// # _ = bar(&5_i32); +/// ``` #[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")] #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(bootstrap, inline)] +#[rustc_intrinsic] pub const fn is_val_statically_known(_arg: T) -> bool { false } -/// Returns whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)` -/// during monomorphization. +/// Non-overlapping *typed* swap of a single value. /// -/// This intrinsic is evaluated after monomorphization, and therefore branching on this value can -/// be used to implement debug assertions that are included in the precompiled standard library, -/// but can be optimized out by builds that monomorphize the standard library code with debug -/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`]. +/// The codegen backends will replace this with a better implementation when +/// `T` is a simple type that can be loaded and stored as an immediate. /// -/// We have separate intrinsics for library UB and language UB because checkers like the const-eval -/// interpreter and Miri already implement checks for language UB. Since such checkers do not know -/// about library preconditions, checks guarded by this intrinsic let them find more UB. -#[rustc_const_unstable(feature = "ub_checks", issue = "none")] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[inline(always)] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -pub(crate) const fn check_library_ub() -> bool { - cfg!(debug_assertions) -} - -/// Returns whether we should check for language UB. This evaluate to the value of `cfg!(debug_assertions)` -/// during monomorphization. +/// The stabilized form of this intrinsic is [`crate::mem::swap`]. /// -/// Since checks implemented at the source level must come strictly before the operation that -/// executes UB, if we enabled language UB checks in const-eval/Miri we would miss out on the -/// interpreter's improved diagnostics for the cases that our source-level checks catch. +/// # Safety /// -/// See `check_library_ub` for more information. -#[rustc_const_unstable(feature = "ub_checks", issue = "none")] +/// `x` and `y` are readable and writable as `T`, and non-overlapping. +#[rustc_nounwind] +#[inline] +#[rustc_intrinsic] +// This has fallback `const fn` MIR, so shouldn't need stability, see #122652 +#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")] +pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { + // SAFETY: The caller provided single non-overlapping items behind + // pointers, so swapping them with `count: 1` is fine. + unsafe { ptr::swap_nonoverlapping(x, y, 1) }; +} + +/// Returns whether we should perform some UB-checking at runtime. This eventually evaluates to +/// `cfg!(ub_checks)`, but behaves different from `cfg!` when mixing crates built with different +/// flags: if the crate has UB checks enabled or carries the `#[rustc_preserve_ub_checks]` +/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into +/// a crate that does not delay evaluation further); otherwise it can happen any time. +/// +/// The common case here is a user program built with ub_checks linked against the distributed +/// sysroot which is built without ub_checks but with `#[rustc_preserve_ub_checks]`. +/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with +/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(ub_checks)` means that +/// assertions are enabled whenever the *user crate* has UB checks enabled. However if the +/// user has UB checks disabled, the checks will still get optimized out. This intrinsic is +/// primarily used by [`ub_checks::assert_unsafe_precondition`]. +#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] #[unstable(feature = "core_intrinsics", issue = "none")] #[inline(always)] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -pub(crate) const fn check_language_ub() -> bool { +#[rustc_intrinsic] +pub const fn ub_checks() -> bool { cfg!(debug_assertions) } @@ -2674,8 +2733,7 @@ pub(crate) const fn check_language_ub() -> bool { #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(bootstrap, inline)] +#[rustc_intrinsic] pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { // const eval overrides this function, but runtime code should always just return null pointers. crate::ptr::null_mut() @@ -2694,180 +2752,61 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(bootstrap, inline)] +#[rustc_intrinsic] pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} /// `ptr` must point to a vtable. /// The intrinsic will return the size stored in that vtable. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(not(bootstrap), rustc_intrinsic_must_be_overridden)] -#[cfg(not(bootstrap))] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] pub unsafe fn vtable_size(_ptr: *const ()) -> usize { unreachable!() } -/// Retag a box pointer as part of casting it to a raw pointer. This is the `Box` equivalent of -/// `(x: &mut T) as *mut T`. The input pointer must be the pointer of a `Box` (passed as raw pointer -/// to avoid all questions around move semantics and custom allocators), and `A` must be the `Box`'s -/// allocator. -#[unstable(feature = "core_intrinsics", issue = "none")] +/// `ptr` must point to a vtable. +/// The intrinsic will return the alignment stored in that vtable. #[rustc_nounwind] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(bootstrap, inline)] -pub unsafe fn retag_box_to_raw(ptr: *mut T) -> *mut T { - // Miri needs to adjust the provenance, but for regular codegen this is not needed - ptr -} - -// Some functions are defined here because they accidentally got made -// available in this module on stable. See . -// (`transmute` also falls into this category, but it cannot be wrapped due to the -// check that `T` and `U` have the same size.) - -/// Check that the preconditions of an unsafe function are followed. The check is enabled at -/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri -/// checks implemented with this macro for language UB are always ignored. -/// -/// This macro should be called as -/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` -/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all -/// those arguments are passed to a function with the body `check_expr`. -/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB -/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation -/// of a documented library precondition that does not *immediately* lead to language UB. -/// -/// If `check_library_ub` is used but the check is actually guarding language UB, the check will -/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice -/// diagnostic, but our ability to detect UB is unchanged. -/// But if `check_language_ub` is used when the check is actually for library UB, the check is -/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the -/// library UB, the backtrace Miri reports may be far removed from original cause. -/// -/// These checks are behind a condition which is evaluated at codegen time, not expansion time like -/// [`debug_assert`]. This means that a standard library built with optimizations and debug -/// assertions disabled will have these checks optimized out of its monomorphizations, but if a -/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of -/// this macro, that monomorphization will contain the check. -/// -/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and -/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to -/// this structure: -/// ```ignore (pseudocode) -/// if ::core::intrinsics::check_language_ub() { -/// precondition_check(args) -/// } -/// ``` -/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and -/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is -/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in -/// MIR, but *can* be inlined and fully optimized by a codegen backend. -/// -/// Callers should avoid introducing any other `let` bindings or any code outside this macro in -/// order to call it. Since the precompiled standard library is built with full debuginfo and these -/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough -/// debuginfo to have a measurable compile-time impact on debug builds. -#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn -macro_rules! assert_unsafe_precondition { - ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { - { - // #[cfg(bootstrap)] (this comment) - // When the standard library is compiled with debug assertions, we want the check to inline for better performance. - // This is important when working on the compiler, which is compiled with debug assertions locally. - // When not compiled with debug assertions (so the precompiled std) we outline the check to minimize the compile - // time impact when debug assertions are disabled. - // The proper solution to this is the `#[rustc_no_mir_inline]` below, but we still want decent performance for cfg(bootstrap). - #[cfg_attr(all(debug_assertions, bootstrap), inline(always))] - #[cfg_attr(all(not(debug_assertions), bootstrap), inline(never))] - - // This check is inlineable, but not by the MIR inliner. - // The reason for this is that the MIR inliner is in an exceptionally bad position - // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`, - // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and - // would be bad for compile times. - // - // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without - // inlining the check. If it's `true`, it can inline it and get significantly better performance. - #[cfg_attr(not(bootstrap), rustc_no_mir_inline)] - #[cfg_attr(not(bootstrap), inline)] - #[rustc_nounwind] - #[rustc_const_unstable(feature = "ub_checks", issue = "none")] - const fn precondition_check($($name:$ty),*) { - if !$e { - ::core::panicking::panic_nounwind( - concat!("unsafe precondition(s) violated: ", $message) - ); - } - } - - if ::core::intrinsics::$kind() { - precondition_check($($arg,)*); - } - } - }; +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub unsafe fn vtable_align(_ptr: *const ()) -> usize { + unreachable!() } -pub(crate) use assert_unsafe_precondition; -/// Checks whether `ptr` is properly aligned with respect to -/// `align_of::()`. +/// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// -/// In `const` this is approximate and can fail spuriously. It is primarily intended -/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the -/// check is anyway not executed in `const`. -#[inline] -pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { - !ptr.is_null() && ptr.is_aligned_to(align) +/// This is used to implement functions like `slice::from_raw_parts_mut` and +/// `ptr::from_raw_parts` in a way compatible with the compiler being able to +/// change the possible layouts of pointers. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn aggregate_raw_ptr, D, M>(_data: D, _meta: M) -> P { + // To implement a fallback we'd have to assume the layout of the pointer, + // but the whole point of this intrinsic is that we shouldn't do that. + unreachable!() } -#[inline] -pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { - let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size }; - len <= max_len +#[unstable(feature = "core_intrinsics", issue = "none")] +pub trait AggregateRawPtr { + type Metadata: Copy; } - -/// Checks whether the regions of memory starting at `src` and `dst` of size -/// `count * size` do *not* overlap. -/// -/// Note that in const-eval this function just returns `true` and therefore must -/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`. -#[inline] -pub(crate) const fn is_nonoverlapping( - src: *const (), - dst: *const (), - size: usize, - count: usize, -) -> bool { - #[inline] - fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool { - let src_usize = src.addr(); - let dst_usize = dst.addr(); - let Some(size) = size.checked_mul(count) else { - crate::panicking::panic_nounwind( - "is_nonoverlapping: `size_of::() * count` overflows a usize", - ) - }; - let diff = src_usize.abs_diff(dst_usize); - // If the absolute distance between the ptrs is at least as big as the size of the buffer, - // they do not overlap. - diff >= size - } - - #[inline] - const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool { - true - } - - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // on bootstrap bump, remove unsafe block - // SAFETY: This function's precondition is equivalent to that of `const_eval_select`. - // Programs which do not execute UB will only see this function return `true`, which makes the - // const and runtime implementation indistinguishable. - unsafe { - const_eval_select((src, dst, size, count), comptime, runtime) - } +impl AggregateRawPtr<*const T> for *const P { + type Metadata =