diff options
| author | Ben Kimock <kimockb@gmail.com> | 2024-11-15 06:29:30 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-15 06:29:30 +0000 |
| commit | 3e71ed157037d9f77ff5ca73b015fe14e2b48792 (patch) | |
| tree | 3efe043e15a6bd9518d8d833eaac2c8f73649f39 /compiler | |
| parent | 7213a278eb67190fbceac4b4d948906ab49ff635 (diff) | |
| parent | b9116571ff44029f457bf6a60c7866fb28267a83 (diff) | |
Merge pull request #4034 from rust-lang/rustup-2024-11-15
Automatic Rustup
Diffstat (limited to 'compiler')
160 files changed, 4596 insertions, 2904 deletions
diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index a2fc9d5c408..f85c30ac7ec 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -31,5 +31,4 @@ jemalloc = ['dep:jemalloc-sys'] llvm = ['rustc_driver_impl/llvm'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] -rustc_use_parallel_compiler = ['rustc_driver_impl/rustc_use_parallel_compiler'] # tidy-alphabetical-end diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 997c44cffff..5f71fb97d76 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1194,7 +1194,7 @@ impl Expr { /// /// Does not ensure that the path resolves to a const param, the caller should check this. pub fn is_potential_trivial_const_arg(&self, strip_identity_block: bool) -> bool { - let this = if strip_identity_block { self.maybe_unwrap_block().1 } else { self }; + let this = if strip_identity_block { self.maybe_unwrap_block() } else { self }; if let ExprKind::Path(None, path) = &this.kind && path.is_potential_trivial_const_arg() @@ -1206,14 +1206,41 @@ impl Expr { } /// Returns an expression with (when possible) *one* outter brace removed - pub fn maybe_unwrap_block(&self) -> (bool, &Expr) { + pub fn maybe_unwrap_block(&self) -> &Expr { if let ExprKind::Block(block, None) = &self.kind && let [stmt] = block.stmts.as_slice() && let StmtKind::Expr(expr) = &stmt.kind { - (true, expr) + expr } else { - (false, self) + self + } + } + + /// Determines whether this expression is a macro call optionally wrapped in braces . If + /// `already_stripped_block` is set then we do not attempt to peel off a layer of braces. + /// + /// Returns the [`NodeId`] of the macro call and whether a layer of braces has been peeled + /// either before, or part of, this function. + pub fn optionally_braced_mac_call( + &self, + already_stripped_block: bool, + ) -> Option<(bool, NodeId)> { + match &self.kind { + ExprKind::Block(block, None) + if let [stmt] = &*block.stmts + && !already_stripped_block => + { + match &stmt.kind { + StmtKind::MacCall(_) => Some((true, stmt.id)), + StmtKind::Expr(expr) if let ExprKind::MacCall(_) = &expr.kind => { + Some((true, expr.id)) + } + _ => None, + } + } + ExprKind::MacCall(_) => Some((already_stripped_block, self.id)), + _ => None, } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index d5c2bc1c7f6..0b4bfc0b36a 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -38,7 +38,6 @@ pub enum TokenTree { } // Ensure all fields of `TokenTree` are `DynSend` and `DynSync`. -#[cfg(parallel_compiler)] fn _dummy() where Token: sync::DynSend + sync::DynSync, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2f8115441de..58497d70a24 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -200,8 +200,8 @@ pub trait Visitor<'ast>: Sized { fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) -> Self::Result { walk_param_bound(self, bounds) } - fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) { - walk_precise_capturing_arg(self, arg); + fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) -> Self::Result { + walk_precise_capturing_arg(self, arg) } fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) -> Self::Result { walk_poly_trait_ref(self, t) @@ -730,14 +730,10 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB pub fn walk_precise_capturing_arg<'a, V: Visitor<'a>>( visitor: &mut V, arg: &'a PreciseCapturingArg, -) { +) -> V::Result { match arg { - PreciseCapturingArg::Lifetime(lt) => { - visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg); - } - PreciseCapturingArg::Arg(path, id) => { - visitor.visit_path(path, *id); - } + PreciseCapturingArg::Lifetime(lt) => visitor.visit_lifetime(lt, LifetimeCtxt::GenericArg), + PreciseCapturingArg::Arg(path, id) => visitor.visit_path(path, *id), } } diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 3acca94a54b..215e6d84d0f 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -45,6 +45,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | asm::InlineAsmArch::X86_64 | asm::InlineAsmArch::Arm | asm::InlineAsmArch::AArch64 + | asm::InlineAsmArch::Arm64EC | asm::InlineAsmArch::RiscV32 | asm::InlineAsmArch::RiscV64 | asm::InlineAsmArch::LoongArch64 diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index d646150a620..be6f2c152a4 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -204,6 +204,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { "meant for internal use only" { keyword => rustdoc_internals fake_variadic => rustdoc_internals + search_unbox => rustdoc_internals } ); } @@ -522,9 +523,18 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "consider removing `for<...>`" ); gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); - for &span in spans.get(&sym::yield_expr).iter().copied().flatten() { - if !span.at_least_rust_2024() { - gate!(&visitor, coroutines, span, "yield syntax is experimental"); + // yield can be enabled either by `coroutines` or `gen_blocks` + if let Some(spans) = spans.get(&sym::yield_expr) { + for span in spans { + if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines)) + && (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks)) + { + #[allow(rustc::untranslatable_diagnostic)] + // Don't know which of the two features to include in the + // error message, so I am arbitrarily picking one. + feature_err(&visitor.sess, sym::coroutines, *span, "yield syntax is experimental") + .emit(); + } } } gate_all!(gen_blocks, "gen blocks are experimental"); diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 2753ac529d1..94f9727eb7f 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -16,9 +16,9 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; use rustc_session::parse::feature_err; use rustc_session::{RustcVersion, Session}; +use rustc_span::Span; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; use crate::fluent_generated; use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; @@ -92,9 +92,7 @@ impl Stability { #[derive(HashStable_Generic)] pub struct ConstStability { pub level: StabilityLevel, - /// This can be `None` for functions that do not have an explicit const feature. - /// We still track them for recursive const stability checks. - pub feature: Option<Symbol>, + pub feature: Symbol, /// This is true iff the `const_stable_indirect` attribute is present. pub const_stable_indirect: bool, /// whether the function has a `#[rustc_promotable]` attribute @@ -272,22 +270,19 @@ pub fn find_stability( /// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable` /// attributes in `attrs`. Returns `None` if no stability attributes are found. -/// -/// `is_const_fn` indicates whether this is a function marked as `const`. pub fn find_const_stability( sess: &Session, attrs: &[Attribute], item_sp: Span, - is_const_fn: bool, ) -> Option<(ConstStability, Span)> { let mut const_stab: Option<(ConstStability, Span)> = None; let mut promotable = false; - let mut const_stable_indirect = None; + let mut const_stable_indirect = false; for attr in attrs { match attr.name_or_empty() { sym::rustc_promotable => promotable = true, - sym::rustc_const_stable_indirect => const_stable_indirect = Some(attr.span), + sym::rustc_const_stable_indirect => const_stable_indirect = true, sym::rustc_const_unstable => { if const_stab.is_some() { sess.dcx() @@ -299,7 +294,7 @@ pub fn find_const_stability( const_stab = Some(( ConstStability { level, - feature: Some(feature), + feature, const_stable_indirect: false, promotable: false, }, @@ -317,7 +312,7 @@ pub fn find_const_stability( const_stab = Some(( ConstStability { level, - feature: Some(feature), + feature, const_stable_indirect: false, promotable: false, }, @@ -340,7 +335,7 @@ pub fn find_const_stability( } } } - if const_stable_indirect.is_some() { + if const_stable_indirect { match &mut const_stab { Some((stab, _)) => { if stab.is_const_unstable() { @@ -351,36 +346,37 @@ pub fn find_const_stability( }) } } - _ => {} + _ => { + // This function has no const stability attribute, but has `const_stable_indirect`. + // We ignore that; unmarked functions are subject to recursive const stability + // checks by default so we do carry out the user's intent. + } } } - // Make sure if `const_stable_indirect` is present, that is recorded. Also make sure all `const - // fn` get *some* marker, since we are a staged_api crate and therefore will do recursive const - // stability checks for them. We need to do this because the default for whether an unmarked - // function enforces recursive stability differs between staged-api crates and force-unmarked - // crates: in force-unmarked crates, only functions *explicitly* marked `const_stable_indirect` - // enforce recursive stability. Therefore when `lookup_const_stability` is `None`, we have to - // assume the function does not have recursive stability. All functions that *do* have recursive - // stability must explicitly record this, and so that's what we do for all `const fn` in a - // staged_api crate. - if (is_const_fn || const_stable_indirect.is_some()) && const_stab.is_none() { - let c = ConstStability { - feature: None, - const_stable_indirect: const_stable_indirect.is_some(), - promotable: false, - level: StabilityLevel::Unstable { - reason: UnstableReason::Default, - issue: None, - is_soft: false, - implied_by: None, - }, - }; - const_stab = Some((c, const_stable_indirect.unwrap_or(DUMMY_SP))); - } const_stab } +/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate +/// without the `staged_api` feature. +pub fn unmarked_crate_const_stab( + _sess: &Session, + attrs: &[Attribute], + regular_stab: Stability, +) -> ConstStability { + assert!(regular_stab.level.is_unstable()); + // The only attribute that matters here is `rustc_const_stable_indirect`. + // We enforce recursive const stability rules for those functions. + let const_stable_indirect = + attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect); + ConstStability { + feature: regular_stab.feature, + const_stable_indirect, + promotable: false, + level: regular_stab.level, + } +} + /// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`. /// Returns `None` if no stability attributes are found. pub fn find_body_stability( diff --git a/compiler/rustc_baked_icu_data/Cargo.toml b/compiler/rustc_baked_icu_data/Cargo.toml index e6cfb4887c9..c35556dcf5b 100644 --- a/compiler/rustc_baked_icu_data/Cargo.toml +++ b/compiler/rustc_baked_icu_data/Cargo.toml @@ -8,11 +8,6 @@ edition = "2021" icu_list = "1.2" icu_locid = "1.2" icu_locid_transform = "1.3.2" -icu_provider = "1.2" +icu_provider = { version = "1.2", features = ["sync"] } zerovec = "0.10.0" # tidy-alphabetical-end - -[features] -# tidy-alphabetical-start -rustc_use_parallel_compiler = ['icu_provider/sync'] -# tidy-alphabetical-end diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index e5b28289faa..454fd14ea74 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -27,7 +27,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ - self, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast, + self, ClauseKind, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast, suggest_constraining_type_params, }; use rustc_middle::util::CallKind; @@ -39,6 +39,7 @@ use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use tracing::{debug, instrument}; @@ -201,16 +202,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut has_suggest_reborrow = false; if !seen_spans.contains(&move_span) { - if !closure { - self.suggest_ref_or_clone( - mpi, - &mut err, - &mut in_pattern, - move_spans, - moved_place.as_ref(), - &mut has_suggest_reborrow, - ); - } + self.suggest_ref_or_clone( + mpi, + &mut err, + &mut in_pattern, + move_spans, + moved_place.as_ref(), + &mut has_suggest_reborrow, + closure, + ); let msg_opt = CapturedMessageOpt { is_partial_move, @@ -266,27 +266,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - let opt_name = self.describe_place_with_options(place.as_ref(), DescribePlaceOpt { - including_downcast: true, - including_tuple_field: true, - }); - let note_msg = match opt_name { - Some(name) => format!("`{name}`"), - None => "value".to_owned(), - }; - if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) - || if let UseSpans::FnSelfUse { kind, .. } = use_spans - && let CallKind::FnCall { fn_trait_id, self_ty } = kind - && let ty::Param(_) = self_ty.kind() - && ty == self_ty - && self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) - { - // this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`. - true - } else { - false - } - { + if self.param_env.caller_bounds().iter().any(|c| { + c.as_trait_clause().is_some_and(|pred| { + pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id()) + }) + }) { // Suppress the next suggestion since we don't want to put more bounds onto // something that already has `Fn`-like bounds (or is a closure), so we can't // restrict anyways. @@ -295,6 +279,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_adding_bounds(&mut err, ty, copy_did, span); } + let opt_name = self.describe_place_with_options(place.as_ref(), DescribePlaceOpt { + including_downcast: true, + including_tuple_field: true, + }); + let note_msg = match opt_name { + Some(name) => format!("`{name}`"), + None => "value".to_owned(), + }; if needs_note { if let Some(local) = place.as_local() { let span = self.body.local_decls[local].source_info.span; @@ -341,6 +333,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { move_spans: UseSpans<'tcx>, moved_place: PlaceRef<'tcx>, has_suggest_reborrow: &mut bool, + moved_or_invoked_closure: bool, ) { let move_span = match move_spans { UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span, @@ -428,104 +421,76 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let typeck = self.infcx.tcx.typeck(self.mir_def_id()); let parent = self.infcx.tcx.parent_hir_node(expr.hir_id); - let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent + let (def_id, call_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind { - (typeck.type_dependent_def_id(parent_expr.hir_id), args, 1) + let def_id = typeck.type_dependent_def_id(parent_expr.hir_id); + (def_id, Some(parent_expr.hir_id), args, 1) } else if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::Call(call, args) = parent_expr.kind && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind() { - (Some(*def_id), args, 0) + (Some(*def_id), Some(call.hir_id), args, 0) } else { - (None, &[][..], 0) + (None, None, &[][..], 0) }; + let ty = place.ty(self.body, self.infcx.tcx).ty; - // If the moved value is a mut reference, it is used in a - // generic function and it's type is a generic param, it can be - // reborrowed to avoid moving. - // for example: - // struct Y(u32); - // x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`. + let mut can_suggest_clone = true; if let Some(def_id) = def_id - && self.infcx.tcx.def_kind(def_id).is_fn_like() && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) - && let Some(arg) = self - .infcx - .tcx - .fn_sig(def_id) - .skip_binder() - .skip_binder() - .inputs() - .get(pos + offset) - && let ty::Param(_) = arg.kind() { - let place = &self.move_data.move_paths[mpi].place; - let ty = place.ty(self.body, self.infcx.tcx).ty; - if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { + // The move occurred as one of the arguments to a function call. Is that + // argument generic? `def_id` can't be a closure here, so using `fn_sig` is fine + let arg_param = if self.infcx.tcx.def_kind(def_id).is_fn_like() + && let sig = + self.infcx.tcx.fn_sig(def_id).instantiate_identity().skip_binder() + && let Some(arg_ty) = sig.inputs().get(pos + offset) + && let ty::Param(arg_param) = arg_ty.kind() + { + Some(arg_param) + } else { + None + }; + + // If the moved value is a mut reference, it is used in a + // generic function and it's type is a generic param, it can be + // reborrowed to avoid moving. + // for example: + // struct Y(u32); + // x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`. + if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() + && arg_param.is_some() + { *has_suggest_reborrow = true; self.suggest_reborrow(err, expr.span, moved_place); return; } - } - let mut can_suggest_clone = true; - if let Some(def_id) = def_id - && let Some(local_def_id) = def_id.as_local() - && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id) - && let Some(fn_sig) = node.fn_sig() - && let Some(ident) = node.ident() - && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) - && let Some(arg) = fn_sig.decl.inputs.get(pos + offset) - { - let mut is_mut = false; - if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = arg.kind - && let Res::Def(DefKind::TyParam, param_def_id) = path.res - && self - .infcx - .tcx - .predicates_of(def_id) - .instantiate_identity(self.infcx.tcx) - .predicates - .into_iter() - .any(|pred| { - if let ty::ClauseKind::Trait(predicate) = pred.kind().skip_binder() - && [ - self.infcx.tcx.get_diagnostic_item(sym::AsRef), - self.infcx.tcx.get_diagnostic_item(sym::AsMut), - self.infcx.tcx.get_diagnostic_item(sym::Borrow), - self.infcx.tcx.get_diagnostic_item(sym::BorrowMut), - ] - .contains(&Some(predicate.def_id())) - && let ty::Param(param) = predicate.self_ty().kind() - && let generics = self.infcx.tcx.generics_of(def_id) - && let param = generics.type_param(*param, self.infcx.tcx) - && param.def_id == param_def_id - { - if [ - self.infcx.tcx.get_diagnostic_item(sym::AsMut), - self.infcx.tcx.get_diagnostic_item(sym::BorrowMut), - ] - .contains(&Some(predicate.def_id())) - { - is_mut = true; - } - true - } else { - false - } - }) + // If the moved place is used generically by the callee and a reference to it + // would still satisfy any bounds on its type, suggest borrowing. + if let Some(¶m) = arg_param + && let Some(generic_args) = call_id.and_then(|id| typeck.node_args_opt(id)) + && let Some(ref_mutability) = self.suggest_borrow_generic_arg( + err, + def_id, + generic_args, + param, + moved_place, + pos + offset, + ty, + expr.span, + ) { - // The type of the argument corresponding to the expression that got moved - // is a type parameter `T`, which is has a `T: AsRef` obligation. - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "borrow the value to avoid moving it", - format!("&{}", if is_mut { "mut " } else { "" }), - Applicability::MachineApplicable, - ); - can_suggest_clone = is_mut; - } else { + can_suggest_clone = ref_mutability.is_mut(); + } else if let Some(local_def_id) = def_id.as_local() + && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id) + && let Some(fn_decl) = node.fn_decl() + && let Some(ident) = node.ident() + && let Some(arg) = fn_decl.inputs.get(pos + offset) + { + // If we can't suggest borrowing in the call, but the function definition + // is local, instead offer changing the function to borrow that argument. let mut span: MultiSpan = arg.span.into(); span.push_span_label( arg.span, @@ -546,8 +511,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } } - let place = &self.move_data.move_paths[mpi].place; - let ty = place.ty(self.body, self.infcx.tcx).ty; if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IntoIterIntoIter, _)) = @@ -557,6 +520,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } = move_spans { // We already suggest cloning for these cases in `explain_captures`. + } else if moved_or_invoked_closure { + // Do not suggest `closure.clone()()`. } else if let UseSpans::ClosureUse { closure_kind: ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)), @@ -665,6 +630,113 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } + /// If a place is used after being moved as an argument to a function, the function is generic + /// in that argument, and a reference to the argument's type would still satisfy the function's + /// bounds, suggest borrowing. This covers, e.g., borrowing an `impl Fn()` argument being passed + /// in an `impl FnOnce()` position. + /// Returns `Some(mutability)` when suggesting to borrow with mutability `mutability`, or `None` + /// if no suggestion is made. + fn suggest_borrow_generic_arg( + &self, + err: &mut Diag<'_>, + callee_did: DefId, + generic_args: ty::GenericArgsRef<'tcx>, + param: ty::ParamTy, + moved_place: PlaceRef<'tcx>, + moved_arg_pos: usize, + moved_arg_ty: Ty<'tcx>, + place_span: Span, + ) -> Option<ty::Mutability> { + let tcx = self.infcx.tcx; + let sig = tcx.fn_sig(callee_did).instantiate_identity().skip_binder(); + let clauses = tcx.predicates_of(callee_did).instantiate_identity(self.infcx.tcx).predicates; + + // First, is there at least one method on one of `param`'s trait bounds? + // This keeps us from suggesting borrowing the argument to `mem::drop`, e.g. + if !clauses.iter().any(|clause| { + clause.as_trait_clause().is_some_and(|tc| { + tc.self_ty().skip_binder().is_param(param.index) + && tc.polarity() == ty::PredicatePolarity::Positive + && tcx + .supertrait_def_ids(tc.def_id()) + .flat_map(|trait_did| tcx.associated_items(trait_did).in_definition_order()) + .any(|item| item.fn_has_self_parameter) + }) + }) { + return None; + } + + // Try borrowing a shared reference first, then mutably. + if let Some(mutbl) = [ty::Mutability::Not, ty::Mutability::Mut].into_iter().find(|&mutbl| { + let re = self.infcx.tcx.lifetimes.re_erased; + let ref_ty = Ty::new_ref(self.infcx.tcx, re, moved_arg_ty, mutbl); + + // Ensure that substituting `ref_ty` in the callee's signature doesn't break + // other inputs or the return type. + let new_args = tcx.mk_args_from_iter(generic_args.iter().enumerate().map( + |(i, arg)| { + if i == param.index as usize { ref_ty.into() } else { arg } + }, + )); + let can_subst = |ty: Ty<'tcx>| { + // Normalize before comparing to see through type aliases and projections. + let old_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, generic_args); + let new_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, new_args); + if let Ok(old_ty) = tcx.try_normalize_erasing_regions(self.param_env, old_ty) + && let Ok(new_ty) = tcx.try_normalize_erasing_regions(self.param_env, new_ty) + { + old_ty == new_ty + } else { + false + } + }; + if !can_subst(sig.output()) + || sig + .inputs() + .iter() + .enumerate() + .any(|(i, &input_ty)| i != moved_arg_pos && !can_subst(input_ty)) + { + return false; + } + + // Test the callee's predicates, substituting a reference in for the self ty + // in bounds on `param`. + clauses.iter().all(|&clause| { + let clause_for_ref = clause.kind().map_bound(|kind| match kind { + ClauseKind::Trait(c) if c.self_ty().is_param(param.index) => { + ClauseKind::Trait(c.with_self_ty(tcx, ref_ty)) + } + ClauseKind::Projection(c) if c.self_ty().is_param(param.index) => { + ClauseKind::Projection(c.with_self_ty(tcx, ref_ty)) + } + _ => kind, + }); + self.infcx.predicate_must_hold_modulo_regions(&Obligation::new( + tcx, + ObligationCause::dummy(), + self.param_env, + ty::EarlyBinder::bind(clause_for_ref).instantiate(tcx, generic_args), + )) + }) + }) { + let place_desc = if let Some(desc) = self.describe_place(moved_place) { + format!("`{desc}`") + } else { + "here".to_owned() + }; + err.span_suggestion_verbose( + place_span.shrink_to_lo(), + format!("consider {}borrowing {place_desc}", mutbl.mutably_str()), + mutbl.ref_prefix_str(), + Applicability::MaybeIncorrect, + ); + Some(mutbl) + } else { + None + } + } + fn report_use_of_uninitialized( &self, mpi: MovePathIndex, @@ -845,74 +917,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); } - fn suggest_borrow_fn_like( - &self, - err: &mut Diag<'_>, - ty: Ty<'tcx>, - move_sites: &[MoveSite], - value_name: &str, - ) -> bool { - let tcx = self.infcx.tcx; - - // Find out if the predicates show that the type is a Fn or FnMut - let find_fn_kind_from_did = |(pred, _): (ty::Clause<'tcx>, _)| { - if let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() - && pred.self_ty() == ty - { - if tcx.is_lang_item(pred.def_id(), LangItem::Fn) { - return Some(hir::Mutability::Not); - } else if tcx.is_lang_item(pred.def_id(), LangItem::FnMut) { - return Some(hir::Mutability::Mut); - } - } - None - }; - - // If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably) - // borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`. - // These types seem reasonably opaque enough that they could be instantiated with their - // borrowed variants in a function body when we see a move error. - let borrow_level = match *ty.kind() { - ty::Param(_) => tcx - .explicit_predicates_of(self.mir_def_id().to_def_id()) - .predicates - .iter() - .copied() - .find_map(find_fn_kind_from_did), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => tcx - .explicit_item_super_predicates(def_id) - .iter_instantiated_copied(tcx, args) - .find_map(|(clause, span)| find_fn_kind_from_did((clause, span))), - ty::Closure(_, args) => match args.as_closure().kind() { - ty::ClosureKind::Fn => Some(hir::Mutability::Not), - ty::ClosureKind::FnMut => Some(hir::Mutability::Mut), - _ => None, - }, - _ => None, - }; - - let Some(borrow_level) = borrow_level else { - return false; - }; - let sugg = move_sites - .iter() - .map(|move_site| { - let move_out = self.move_data.moves[(*move_site).moi]; - let moved_place = &self.move_data.move_paths[move_out.path].place; - let move_spans = self.move_spans(moved_place.as_ref(), move_out.source); - let move_span = move_spans.args_or_use(); - let suggestion = borrow_level.ref_prefix_str().to_owned(); - (move_span.shrink_to_lo(), suggestion) - }) - .collect(); - err.multipart_suggestion_verbose( - format!("consider {}borrowing {value_name}", borrow_level.mutably_str()), - sugg, - Applicability::MaybeIncorrect, - ); - true - } - /// In a move error that occurs on a call within a loop, we try to identify cases where cloning /// the value would lead to a logic error. We infer these cases by seeing if the moved value is /// part of the logic to break the loop, either through an explicit `break` or if the expression diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs index bfd7e83501c..d77c53a3984 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs @@ -4,15 +4,16 @@ use std::ops::ControlFlow; use either::Either; +use itertools::Itertools as _; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{Applicability, Diag}; +use rustc_errors::{Diag, Subdiagnostic}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::{self, ConstraintCategory, Location}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_span::Symbol; +use rustc_trait_selection::errors::impl_trait_overcapture_suggestion; use crate::MirBorrowckCtxt; use crate::borrow_set::BorrowData; @@ -61,6 +62,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // *does* mention. We'll use that for the `+ use<'a>` suggestion below. let mut visitor = CheckExplicitRegionMentionAndCollectGenerics { tcx, + generics: tcx.generics_of(opaque_def_id), offending_region_idx, seen_opaques: [opaque_def_id].into_iter().collect(), seen_lifetimes: Default::default(), @@ -83,34 +85,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { "this call may capture more lifetimes than intended, \ because Rust 2024 has adjusted the `impl Trait` lifetime capture rules", ); - let mut seen_generics: Vec<_> = - visitor.seen_lifetimes.iter().map(ToString::to_string).collect(); - // Capture all in-scope ty/const params. - seen_generics.extend( - ty::GenericArgs::identity_for_item(tcx, opaque_def_id) - .iter() - .filter(|arg| { - matches!( - arg.unpack(), - ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_) - ) - }) - .map(|arg| arg.to_string()), - ); - if opaque_def_id.is_local() { - diag.span_suggestion_verbose( - tcx.def_span(opaque_def_id).shrink_to_hi(), - "add a precise capturing bound to avoid overcapturing", - format!(" + use<{}>", seen_generics.join(", ")), - Applicability::MaybeIncorrect, - ); + let mut captured_args = visitor.seen_lifetimes; + // Add in all of the type and const params, too. + // Ordering here is kinda strange b/c we're walking backwards, + // but we're trying to provide *a* suggestion, not a nice one. + let mut next_generics = Some(visitor.generics); + let mut any_synthetic = false; + while let Some(generics) = next_generics { + for param in &generics.own_params { + if param.kind.is_ty_or_const() { + captured_args.insert(param.def_id); + } + if param.kind.is_synthetic() { + any_synthetic = true; + } + } + next_generics = generics.parent.map(|def_id| tcx.generics_of(def_id)); + } + + if let Some(opaque_def_id) = opaque_def_id.as_local() + && let hir::OpaqueTyOrigin::FnReturn { parent, .. } = + tcx.hir().expect_opaque_ty(opaque_def_id).origin + { + if let Some(sugg) = impl_trait_overcapture_suggestion( + tcx, + opaque_def_id, + parent, + captured_args, + ) { + sugg.add_to_diag(diag); + } } else { diag.span_help( tcx.def_span(opaque_def_id), format!( "if you can modify this crate, add a precise \ capturing bound to avoid overcapturing: `+ use<{}>`", - seen_generics.join(", ") + if any_synthetic { + "/* Args */".to_string() + } else { + captured_args + .into_iter() + .map(|def_id| tcx.item_name(def_id)) + .join(", ") + } ), ); } @@ -182,9 +200,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> { struct CheckExplicitRegionMentionAndCollectGenerics<'tcx> { tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, offending_region_idx: usize, seen_opaques: FxIndexSet<DefId>, - seen_lifetimes: FxIndexSet<Symbol>, + seen_lifetimes: FxIndexSet<DefId>, } impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CheckExplicitRegionMentionAndCollectGenerics<'tcx> { @@ -214,7 +233,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CheckExplicitRegionMentionAndCollectGen if param.index as usize == self.offending_region_idx { ControlFlow::Break(()) } else { - self.seen_lifetimes.insert(param.name); + self.seen_lifetimes.insert(self.generics.region_param(param, self.tcx).def_id); ControlFlow::Continue(()) } } diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index b686a8cf935..f419c1e776b 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -39,50 +39,10 @@ pub(crate) fn cfg_eval( let features = Some(features); CfgEval(StripUnconfigured { sess, features, config_tokens: true, lint_node_id }) .configure_annotatable(annotatable) - // Since the item itself has already been configured by the `InvocationCollector`, - // we know that fold result vector will contain exactly one element. - .unwrap() } struct CfgEval<'a>(StripUnconfigured<'a>); -fn flat_map_annotatable( - vis: &mut impl MutVisitor, - annotatable: Annotatable, -) -> Option<Annotatable> { - match annotatable { - Annotatable::Item(item) => vis.flat_map_item(item).pop().map(Annotatable::Item), - Annotatable::AssocItem(item, ctxt) => { - Some(Annotatable::AssocItem(vis.flat_map_assoc_item(item, ctxt).pop()?, ctxt)) - } - Annotatable::ForeignItem(item) => { - vis.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem) - } - Annotatable::Stmt(stmt) => { - vis.flat_map_stmt(stmt.into_inner()).pop().map(P).map(Annotatable::Stmt) - } - Annotatable::Expr(mut expr) => { - vis.visit_expr(&mut expr); - Some(Annotatable::Expr(expr)) - } - Annotatable::Arm(arm) => vis.flat_map_arm(arm).pop().map(Annotatable::Arm), - Annotatable::ExprField(field) => { - vis.flat_map_expr_field(field).pop().map(Annotatable::ExprField) - } - Annotatable::PatField(fp) => vis.flat_map_pat_field(fp).pop().map(Annotatable::PatField), - Annotatable::GenericParam(param) => { - vis.flat_map_generic_param(param).pop().map(Annotatable::GenericParam) - } - Annotatable::Param(param) => vis.flat_map_param(param).pop().map(Annotatable::Param), - Annotatable::FieldDef(sf) => vis.flat_map_field_def(sf).pop().map(Annotatable::FieldDef), - Annotatable::Variant(v) => vis.flat_map_variant(v).pop().map(Annotatable::Variant), - Annotatable::Crate(mut krate) => { - vis.visit_crate(&mut krate); - Some(Annotatable::Crate(krate)) - } - } -} - fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool { struct CfgFinder; @@ -106,14 +66,7 @@ fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool { Annotatable::ForeignItem(item) => CfgFinder.visit_foreign_item(item), Annotatable::Stmt(stmt) => CfgFinder.visit_stmt(stmt), Annotatable::Expr(expr) => CfgFinder.visit_expr(expr), - Annotatable::Arm(arm) => CfgFinder.visit_arm(arm), - Annotatable::ExprField(field) => CfgFinder.visit_expr_field(field), - Annotatable::PatField(field) => CfgFinder.visit_pat_field(field), - Annotatable::GenericParam(param) => CfgFinder.visit_generic_param(param), - Annotatable::Param(param) => CfgFinder.visit_param(param), - Annotatable::FieldDef(field) => CfgFinder.visit_field_def(field), - Annotatable::Variant(variant) => CfgFinder.visit_variant(variant), - Annotatable::Crate(krate) => CfgFinder.visit_crate(krate), + _ => unreachable!(), }; res.is_break() } @@ -123,11 +76,11 @@ impl CfgEval<'_> { self.0.configure(node) } - fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> { + fn configure_annotatable(mut self, annotatable: Annotatable) -> Annotatable { // Tokenizing and re-parsing the `Annotatable` can have a significant // performance impact, so try to avoid it if possible if !has_cfg_or_cfg_attr(&annotatable) { - return Some(annotatable); + return annotatable; } // The majority of parsed attribute targets will never need to have early cfg-expansion @@ -140,39 +93,6 @@ impl CfgEval<'_> { // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization // process is lossless, so this process is invisible to proc-macros. - let parse_annotatable_with: for<'a> fn(&mut Parser<'a>) -> PResult<'a, _> = - match annotatable { - Annotatable::Item(_) => { - |parser| Ok(Annotatable::Item(parser.parse_item(ForceCollect::Yes)?.unwrap())) - } - Annotatable::AssocItem(_, AssocCtxt::Trait) => |parser| { - Ok(Annotatable::AssocItem( - parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(), - AssocCtxt::Trait, - )) - }, - Annotatable::AssocItem(_, AssocCtxt::Impl) => |parser| { - Ok(Annotatable::AssocItem( - parser.parse_impl_item(ForceCollect::Yes)?.unwrap().unwrap(), - AssocCtxt::Impl, - )) - }, - Annotatable::ForeignItem(_) => |parser| { - Ok(Annotatable::ForeignItem( - parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap(), - )) - }, - Annotatable::Stmt(_) => |parser| { - Ok(Annotatable::Stmt(P(parser - .parse_stmt_without_recovery(false, ForceCollect::Yes)? - .unwrap()))) - }, - Annotatable::Expr(_) => { - |parser| Ok(Annotatable::Expr(parser.parse_expr_force_collect()?)) - } - _ => unreachable!(), - }; - // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`) // to `None`-delimited groups containing the corresponding tokens. This // is normally delayed until the proc-macro server actually needs to @@ -191,19 +111,56 @@ impl CfgEval<'_> { // Re-parse the tokens, setting the `capture_cfg` flag to save extra information // to the captured `AttrTokenStream` (specifically, we capture // `AttrTokenTree::AttrsTarget` for all occurrences of `#[cfg]` and `#[cfg_attr]`) + // + // After that we have our re-parsed `AttrTokenStream`, recursively configuring + // our attribute target will correctly configure the tokens as well. let mut parser = Parser::new(&self.0.sess.psess, orig_tokens, None); parser.capture_cfg = true; - match parse_annotatable_with(&mut parser) { - Ok(a) => annotatable = a, + let res: PResult<'_, Annotatable> = try { + match annotatable { + Annotatable::Item(_) => { + let item = parser.parse_item(ForceCollect::Yes)?.unwrap(); + Annotatable::Item(self.flat_map_item(item).pop().unwrap()) + } + Annotatable::AssocItem(_, AssocCtxt::Trait) => { + let item = parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(); + Annotatable::AssocItem( + self.flat_map_assoc_item(item, AssocCtxt::Trait).pop().unwrap(), + AssocCtxt::Trait, + ) + } + Annotatable::AssocItem(_, AssocCtxt::Impl) => { + let item = parser.parse_impl_item(ForceCollect::Yes)?.unwrap().unwrap(); + Annotatable::AssocItem( + self.flat_map_assoc_item(item, AssocCtxt::Impl).pop().unwrap(), + AssocCtxt::Impl, + ) + } + Annotatable::ForeignItem(_) => { + let item = parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap(); + Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) + } + Annotatable::Stmt(_) => { + let stmt = + parser.parse_stmt_without_recovery(false, ForceCollect::Yes)?.unwrap(); + Annotatable::Stmt(P(self.flat_map_stmt(stmt).pop().unwrap())) + } + Annotatable::Expr(_) => { + let mut expr = parser.parse_expr_force_collect()?; + self.visit_expr(&mut expr); + Annotatable::Expr(expr) + } + _ => unreachable!(), + } + }; + + match res { + Ok(ann) => ann, Err(err) => { err.emit(); - return Some(annotatable); + annotatable } } - - // Now that we have our re-parsed `AttrTokenStream`, recursively configuring - // our attribute target will correctly configure the tokens as well. - flat_map_annotatable(self, annotatable) } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 82baaca9a46..79c198ed2d0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -680,6 +680,12 @@ impl<'a> TraitDef<'a> { param_clone } }) + .map(|mut param| { + // Remove all attributes, because there might be helper attributes + // from other macros that will not be valid in the expanded implementation. + param.attrs.clear(); + param + }) .collect(); // and similarly for where clauses diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs deleted file mode 100644 index c7725e49c94..00000000000 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ /dev/null @@ -1,12 +0,0 @@ -use rustc_codegen_ssa::back::archive::{ - ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, -}; -use rustc_session::Session; - -pub(crate) struct ArArchiveBuilderBuilder; - -impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { - fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> { - Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER)) - } -} diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 19a1de53d1d..b506b1f5731 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -43,7 +43,6 @@ use rustc_codegen_ssa::CodegenResults; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -56,7 +55,6 @@ use crate::prelude::*; mod abi; mod allocator; mod analyze; -mod archive; mod base; mod cast; mod codegen_i128; @@ -249,17 +247,6 @@ impl CodegenBackend for CraneliftCodegenBackend { self.config.borrow().as_ref().unwrap(), ) } - - fn link( - &self, - sess: &Session, - codegen_results: CodegenResults, - outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed> { - use rustc_codegen_ssa::back::link::link_binary; - - link_binary(sess, &crate::archive::ArArchiveBuilderBuilder, &codegen_results, outputs) - } } fn target_triple(sess: &Session) -> target_lexicon::Triple { diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs deleted file mode 100644 index 82e98370b37..00000000000 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::path::Path; - -use rustc_codegen_ssa::back::archive::{ - ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, - ImportLibraryItem, -}; -use rustc_session::Session; - -pub(crate) struct ArArchiveBuilderBuilder; - -impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { - fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> { - Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER)) - } - - fn create_dll_import_lib( - &self, - _sess: &Session, - _lib_name: &str, - _items: Vec<ImportLibraryItem>, - _output_path: &Path, - ) { - unimplemented!("creating dll imports is not yet supported"); - } -} diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 9d62ccc95d5..5d8c5c199b1 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -52,6 +52,10 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { fn clear_dbg_loc(&mut self) { self.location = None; } + + fn get_dbg_loc(&self) -> Option<Self::DILocation> { + self.location + } } /// Generate the `debug_context` in an MIR Body. diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index f70dc94b267..452e92bffa2 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -58,7 +58,6 @@ extern crate rustc_driver; mod abi; mod allocator; -mod archive; mod asm; mod attributes; mod back; @@ -103,7 +102,7 @@ use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBacken use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::IntoDynSyncSend; -use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; +use rustc_errors::DiagCtxtHandle; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -261,17 +260,6 @@ impl CodegenBackend for GccCodegenBackend { .join(sess) } - fn link( - &self, - sess: &Session, - codegen_results: CodegenResults, - outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed> { - use rustc_codegen_ssa::back::link::link_binary; - - link_binary(sess, &crate::archive::ArArchiveBuilderBuilder, &codegen_results, outputs) - } - fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> { target_features(sess, allow_unstable, &self.target_info) } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 751b2235dc8..ac76b781218 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1574,6 +1574,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { cfi::typeid_for_fnabi(self.tcx, fn_abi, options) }; let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); + let dbg_loc = self.get_dbg_loc(); // Test whether the function pointer is associated with the type identifier. let cond = self.type_test(llfn, typeid_metadata); @@ -1582,10 +1583,16 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.cond_br(cond, bb_pass, bb_fail); self.switch_to_block(bb_fail); + if let Some(dbg_loc) = dbg_loc { + self.set_dbg_loc(dbg_loc); + } self.abort(); self.unreachable(); self.switch_to_block(bb_pass); + if let Some(dbg_loc) = dbg_loc { + self.set_dbg_loc(dbg_loc); + } } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ba863d9d74b..3a7c7efe03b 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -274,8 +274,12 @@ pub(crate) unsafe fn create_module<'ll>( } } - // Control Flow Guard is currently only supported by the MSVC linker on Windows. - if sess.target.is_like_msvc { + // Control Flow Guard is currently only supported by MSVC and LLVM on Windows. + if sess.target.is_like_msvc + || (sess.target.options.os == "windows" + && sess.target.options.env == "gnu" + && sess.target.options.abi == "llvm") + { match sess.opts.cg.control_flow_guard { CFGuard::Disabled => {} CFGuard::NoChecks => { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index feac97f3e2b..a6e07ea2a60 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,5 +1,7 @@ use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion}; +use crate::coverageinfo::mapgen::LocalFileId; + /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] #[repr(C)] @@ -137,8 +139,12 @@ pub(crate) struct CoverageSpan { } impl CoverageSpan { - pub(crate) fn from_source_region(file_id: u32, code_region: &SourceRegion) -> Self { - let &SourceRegion { file_name: _, start_line, start_col, end_line, end_col } = code_region; + pub(crate) fn from_source_region( + local_file_id: LocalFileId, + code_region: &SourceRegion, + ) -> Self { + let file_id = local_file_id.as_u32(); + let &SourceRegion { start_line, start_col, end_line, end_col } = code_region; // Internally, LLVM uses the high bit of `end_col` to distinguish between // code regions and gap regions, so it can't be used by the column number. assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}"); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index 5ed640b840e..e3c0df27883 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -6,7 +6,6 @@ use rustc_middle::mir::coverage::{ SourceRegion, }; use rustc_middle::ty::Instance; -use rustc_span::Symbol; use tracing::{debug, instrument}; use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; @@ -180,7 +179,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> { } pub(crate) struct FunctionCoverage<'tcx> { - function_coverage_info: &'tcx FunctionCoverageInfo, + pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo, is_used: bool, counters_seen: BitSet<CounterId>, @@ -199,11 +198,6 @@ impl<'tcx> FunctionCoverage<'tcx> { if self.is_used { self.function_coverage_info.function_source_hash } else { 0 } } - /// Returns an iterator over all filenames used by this function's mappings. - pub(crate) fn all_file_names(&self) -> impl Iterator<Item = Symbol> + Captures<'_> { - self.function_coverage_info.mappings.iter().map(|mapping| mapping.source_region.file_name) - } - /// Convert this function's coverage expression data into a form that can be /// passed through FFI to LLVM. pub(crate) fn counter_expressions( diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index bb2f634cb25..b582dd967a7 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -12,8 +12,10 @@ use rustc_index::IndexVec; use rustc_middle::mir::coverage::MappingKind; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, mir}; -use rustc_span::Symbol; +use rustc_session::RemapFileNameExt; +use rustc_session::config::RemapPathScopeComponents; use rustc_span::def_id::DefIdSet; +use rustc_span::{Span, Symbol}; use rustc_target::spec::HasTargetSpec; use tracing::debug; @@ -70,8 +72,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { .map(|(instance, function_coverage)| (instance, function_coverage.into_finished())) .collect::<Vec<_>>(); - let all_file_names = - function_coverage_entries.iter().flat_map(|(_, fn_cov)| fn_cov.all_file_names()); + let all_file_names = function_coverage_entries + .iter() + .map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span) + .map(|span| span_file_name(tcx, span)); let global_file_table = GlobalFileTable::new(all_file_names); // Encode all filenames referenced by coverage mappings in this CGU. @@ -96,7 +100,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { let is_used = function_coverage.is_used(); let coverage_mapping_buffer = - encode_mappings_for_function(&global_file_table, &function_coverage); + encode_mappings_for_function(tcx, &global_file_table, &function_coverage); if coverage_mapping_buffer.is_empty() { if function_coverage.is_used() { @@ -164,13 +168,13 @@ impl GlobalFileTable { Self { raw_file_table } } - fn global_file_id_for_file_name(&self, file_name: Symbol) -> u32 { + fn global_file_id_for_file_name(&self, file_name: Symbol) -> GlobalFileId { let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| { bug!("file name not found in prepared global file table: {file_name}"); }); // The raw file table doesn't include an entry for the working dir // (which has ID 0), so add 1 to get the correct ID. - (raw_id + 1) as u32 + GlobalFileId::from_usize(raw_id + 1) } fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> { @@ -196,19 +200,27 @@ impl GlobalFileTable { } rustc_index::newtype_index! { - struct LocalFileId {} + /// An index into the CGU's overall list of file paths. The underlying paths + /// will be embedded in the `__llvm_covmap` linker section. + struct GlobalFileId {} +} +rustc_index::newtype_index! { + /// An index into a function's list of global file IDs. That underlying list + /// of local-to-global mappings will be embedded in the function's record in + /// the `__llvm_covfun` linker section. + pub(crate) struct LocalFileId {} } /// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU) /// file IDs. #[derive(Default)] struct VirtualFileMapping { - local_to_global: IndexVec<LocalFileId, u32>, - global_to_local: FxIndexMap<u32, LocalFileId>, + local_to_global: IndexVec<LocalFileId, GlobalFileId>, + global_to_local: FxIndexMap<GlobalFileId, LocalFileId>, } impl VirtualFileMapping { - fn local_id_for_global(&mut self, global_file_id: u32) -> LocalFileId { + fn local_id_for_global(&mut self, global_file_id: GlobalFileId) -> LocalFileId { *self .global_to_local .entry(global_file_id) @@ -216,16 +228,26 @@ impl VirtualFileMapping { } fn into_vec(self) -> Vec<u32> { - self.local_to_global.raw + // This conversion should be optimized away to ~zero overhead. + // In any case, it's probably not hot enough to worry about. + self.local_to_global.into_iter().map(|global| global.as_u32()).collect() } } +fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol { + let source_file = tcx.sess.source_map().lookup_source_file(span.lo()); + let name = + source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(); + Symbol::intern(&name) +} + /// Using the expressions and counter regions collected for a single function, /// generate the variable-sized payload of its corresponding `__llvm_covfun` /// entry. The payload is returned as a vector of bytes. /// /// Newly-encountered filenames will be added to the global file table. fn encode_mappings_for_function( + tcx: TyCtxt<'_>, global_file_table: &GlobalFileTable, function_coverage: &FunctionCoverage<'_>, ) -> Vec<u8> { @@ -242,53 +264,45 @@ fn encode_mappings_for_function( let mut mcdc_branch_regions = vec![]; let mut mcdc_decision_regions = vec![]; - // Group mappings into runs with the same filename, preserving the order - // yielded by `FunctionCoverage`. - // Prepare file IDs for each filename, and prepare the mapping data so that - // we can pass it through FFI to LLVM. - for (file_name, counter_regions_for_file) in - &counter_regions.group_by(|(_, region)| region.file_name) - { - // Look up the global file ID for this filename. - let global_file_id = global_file_table.global_file_id_for_file_name(file_name); - - // Associate that global file ID with a local file ID for this function. - let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id); - debug!(" file id: {local_file_id:?} => global {global_file_id} = '{file_name:?}'"); - - // For each counter/region pair in this function+file, convert it to a - // form suitable for FFI. - for (mapping_kind, region) in counter_regions_for_file { - debug!("Adding counter {mapping_kind:?} to map for {region:?}"); - let span = ffi::CoverageSpan::from_source_region(local_file_id.as_u32(), region); - match mapping_kind { - MappingKind::Code(term) => { - code_regions - .push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) }); - } - MappingKind::Branch { true_term, false_term } => { - branch_regions.push(ffi::BranchRegion { - span, - true_counter: ffi::Counter::from_term(true_term), - false_counter: ffi::Counter::from_term(false_term), - }); - } - MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => { - mcdc_branch_regions.push(ffi::MCDCBranchRegion { - span, - true_counter: ffi::Counter::from_term(true_term), - false_counter: ffi::Counter::from_term(false_term), - mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params), - }); - } - MappingKind::MCDCDecision(mcdc_decision_params) => { - mcdc_decision_regions.push(ffi::MCDCDecisionRegion { - span, - mcdc_decision_params: ffi::mcdc::DecisionParameters::from( - mcdc_decision_params, - ), - }); - } + // Currently a function's mappings must all be in the same file as its body span. + let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span); + + // Look up the global file ID for that filename. + let global_file_id = global_file_table.global_file_id_for_file_name(file_name); + + // Associate that global file ID with a local file ID for this function. + let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id); + debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'"); + + // For each counter/region pair in this function+file, convert it to a + // form suitable for FFI. + for (mapping_kind, region) in counter_regions { + debug!("Adding counter {mapping_kind:?} to map for {region:?}"); + let span = ffi::CoverageSpan::from_source_region(local_file_id, region); + match mapping_kind { + MappingKind::Code(term) => { + code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) }); + } + MappingKind::Branch { true_term, false_term } => { + branch_regions.push(ffi::BranchRegion { + span, + true_counter: ffi::Counter::from_term(true_term), + false_counter: ffi::Counter::from_term(false_term), + }); + } + MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => { + mcdc_branch_regions.push(ffi::MCDCBranchRegion { + span, + true_counter: ffi::Counter::from_term(true_term), + false_counter: ffi::Counter::from_term(false_term), + mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params), + }); + } + MappingKind::MCDCDecision(mcdc_decision_params) => { + mcdc_decision_regions.push(ffi::MCDCDecisionRegion { + span, + mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params), + }); } } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 9e1e5127e80..89492e4b9fe 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -206,6 +206,10 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { } } + fn get_dbg_loc(&self) -> Option<&'ll DILocation> { + unsafe { llvm::LLVMGetCurrentDebugLocation2(self.llbuilder) } + } + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { gdb::insert_reference_to_gdb_debug_scripts_section_global(self) } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 49e616b5371..3dfb86d422d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -382,7 +382,7 @@ impl CodegenBackend for LlvmCodegenBackend { // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - link_binary(sess, &LlvmArchiveBuilderBuilder, &codegen_results, outputs) + link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 75a5ec44c22..7f59264824e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1063,6 +1063,7 @@ unsafe extern "C" { // Metadata pub fn LLVMSetCurrentDebugLocation2<'a>(Builder: &Builder<'a>, Loc: *const Metadata); + pub fn LLVMGetCurrentDebugLocation2<'a>(Builder: &Builder<'a>) -> Option<&'a Metadata>; // Terminators pub fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index c382242d8d0..db2b03d9aed 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -344,15 +344,23 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { }) { if enabled { + // Also add all transitively implied features. features.extend(sess.target.implied_target_features(std::iter::once(feature))); } else { + // Remove transitively reverse-implied features. + // We don't care about the order in `features` since the only thing we use it for is the // `features.contains` below. #[allow(rustc::potential_query_instability)] features.retain(|f| { - // Keep a feature if it does not imply `feature`. Or, equivalently, - // remove the reverse-dependencies of `feature`. - !sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) + if sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) { + // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to + // remove `f`. (This is the standard logical contraposition principle.) + false + } else { + // We can keep `f`. + true + } }); } } diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index e83bfa7b70d..d4836eb7a1d 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -304,6 +304,14 @@ pub trait ArchiveBuilder { fn build(self: Box<Self>, output: &Path) -> bool; } +pub struct ArArchiveBuilderBuilder; + +impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { + fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> { + Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER)) + } +} + #[must_use = "must call build() to finish building the archive"] pub struct ArArchiveBuilder<'a> { sess: &'a Session, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 3120b5bf0af..fd1126e8528 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -69,7 +69,7 @@ pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { pub fn link_binary( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, - codegen_results: &CodegenResults, + codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorGuaranteed> { let _timer = sess.timer("link_binary"); @@ -116,7 +116,7 @@ pub fn link_binary( link_rlib( sess, archive_builder_builder, - codegen_results, + &codegen_results, RlibFlavor::Normal, &path, )? @@ -126,7 +126,7 @@ pub fn link_binary( link_staticlib( sess, archive_builder_builder, - codegen_results, + &codegen_results, &out_filename, &path, )?; @@ -137,7 +137,7 @@ pub fn link_binary( archive_builder_builder, crate_type, &out_filename, - codegen_results, + &codegen_results, path.as_ref(), )?; } @@ -1647,7 +1647,7 @@ fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> Pat return file_path; } } - for search_path in sess.target_filesearch(PathKind::Native).search_paths() { + for search_path in sess.target_filesearch().search_paths(PathKind::Native) { let file_path = search_path.dir.join(name); if file_path.exists() { return file_path; diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 676fb181d67..cbf214763b4 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -16,6 +16,8 @@ use rustc_span::symbol::Symbol; use super::CodegenObject; use super::write::WriteBackendMethods; +use crate::back::archive::ArArchiveBuilderBuilder; +use crate::back::link::link_binary; use crate::back::write::TargetMachineFactoryFn; use crate::{CodegenResults, ModuleCodegen}; @@ -87,7 +89,9 @@ pub trait CodegenBackend { sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames, - ) -> Result<(), ErrorGuaranteed>; + ) -> Result<(), ErrorGuaranteed> { + link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs) + } /// Returns `true` if this backend can be safely called from multiple threads. /// diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index 670433a6c33..fe135e911fb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -81,6 +81,7 @@ pub trait DebugInfoBuilderMethods: BackendTypes { ); fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation); fn clear_dbg_loc(&mut self); + fn get_dbg_loc(&self) -> Option<Self::DILocation>; fn insert_reference_to_gdb_debug_scripts_section_global(&mut self); fn set_var_name(&mut self, value: Self::Value, name: &str); } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 8cd0ecb3e4e..ffe32acb316 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -3,6 +3,7 @@ use std::assert_matches::assert_matches; use std::borrow::Cow; use std::mem; +use std::num::NonZero; use std::ops::Deref; use rustc_attr::{ConstStability, StabilityLevel}; @@ -271,9 +272,18 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { /// context. pub fn check_op_spanned<O: NonConstOp<'tcx>>(&mut self, op: O, span: Span) { let gate = match op.status_in_item(self.ccx) { - Status::Unstable { gate, safe_to_expose_on_stable, is_function_call } - if self.tcx.features().enabled(gate) => - { + Status::Unstable { + gate, + safe_to_expose_on_stable, + is_function_call, + gate_already_checked, + } if gate_already_checked || self.tcx.features().enabled(gate) => { + if gate_already_checked { + assert!( + !safe_to_expose_on_stable, + "setting `gate_already_checked` without `safe_to_expose_on_stable` makes no sense" + ); + } // Generally this is allowed since the feature gate is enabled -- except // if this function wants to be safe-to-expose-on-stable. if !safe_to_expose_on_stable @@ -709,6 +719,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Intrinsics are language primitives, not regular calls, so treat them separately. if let Some(intrinsic) = tcx.intrinsic(callee) { + if !tcx.is_const_fn(callee) { + // Non-const intrinsic. + self.check_op(ops::IntrinsicNonConst { name: intrinsic.name }); + // If we allowed this, we're in miri-unleashed mode, so we might + // as well skip the remaining checks. + return; + } // We use `intrinsic.const_stable` to determine if this can be safely exposed to // stable code, rather than `const_stable_indirect`. This is to make // `#[rustc_const_stable_indirect]` an attribute that is always safe to add. @@ -716,17 +733,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // fallback body is safe to expose on stable. let is_const_stable = intrinsic.const_stable || (!intrinsic.must_be_overridden - && tcx.is_const_fn(callee) && is_safe_to_expose_on_stable_const_fn(tcx, callee)); match tcx.lookup_const_stability(callee) { None => { - // Non-const intrinsic. - self.check_op(ops::IntrinsicNonConst { name: intrinsic.name }); - } - Some(ConstStability { feature: None, .. }) => { - // Intrinsic does not need a separate feature gate (we rely on the - // regular stability checker). However, we have to worry about recursive - // const stability. + // This doesn't need a separate const-stability check -- const-stability equals + // regular stability, and regular stability is checked separately. + // However, we *do* have to worry about *recursive* const stability. if !is_const_stable && self.enforce_recursive_const_stability() { self.dcx().emit_err(errors::UnmarkedIntrinsicExposed { span: self.span, @@ -735,14 +747,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } Some(ConstStability { - feature: Some(feature), level: StabilityLevel::Unstable { .. }, + feature, .. }) => { self.check_op(ops::IntrinsicUnstable { name: intrinsic.name, feature, - const_stable: is_const_stable, + const_stable_indirect: is_const_stable, }); } Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { @@ -773,7 +785,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { // All good. } - None | Some(ConstStability { feature: None, .. }) => { + None => { // This doesn't need a separate const-stability check -- const-stability equals // regular stability, and regular stability is checked separately. // However, we *do* have to worry about *recursive* const stability. @@ -787,8 +799,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } Some(ConstStability { - feature: Some(feature), - level: StabilityLevel::Unstable { implied_by: implied_feature, .. }, + level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + feature, .. }) => { // An unstable const fn with a feature gate. @@ -797,6 +809,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if // the callee is safe to expose, to avoid bypassing recursive stability. + // This is not ideal since it means the user sees an error, not the macro + // author, but that's also the case if one forgets to set + // `#[allow_internal_unstable]` in the first place. Note that this cannot be + // integrated in the check below since we want to enforce + // `callee_safe_to_expose_on_stable` even if + // `!self.enforce_recursive_const_stability()`. if (self.span.allows_unstable(feature) || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) && callee_safe_to_expose_on_stable @@ -810,16 +828,30 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // to allow this. let feature_enabled = callee.is_local() || tcx.features().enabled(feature) - || implied_feature.is_some_and(|f| tcx.features().enabled(f)); - // We do *not* honor this if we are in the "danger zone": we have to enforce - // recursive const-stability and the callee is not safe-to-expose. In that - // case we need `check_op` to do the check. - let danger_zone = !callee_safe_to_expose_on_stable - && self.enforce_recursive_const_stability(); - if danger_zone || !feature_enabled { + || implied_feature.is_some_and(|f| tcx.features().enabled(f)) + || { + // When we're compiling the compiler itself we may pull in + // crates from crates.io, but those crates may depend on other + // crates also pulled in from crates.io. We want to ideally be + // able to compile everything without requiring upstream + // modifications, so in the case that this looks like a + // `rustc_private` crate (e.g., a compiler crate) and we also have + // the `-Z force-unstable-if-unmarked` flag present (we're + // compiling a compiler crate), then let this missing feature + // annotation slide. + // This matches what we do in `eval_stability_allow_unstable` for + // regular stability. + feature == sym::rustc_private + && issue == NonZero::new(27812) + && self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked + }; + // Even if the feature is enabled, we still need check_op to double-check + // this if the callee is not safe to expose on stable. + if !feature_enabled || !callee_safe_to_expose_on_stable { self.check_op(ops::FnCallUnstable { def_id: callee, feature, + feature_enabled, safe_to_expose_on_stable: callee_safe_to_expose_on_stable, }); } diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index dcdaafaecc2..ebdd55a4f70 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -53,10 +53,11 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { } pub fn enforce_recursive_const_stability(&self) -> bool { - // We can skip this if `staged_api` is not enabled, since in such crates - // `lookup_const_stability` will always be `None`. + // We can skip this if neither `staged_api` nor `-Zforce-unstable-if-unmarked` are enabled, + // since in such crates `lookup_const_stability` will always be `None`. self.const_kind == Some(hir::ConstContext::ConstFn) - && self.tcx.features().staged_api() + && (self.tcx.features().staged_api() + || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) && is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id()) } @@ -109,14 +110,15 @@ pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> b match tcx.lookup_const_stability(def_id) { None => { - // Only marked functions can be trusted. Note that this may be a function in a - // non-staged-API crate where no recursive checks were done! - false + // In a `staged_api` crate, we do enforce recursive const stability for all unmarked + // functions, so we can trust local functions. But in another crate we don't know which + // rules were applied, so we can't trust that. + def_id.is_local() && tcx.features().staged_api() } Some(stab) => { - // We consider things safe-to-expose if they are stable, if they don't have any explicit - // const stability attribute, or if they are marked as `const_stable_indirect`. - stab.is_const_stable() || stab.feature.is_none() || stab.const_stable_indirect + // We consider things safe-to-expose if they are stable or if they are marked as + // `const_stable_indirect`. + stab.is_const_stable() || stab.const_stable_indirect } } } diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 036ca763280..ca95e42dd2b 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -28,6 +28,9 @@ pub enum Status { Unstable { /// The feature that must be enabled to use this operation. gate: Symbol, + /// Whether the feature gate was already checked (because the logic is a bit more + /// complicated than just checking a single gate). + gate_already_checked: bool, /// Whether it is allowed to use this operation from stable `const fn`. /// This will usually be `false`. safe_to_expose_on_stable: bool, @@ -82,6 +85,7 @@ impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> { // We use the `const_trait_impl` gate for all conditionally-const calls. Status::Unstable { gate: sym::const_trait_impl, + gate_already_checked: false, safe_to_expose_on_stable: false, // We don't want the "mark the callee as `#[rustc_const_stable_indirect]`" hint is_function_call: false, @@ -330,6 +334,9 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { pub(crate) struct FnCallUnstable { pub def_id: DefId, pub feature: Symbol, + /// If this is true, then the feature is enabled, but we need to still check if it is safe to + /// expose on stable. + pub feature_enabled: bool, pub safe_to_expose_on_stable: bool, } @@ -337,12 +344,14 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable { gate: self.feature, + gate_already_checked: self.feature_enabled, safe_to_expose_on_stable: self.safe_to_expose_on_stable, is_function_call: true, } } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { + assert!(!self.feature_enabled); let mut err = ccx.dcx().create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(self.def_id), @@ -376,14 +385,15 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicNonConst { pub(crate) struct IntrinsicUnstable { pub name: Symbol, pub feature: Symbol, - pub const_stable: bool, + pub const_stable_indirect: bool, } impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable { fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable { gate: self.feature, - safe_to_expose_on_stable: self.const_stable, + gate_already_checked: false, + safe_to_expose_on_stable: self.const_stable_indirect, // We do *not* want to suggest to mark the intrinsic as `const_stable_indirect`, // that's not a trivial change! is_function_call: false, @@ -410,6 +420,7 @@ impl<'tcx> NonConstOp<'tcx> for Coroutine { { Status::Unstable { gate: sym::const_async_blocks, + gate_already_checked: false, safe_to_expose_on_stable: false, is_function_call: false, } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 5a477143a62..c8ecddb046c 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -10,11 +10,11 @@ bitflags = "2.4.1" either = "1.0" elsa = "=1.7.1" ena = "0.14.3" -indexmap = { version = "2.4.0" } +indexmap = { version = "2.4.0", features = ["rustc-rayon"] } jobserver_crate = { version = "0.1.28", package = "jobserver" } measureme = "11" rustc-hash = "2.0.0" -rustc-rayon = { version = "0.5.0", optional = true } +rustc-rayon = "0.5.0" rustc-stable-hash = { version = "0.1.0", features = ["nightly"] } rustc_arena = { path = "../rustc_arena" } rustc_graphviz = { path = "../rustc_graphviz" } @@ -53,8 +53,3 @@ memmap2 = "0.2.1" [target.'cfg(not(target_has_atomic = "64"))'.dependencies] portable-atomic = "1.5.1" - -[features] -# tidy-alphabetical-start -rustc_use_parallel_compiler = ["indexmap/rustc-rayon", "dep:rustc-rayon"] -# tidy-alphabetical-end diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index afac08ae6f8..bede4c49703 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,7 +10,6 @@ #![allow(internal_features)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] -#![cfg_attr(not(parallel_compiler), feature(cell_leak))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 83fdaff515b..2b629024bfe 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -1,194 +1,162 @@ -cfg_match! { - cfg(not(parallel_compiler)) => { - pub auto trait DynSend {} - pub auto trait DynSync {} - - impl<T> DynSend for T {} - impl<T> DynSync for T {} - } - _ => { - #[rustc_on_unimplemented( - message = "`{Self}` doesn't implement `DynSend`. \ - Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Send`" - )] - // This is an auto trait for types which can be sent across threads if `sync::is_dyn_thread_safe()` - // is true. These types can be wrapped in a `FromDyn` to get a `Send` type. Wrapping a - // `Send` type in `IntoDynSyncSend` will create a `DynSend` type. - pub unsafe auto trait DynSend {} - - #[rustc_on_unimplemented( - message = "`{Self}` doesn't implement `DynSync`. \ - Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Sync`" - )] - // This is an auto trait for types which can be shared across threads if `sync::is_dyn_thread_safe()` - // is true. These types can be wrapped in a `FromDyn` to get a `Sync` type. Wrapping a - // `Sync` type in `IntoDynSyncSend` will create a `DynSync` type. - pub unsafe auto trait DynSync {} - - // Same with `Sync` and `Send`. - unsafe impl<T: DynSync + ?Sized> DynSend for &T {} - - macro_rules! impls_dyn_send_neg { - ($([$t1: ty $(where $($generics1: tt)*)?])*) => { - $(impl$(<$($generics1)*>)? !DynSend for $t1 {})* - }; - } - - // Consistent with `std` - impls_dyn_send_neg!( - [std::env::Args] - [std::env::ArgsOs] - [*const T where T: ?Sized] - [*mut T where T: ?Sized] - [std::ptr::NonNull<T> where T: ?Sized] - [std::rc::Rc<T> where T: ?Sized] - [std::rc::Weak<T> where T: ?Sized] - [std::sync::MutexGuard<'_, T> where T: ?Sized] - [std::sync::RwLockReadGuard<'_, T> where T: ?Sized] - [std::sync::RwLockWriteGuard<'_, T> where T: ?Sized] - [std::io::StdoutLock<'_>] - [std::io::StderrLock<'_>] - ); - - #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] - // Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms - impl !DynSend for std::env::VarsOs {} - - macro_rules! already_send { - ($([$ty: ty])*) => { - $(unsafe impl DynSend for $ty where $ty: Send {})* - }; - } - - // These structures are already `Send`. - already_send!( - [std::backtrace::Backtrace] - [std::io::Stdout] - [std::io::Stderr] - [std::io::Error] - [std::fs::File] - [rustc_arena::DroplessArena] - [crate::memmap::Mmap] - [crate::profiling::SelfProfiler] - [crate::owned_slice::OwnedSlice] - ); - - macro_rules! impl_dyn_send { - ($($($attr: meta)* [$ty: ty where $($generics2: tt)*])*) => { - $(unsafe impl<$($generics2)*> DynSend for $ty {})* - }; - } - - impl_dyn_send!( - [std::sync::atomic::AtomicPtr<T> where T] - [std::sync::Mutex<T> where T: ?Sized+ DynSend] - [std::sync::mpsc::Sender<T> where T: DynSend] - [std::sync::Arc<T> where T: ?Sized + DynSync + DynSend] - [std::sync::LazyLock<T, F> where T: DynSend, F: DynSend] - [std::collections::HashSet<K, S> where K: DynSend, S: DynSend] - [std::collections::HashMap<K, V, S> where K: DynSend, V: DynSend, S: DynSend] - [std::collections::BTreeMap<K, V, A> where K: DynSend, V: DynSend, A: std::alloc::Allocator + Clone + DynSend] - [Vec<T, A> where T: DynSend, A: std::alloc::Allocator + DynSend] - [Box<T, A> where T: ?Sized + DynSend, A: std::alloc::Allocator + DynSend] - [crate::sync::RwLock<T> where T: DynSend] - [crate::tagged_ptr::CopyTaggedPtr<P, T, CP> where P: Send + crate::tagged_ptr::Pointer, T: Send + crate::tagged_ptr::Tag, const CP: bool] - [rustc_arena::TypedArena<T> where T: DynSend] - [indexmap::IndexSet<V, S> where V: DynSend, S: DynSend] - [indexmap::IndexMap<K, V, S> where K: DynSend, V: DynSend, S: DynSend] - [thin_vec::ThinVec<T> where T: DynSend] - [smallvec::SmallVec<A> where A: smallvec::Array + DynSend] - ); - - macro_rules! impls_dyn_sync_neg { - ($([$t1: ty $(where $($generics1: tt)*)?])*) => { - $(impl$(<$($generics1)*>)? !DynSync for $t1 {})* - }; - } - - // Consistent with `std` - impls_dyn_sync_neg!( - [std::env::Args] - [std::env::ArgsOs] - [*const T where T: ?Sized] - [*mut T where T: ?Sized] - [std::cell::Cell<T> where T: ?Sized] - [std::cell::RefCell<T> where T: ?Sized] - [std::cell::UnsafeCell<T> where T: ?Sized] - [std::ptr::NonNull<T> where T: ?Sized] - [std::rc::Rc<T> where T: ?Sized] - [std::rc::Weak<T> where T: ?Sized] - [std::cell::OnceCell<T> where T] - [std::sync::mpsc::Receiver<T> where T] - [std::sync::mpsc::Sender<T> where T] - ); - - #[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] - // Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms - impl !DynSync for std::env::VarsOs {} - - macro_rules! already_sync { - ($([$ty: ty])*) => { - $(unsafe impl DynSync for $ty where $ty: Sync {})* - }; - } +#[rustc_on_unimplemented(message = "`{Self}` doesn't implement `DynSend`. \ + Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Send`")] +// This is an auto trait for types which can be sent across threads if `sync::is_dyn_thread_safe()` +// is true. These types can be wrapped in a `FromDyn` to get a `Send` type. Wrapping a +// `Send` type in `IntoDynSyncSend` will create a `DynSend` type. +pub unsafe auto trait DynSend {} + +#[rustc_on_unimplemented(message = "`{Self}` doesn't implement `DynSync`. \ + Add it to `rustc_data_structures::marker` or use `IntoDynSyncSend` if it's already `Sync`")] +// This is an auto trait for types which can be shared across threads if `sync::is_dyn_thread_safe()` +// is true. These types can be wrapped in a `FromDyn` to get a `Sync` type. Wrapping a +// `Sync` type in `IntoDynSyncSend` will create a `DynSync` type. +pub unsafe auto trait DynSync {} + +// Same with `Sync` and `Send`. +unsafe impl<T: DynSync + ?Sized> DynSend for &T {} + +macro_rules! impls_dyn_send_neg { + ($([$t1: ty $(where $($generics1: tt)*)?])*) => { + $(impl$(<$($generics1)*>)? !DynSend for $t1 {})* + }; +} - // These structures are already `Sync`. - already_sync!( - [std::sync::atomic::AtomicBool] - [std::sync::atomic::AtomicUsize] - [std::sync::atomic::AtomicU8] - [std::sync::atomic::AtomicU32] - [std::backtrace::Backtrace] - [std::io::Error] - [std::fs::File] - [jobserver_crate::Client] - [crate::memmap::Mmap] - [crate::profiling::SelfProfiler] - [crate::owned_slice::OwnedSlice] - ); +// Consistent with `std` +impls_dyn_send_neg!( + [std::env::Args] + [std::env::ArgsOs] + [*const T where T: ?Sized] + [*mut T where T: ?Sized] + [std::ptr::NonNull<T> where T: ?Sized] + [std::rc::Rc<T> where T: ?Sized] + [std::rc::Weak<T> where T: ?Sized] + [std::sync::MutexGuard<'_, T> where T: ?Sized] + [std::sync::RwLockReadGuard<'_, T> where T: ?Sized] + [std::sync::RwLockWriteGuard<'_, T> where T: ?Sized] + [std::io::StdoutLock<'_>] + [std::io::StderrLock<'_>] +); + +#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] +// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms +impl !DynSend for std::env::VarsOs {} + +macro_rules! already_send { + ($([$ty: ty])*) => { + $(unsafe impl DynSend for $ty where $ty: Send {})* + }; +} - // Use portable AtomicU64 for targets without native 64-bit atomics - #[cfg(target_has_atomic = "64")] - already_sync!( - [std::sync::atomic::AtomicU64] - ); +// These structures are already `Send`. +already_send!( + [std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File] + [rustc_arena::DroplessArena][crate::memmap::Mmap][crate::profiling::SelfProfiler] + [crate::owned_slice::OwnedSlice] +); + +macro_rules! impl_dyn_send { + ($($($attr: meta)* [$ty: ty where $($generics2: tt)*])*) => { + $(unsafe impl<$($generics2)*> DynSend for $ty {})* + }; +} - #[cfg(not(target_has_atomic = "64"))] - already_sync!( - [portable_atomic::AtomicU64] - ); +impl_dyn_send!( + [std::sync::atomic::AtomicPtr<T> where T] + [std::sync::Mutex<T> where T: ?Sized+ DynSend] + [std::sync::mpsc::Sender<T> where T: DynSend] + [std::sync::Arc<T> where T: ?Sized + DynSync + DynSend] + [std::sync::LazyLock<T, F> where T: DynSend, F: DynSend] + [std::collections::HashSet<K, S> where K: DynSend, S: DynSend] + [std::collections::HashMap<K, V, S> where K: DynSend, V: DynSend, S: DynSend] + [std::collections::BTreeMap<K, V, A> where K: DynSend, V: DynSend, A: std::alloc::Allocator + Clone + DynSend] + [Vec<T, A> where T: DynSend, A: std::alloc::Allocator + DynSend] + [Box<T, A> where T: ?Sized + DynSend, A: std::alloc::Allocator + DynSend] + [crate::sync::RwLock<T> where T: DynSend] + [crate::tagged_ptr::CopyTaggedPtr<P, T, CP> where P: Send + crate::tagged_ptr::Pointer, T: Send + crate::tagged_ptr::Tag, const CP: bool] + [rustc_arena::TypedArena<T> where T: DynSend] + [indexmap::IndexSet<V, S> where V: DynSend, S: DynSend] + [indexmap::IndexMap<K, V, S> where K: DynSend, V: DynSend, S: DynSend] + [thin_vec::ThinVec<T> where T: DynSend] + [smallvec::SmallVec<A> where A: smallvec::Array + DynSend] +); + +macro_rules! impls_dyn_sync_neg { + ($([$t1: ty $(where $($generics1: tt)*)?])*) => { + $(impl$(<$($generics1)*>)? !DynSync for $t1 {})* + }; +} - macro_rules! impl_dyn_sync { - ($($($attr: meta)* [$ty: ty where $($generics2: tt)*])*) => { - $(unsafe impl<$($generics2)*> DynSync for $ty {})* - }; - } +// Consistent with `std` +impls_dyn_sync_neg!( + [std::env::Args] + [std::env::ArgsOs] + [*const T where T: ?Sized] + [*mut T where T: ?Sized] + [std::cell::Cell<T> where T: ?Sized] + [std::cell::RefCell<T> where T: ?Sized] + [std::cell::UnsafeCell<T> where T: ?Sized] + [std::ptr::NonNull<T> where T: ?Sized] + [std::rc::Rc<T> where T: ?Sized] + [std::rc::Weak<T> where T: ?Sized] + [std::cell::OnceCell<T> where T] + [std::sync::mpsc::Receiver<T> where T] + [std::sync::mpsc::Sender<T> where T] +); + +#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] +// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms +impl !DynSync for std::env::VarsOs {} + +macro_rules! already_sync { + ($([$ty: ty])*) => { + $(unsafe impl DynSync for $ty where $ty: Sync {})* + }; +} - impl_dyn_sync!( - [std::sync::atomic::AtomicPtr<T> where T] - [std::sync::OnceLock<T> where T: DynSend + DynSync] - [std::sync::Mutex<T> where T: ?Sized + DynSend] - [std::sync::Arc<T> where T: ?Sized + DynSync + DynSend] - [std::sync::LazyLock<T, F> where T: DynSend + DynSync, F: DynSend] - [std::collections::HashSet<K, S> where K: DynSync, S: DynSync] - [std::collections::HashMap<K, V, S> where K: DynSync, V: DynSync, S: DynSync] - [std::collections::BTreeMap<K, V, A> where K: DynSync, V: DynSync, A: std::alloc::Allocator + Clone + DynSync] - [Vec<T, A> where T: DynSync, A: std::alloc::Allocator + DynSync] - [Box<T, A> where T: ?Sized + DynSync, A: std::alloc::Allocator + DynSync] - [crate::sync::RwLock<T> where T: DynSend + DynSync] - [crate::sync::WorkerLocal<T> where T: DynSend] - [crate::intern::Interned<'a, T> where 'a, T: DynSync] - [crate::tagged_ptr::CopyTaggedPtr<P, T, CP> where P: Sync + crate::tagged_ptr::Pointer, T: Sync + crate::tagged_ptr::Tag, const CP: bool] - [parking_lot::lock_api::Mutex<R, T> where R: DynSync, T: ?Sized + DynSend] - [parking_lot::lock_api::RwLock<R, T> where R: DynSync, T: ?Sized + DynSend + DynSync] - [indexmap::IndexSet<V, S> where V: DynSync, S: DynSync] - [indexmap::IndexMap<K, V, S> where K: DynSync, V: DynSync, S: DynSync] - [smallvec::SmallVec<A> where A: smallvec::Array + DynSync] - [thin_vec::ThinVec<T> where T: DynSync] - ); - } +// These structures are already `Sync`. +already_sync!( + [std::sync::atomic::AtomicBool][std::sync::atomic::AtomicUsize][std::sync::atomic::AtomicU8] + [std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Error][std::fs::File] + [jobserver_crate::Client][crate::memmap::Mmap][crate::profiling::SelfProfiler] + [crate::owned_slice::OwnedSlice] +); + +// Use portable AtomicU64 for targets without native 64-bit atomics +#[cfg(target_has_atomic = "64")] +already_sync!([std::sync::atomic::AtomicU64]); + +#[cfg(not(target_has_atomic = "64"))] +already_sync!([portable_atomic::AtomicU64]); + +macro_rules! impl_dyn_sync { + ($($($attr: meta)* [$ty: ty where $($generics2: tt)*])*) => { + $(unsafe impl<$($generics2)*> DynSync for $ty {})* + }; } +impl_dyn_sync!( + [std::sync::atomic::AtomicPtr<T> where T] + [std::sync::OnceLock<T> where T: DynSend + DynSync] + [std::sync::Mutex<T> where T: ?Sized + DynSend] + [std::sync::Arc<T> where T: ?Sized + DynSync + DynSend] + [std::sync::LazyLock<T, F> where T: DynSend + DynSync, F: DynSend] + [std::collections::HashSet<K, S> where K: DynSync, S: DynSync] + [std::collections::HashMap<K, V, S> where K: DynSync, V: DynSync, S: DynSync] + [std::collections::BTreeMap<K, V, A> where K: DynSync, V: DynSync, A: std::alloc::Allocator + Clone + DynSync] + [Vec<T, A> where T: DynSync, A: std::alloc::Allocator + DynSync] + [Box<T, A> where T: ?Sized + DynSync, A: std::alloc::Allocator + DynSync] + [crate::sync::RwLock<T> where T: DynSend + DynSync] + [crate::sync::WorkerLocal<T> where T: DynSend] + [crate::intern::Interned<'a, T> where 'a, T: DynSync] + [crate::tagged_ptr::CopyTaggedPtr<P, T, CP> where P: Sync + crate::tagged_ptr::Pointer, T: Sync + crate::tagged_ptr::Tag, const CP: bool] + [parking_lot::lock_api::Mutex<R, T> where R: DynSync, T: ?Sized + DynSend] + [parking_lot::lock_api::RwLock<R, T> where R: DynSync, T: ?Sized + DynSend + DynSync] + [indexmap::IndexSet<V, S> where V: DynSync, S: DynSync] + [indexmap::IndexMap<K, V, S> where K: DynSync, V: DynSync, S: DynSync] + [smallvec::SmallVec<A> where A: smallvec::Array + DynSync] + [thin_vec::ThinVec<T> where T: DynSync] +); + pub fn assert_dyn_sync<T: ?Sized + DynSync>() {} pub fn assert_dyn_send<T: ?Sized + DynSend>() {} pub fn assert_dyn_send_val<T: ?Sized + DynSend>(_t: &T) {} @@ -203,7 +171,6 @@ impl<T> FromDyn<T> { // Check that `sync::is_dyn_thread_safe()` is true on creation so we can // implement `Send` and `Sync` for this structure when `T` // implements `DynSend` and `DynSync` respectively. - #[cfg(parallel_compiler)] assert!(crate::sync::is_dyn_thread_safe()); FromDyn(val) } @@ -215,11 +182,9 @@ impl<T> FromDyn<T> { } // `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true. -#[cfg(parallel_compiler)] unsafe impl<T: DynSend> Send for FromDyn<T> {} // `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true. -#[cfg(parallel_compiler)] unsafe impl<T: DynSync> Sync for FromDyn<T> {} impl<T> std::ops::Deref for FromDyn<T> { @@ -237,9 +202,7 @@ impl<T> std::ops::Deref for FromDyn<T> { #[derive(Copy, Clone)] pub struct IntoDynSyncSend<T: ?Sized>(pub T); -#[cfg(parallel_compiler)] unsafe impl<T: ?Sized + Send> DynSend for IntoDynSyncSend<T> {} -#[cfg(parallel_compiler)] unsafe impl<T: ?Sized + Sync> DynSync for IntoDynSyncSend<T> {} impl<T> std::ops::Deref for IntoDynSyncSend<T> { diff --git a/compiler/rustc_data_structures/src/owned_slice.rs b/compiler/rustc_data_structures/src/owned_slice.rs index bbe6691e548..c8be0ab52e9 100644 --- a/compiler/rustc_data_structures/src/owned_slice.rs +++ b/compiler/rustc_data_structures/src/owned_slice.rs @@ -139,11 +139,9 @@ impl Borrow<[u8]> for OwnedSlice { } // Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Arc<dyn Send + Sync>)`, which is `Send` -#[cfg(parallel_compiler)] unsafe impl sync::Send for OwnedSlice {} // Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Arc<dyn Send + Sync>)`, which is `Sync` -#[cfg(parallel_compiler)] unsafe impl sync::Sync for OwnedSlice {} #[cfg(test)] diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs index d0b6fe2bc6f..65488c73d3c 100644 --- a/compiler/rustc_data_structures/src/sharded.rs +++ b/compiler/rustc_data_structures/src/sharded.rs @@ -3,27 +3,22 @@ use std::collections::hash_map::RawEntryMut; use std::hash::{Hash, Hasher}; use std::{iter, mem}; -#[cfg(parallel_compiler)] use either::Either; use crate::fx::{FxHashMap, FxHasher}; -#[cfg(parallel_compiler)] -use crate::sync::{CacheAligned, is_dyn_thread_safe}; -use crate::sync::{Lock, LockGuard, Mode}; +use crate::sync::{CacheAligned, Lock, LockGuard, Mode, is_dyn_thread_safe}; // 32 shards is sufficient to reduce contention on an 8-core Ryzen 7 1700, // but this should be tested on higher core count CPUs. How the `Sharded` type gets used // may also affect the ideal number of shards. const SHARD_BITS: usize = 5; -#[cfg(parallel_compiler)] const SHARDS: usize = 1 << SHARD_BITS; /// An array of cache-line aligned inner locked structures with convenience methods. /// A single field is used when the compiler uses only one thread. pub enum Sharded<T> { Single(Lock<T>), - #[cfg(parallel_compiler)] Shards(Box<[CacheAligned<Lock<T>>; SHARDS]>), } @@ -37,7 +32,6 @@ impl<T: Default> Default for Sharded<T> { impl<T> Sharded<T> { #[inline] pub fn new(mut value: impl FnMut() -> T) -> Self { - #[cfg(parallel_compiler)] if is_dyn_thread_safe() { return Sharded::Shards(Box::new( [(); SHARDS].map(|()| CacheAligned(Lock::new(value()))), @@ -52,7 +46,6 @@ impl<T> Sharded<T> { pub fn get_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> &Lock<T> { match self { Self::Single(single) => single, - #[cfg(parallel_compiler)] Self::Shards(..) => self.get_shard_by_hash(make_hash(_val)), } } @@ -66,7 +59,6 @@ impl<T> Sharded<T> { pub fn get_shard_by_index(&self, _i: usize) -> &Lock<T> { match self { Self::Single(single) => single, - #[cfg(parallel_compiler)] Self::Shards(shards) => { // SAFETY: The index gets ANDed with the shard mask, ensuring it is always inbounds. unsafe { &shards.get_unchecked(_i & (SHARDS - 1)).0 } @@ -87,7 +79,6 @@ impl<T> Sharded<T> { // `might_be_dyn_thread_safe` was also false. unsafe { single.lock_assume(Mode::NoSync) } } - #[cfg(parallel_compiler)] Self::Shards(..) => self.lock_shard_by_hash(make_hash(_val)), } } @@ -110,7 +101,6 @@ impl<T> Sharded<T> { // `might_be_dyn_thread_safe` was also false. unsafe { single.lock_assume(Mode::NoSync) } } - #[cfg(parallel_compiler)] Self::Shards(shards) => { // Synchronization is enabled so use the `lock_assume_sync` method optimized // for that case. @@ -127,11 +117,7 @@ impl<T> Sharded<T> { #[inline] pub fn lock_shards(&self) -> impl Iterator<Item = LockGuard<'_, T>> { match self { - #[cfg(not(parallel_compiler))] - Self::Single(single) => iter::once(single.lock()), - #[cfg(parallel_compiler)] Self::Single(single) => Either::Left(iter::once(single.lock())), - #[cfg(parallel_compiler)] Self::Shards(shards) => Either::Right(shards.iter().map(|shard| shard.0.lock())), } } @@ -139,11 +125,7 @@ impl<T> Sharded<T> { #[inline] pub fn try_lock_shards(&self) -> impl Iterator<Item = Option<LockGuard<'_, T>>> { match self { - #[cfg(not(parallel_compiler))] - Self::Single(single) => iter::once(single.try_lock()), - #[cfg(parallel_compiler)] Self::Single(single) => Either::Left(iter::once(single.try_lock())), - #[cfg(parallel_compiler)] Self::Shards(shards) => Either::Right(shards.iter().map(|shard| shard.0.try_lock())), } } @@ -151,7 +133,6 @@ impl<T> Sharded<T> { #[inline] pub fn shards() -> usize { - #[cfg(parallel_compiler)] if is_dyn_thread_safe() { return SHARDS; } diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index a3491dbfec7..7a9533031f4 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -54,9 +54,7 @@ mod worker_local; pub use worker_local::{Registry, WorkerLocal}; mod parallel; -#[cfg(parallel_compiler)] -pub use parallel::scope; -pub use parallel::{join, par_for_each_in, par_map, parallel_guard, try_par_for_each_in}; +pub use parallel::{join, par_for_each_in, par_map, parallel_guard, scope, try_par_for_each_in}; pub use vec::{AppendOnlyIndexVec, AppendOnlyVec}; mod vec; @@ -104,226 +102,66 @@ mod mode { } } -pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; - -cfg_match! { - cfg(not(parallel_compiler)) => { - use std::ops::Add; - use std::cell::Cell; - use std::sync::atomic::Ordering; - - pub unsafe auto trait Send {} - pub unsafe auto trait Sync {} - - unsafe impl<T> Send for T {} - unsafe impl<T> Sync for T {} - - /// This is a single threaded variant of `AtomicU64`, `AtomicUsize`, etc. - /// It has explicit ordering arguments and is only intended for use with - /// the native atomic types. - /// You should use this type through the `AtomicU64`, `AtomicUsize`, etc, type aliases - /// as it's not intended to be used separately. - #[derive(Debug, Default)] - pub struct Atomic<T: Copy>(Cell<T>); - - impl<T: Copy> Atomic<T> { - #[inline] - pub fn new(v: T) -> Self { - Atomic(Cell::new(v)) - } - - #[inline] - pub fn into_inner(self) -> T { - self.0.into_inner() - } - - #[inline] - pub fn load(&self, _: Ordering) -> T { - self.0.get() - } - - #[inline] - pub fn store(&self, val: T, _: Ordering) { - self.0.set(val) - } - - #[inline] - pub fn swap(&self, val: T, _: Ordering) -> T { - self.0.replace(val) - } - } +// FIXME(parallel_compiler): Get rid of these aliases across the compiler. - impl Atomic<bool> { - pub fn fetch_or(&self, val: bool, _: Ordering) -> bool { - let old = self.0.get(); - self.0.set(val | old); - old - } - pub fn fetch_and(&self, val: bool, _: Ordering) -> bool { - let old = self.0.get(); - self.0.set(val & old); - old - } - } +pub use std::marker::{Send, Sync}; +// Use portable AtomicU64 for targets without native 64-bit atomics +#[cfg(target_has_atomic = "64")] +pub use std::sync::atomic::AtomicU64; +pub use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize}; +pub use std::sync::{Arc as Lrc, OnceLock, Weak}; - impl<T: Copy + PartialEq> Atomic<T> { - #[inline] - pub fn compare_exchange(&self, - current: T, - new: T, - _: Ordering, - _: Ordering) - -> Result<T, T> { - let read = self.0.get(); - if read == current { - self.0.set(new); - Ok(read) - } else { - Err(read) - } - } - } +pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; +pub use parking_lot::{ + MappedMutexGuard as MappedLockGuard, MappedRwLockReadGuard as MappedReadGuard, + MappedRwLockWriteGuard as MappedWriteGuard, RwLockReadGuard as ReadGuard, + RwLockWriteGuard as WriteGuard, +}; +#[cfg(not(target_has_atomic = "64"))] +pub use portable_atomic::AtomicU64; - impl<T: Add<Output=T> + Copy> Atomic<T> { - #[inline] - pub fn fetch_add(&self, val: T, _: Ordering) -> T { - let old = self.0.get(); - self.0.set(old + val); - old - } - } +pub type LRef<'a, T> = &'a T; - pub type AtomicUsize = Atomic<usize>; - pub type AtomicBool = Atomic<bool>; - pub type AtomicU32 = Atomic<u32>; - pub type AtomicU64 = Atomic<u64>; - - 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 LRef<'a, T> = &'a mut T; - - #[derive(Debug, Default)] - pub struct MTLock<T>(T); - - impl<T> MTLock<T> { - #[inline(always)] - pub fn new(inner: T) -> Self { - MTLock(inner) - } - - #[inline(always)] - pub fn into_inner(self) -> T { - self.0 - } - - #[inline(always)] - pub fn get_mut(&mut self) -> &mut T { - &mut self.0 - } - - #[inline(always)] - pub fn lock(&self) -> &T { - &self.0 - } - - #[inline(always)] - pub fn lock_mut(&mut self) -> &mut T { - &mut self.0 - } - } +#[derive(Debug, Default)] +pub struct MTLock<T>(Lock<T>); - // FIXME: Probably a bad idea (in the threaded case) - impl<T: Clone> Clone for MTLock<T> { - #[inline] - fn clone(&self) -> Self { - MTLock(self.0.clone()) - } - } +impl<T> MTLock<T> { + #[inline(always)] + pub fn new(inner: T) -> Self { + MTLock(Lock::new(inner)) } - _ => { - pub use std::marker::Send as Send; - pub use std::marker::Sync as Sync; - - pub use parking_lot::RwLockReadGuard as ReadGuard; - pub use parking_lot::MappedRwLockReadGuard as MappedReadGuard; - pub use parking_lot::RwLockWriteGuard as WriteGuard; - pub use parking_lot::MappedRwLockWriteGuard as MappedWriteGuard; - - pub use parking_lot::MappedMutexGuard as MappedLockGuard; - pub use std::sync::OnceLock; - - pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32}; - - // Use portable AtomicU64 for targets without native 64-bit atomics - #[cfg(target_has_atomic = "64")] - pub use std::sync::atomic::AtomicU64; - - #[cfg(not(target_has_atomic = "64"))] - pub use portable_atomic::AtomicU64; - - pub use std::sync::Arc as Lrc; - pub use std::sync::Weak as Weak; - - pub type LRef<'a, T> = &'a T; - - #[derive(Debug, Default)] - pub struct MTLock<T>(Lock<T>); - - impl<T> MTLock<T> { - #[inline(always)] - pub fn new(inner: T) -> Self { - MTLock(Lock::new(inner)) - } - - #[inline(always)] - pub fn into_inner(self) -> T { - self.0.into_inner() - } - - #[inline(always)] - pub fn get_mut(&mut self) -> &mut T { - self.0.get_mut() - } - - #[inline(always)] - pub fn lock(&self) -> LockGuard<'_, T> { - self.0.lock() - } + #[inline(always)] + pub fn into_inner(self) -> T { + self.0.into_inner() + } - #[inline(always)] - pub fn lock_mut(&self) -> LockGuard<'_, T> { - self.lock() - } - } + #[inline(always)] + pub fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } - use parking_lot::RwLock as InnerRwLock; + #[inline(always)] + pub fn lock(&self) -> LockGuard<'_, T> { + self.0.lock() + } - /// This makes locks panic if they are already held. - /// It is only useful when you are running in a single thread - const ERROR_CHECKING: bool = false; + #[inline(always)] + pub fn lock_mut(&self) -> LockGuard<'_, T> { + self.lock() } } +use parking_lot::RwLock as InnerRwLock; + +/// This makes locks panic if they are already held. +/// It is only useful when you are running in a single thread +const ERROR_CHECKING: bool = false; + pub type MTLockRef<'a, T> = LRef<'a, MTLock<T>>; #[derive(Default)] -#[cfg_attr(parallel_compiler, repr(align(64)))] +#[repr(align(64))] pub struct CacheAligned<T>(pub T); pub trait HashMapExt<K, V> { @@ -357,14 +195,6 @@ impl<T> RwLock<T> { self.0.get_mut() } - #[cfg(not(parallel_compiler))] - #[inline(always)] - #[track_caller] - pub fn read(&self) -> ReadGuard<'_, T> { - self.0.borrow() - } - - #[cfg(parallel_compiler)] #[inline(always)] pub fn read(&self) -> ReadGuard<'_, T> { if ERROR_CHECKING { @@ -380,26 +210,11 @@ impl<T> RwLock<T> { f(&*self.read()) } - #[cfg(not(parallel_compiler))] - #[inline(always)] - pub fn try_write(&self) -> Result<WriteGuard<'_, T>, ()> { - self.0.try_borrow_mut().map_err(|_| ()) - } - - #[cfg(parallel_compiler)] #[inline(always)] pub fn try_write(&self) -> Result<WriteGuard<'_, T>, ()> { self.0.try_write().ok_or(()) } - #[cfg(not(parallel_compiler))] - #[inline(always)] - #[track_caller] - pub fn write(&self) -> WriteGuard<'_, T> { - self.0.borrow_mut() - } - - #[cfg(parallel_compiler)] #[inline(always)] pub fn write(&self) -> WriteGuard<'_, T> { if ERROR_CHECKING { @@ -427,13 +242,6 @@ impl<T> RwLock<T> { self.write() } - #[cfg(not(parallel_compiler))] - #[inline(always)] - pub fn leak(&self) -> &T { - ReadGuard::leak(self.read()) - } - - #[cfg(parallel_compiler)] #[inline(always)] pub fn leak(&self) -> &T { let guard = self.read(); diff --git a/compiler/rustc_data_structures/src/sync/freeze.rs b/compiler/rustc_data_structures/src/sync/freeze.rs index fad5f583d1c..5236c9fe156 100644 --- a/compiler/rustc_data_structures/src/sync/freeze.rs +++ b/compiler/rustc_data_structures/src/sync/freeze.rs @@ -5,9 +5,7 @@ use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; use std::sync::atomic::Ordering; -use crate::sync::{AtomicBool, ReadGuard, RwLock, WriteGuard}; -#[cfg(parallel_compiler)] -use crate::sync::{DynSend, DynSync}; +use crate::sync::{AtomicBool, DynSend, DynSync, ReadGuard, RwLock, WriteGuard}; /// A type which allows mutation using a lock until /// the value is frozen and can be accessed lock-free. @@ -22,7 +20,6 @@ pub struct FreezeLock<T> { lock: RwLock<()>, } -#[cfg(parallel_compiler)] unsafe impl<T: DynSync + DynSend> DynSync for FreezeLock<T> {} impl<T> FreezeLock<T> { diff --git a/compiler/rustc_data_structures/src/sync/lock.rs b/compiler/rustc_data_structures/src/sync/lock.rs index 012ee7f900e..2ccf06ccd4f 100644 --- a/compiler/rustc_data_structures/src/sync/lock.rs +++ b/compiler/rustc_data_structures/src/sync/lock.rs @@ -1,236 +1,177 @@ //! This module implements a lock which only uses synchronization if `might_be_dyn_thread_safe` is true. //! It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync` traits. -//! -//! When `cfg(parallel_compiler)` is not set, the lock is instead a wrapper around `RefCell`. #![allow(dead_code)] use std::fmt; -#[cfg(parallel_compiler)] -pub use maybe_sync::*; -#[cfg(not(parallel_compiler))] -pub use no_sync::*; - #[derive(Clone, Copy, PartialEq)] pub enum Mode { NoSync, Sync, } -mod maybe_sync { - use std::cell::{Cell, UnsafeCell}; - use std::intrinsics::unlikely; - use std::marker::PhantomData; - use std::mem::ManuallyDrop; - use std::ops::{Deref, DerefMut}; +use std::cell::{Cell, UnsafeCell}; +use std::intrinsics::unlikely; +use std::marker::PhantomData; +use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; - use parking_lot::RawMutex; - use parking_lot::lock_api::RawMutex as _; +use parking_lot::RawMutex; +use parking_lot::lock_api::RawMutex as _; - use super::Mode; - use crate::sync::mode; - #[cfg(parallel_compiler)] - use crate::sync::{DynSend, DynSync}; +use crate::sync::{DynSend, DynSync, mode}; - /// A guard holding mutable access to a `Lock` which is in a locked state. - #[must_use = "if unused the Lock will immediately unlock"] - pub struct LockGuard<'a, T> { - lock: &'a Lock<T>, - marker: PhantomData<&'a mut T>, +/// A guard holding mutable access to a `Lock` which is in a locked state. +#[must_use = "if unused the Lock will immediately unlock"] +pub struct LockGuard<'a, T> { + lock: &'a Lock<T>, + marker: PhantomData<&'a mut T>, - /// The synchronization mode of the lock. This is explicitly passed to let LLVM relate it - /// to the original lock operation. - mode: Mode, - } + /// The synchronization mode of the lock. This is explicitly passed to let LLVM relate it + /// to the original lock operation. + mode: Mode, +} - impl<'a, T: 'a> Deref for LockGuard<'a, T> { - type Target = T; - #[inline] - fn deref(&self) -> &T { - // SAFETY: We have shared access to the mutable access owned by this type, - // so we can give out a shared reference. - unsafe { &*self.lock.data.get() } - } +impl<'a, T: 'a> Deref for LockGuard<'a, T> { + type Target = T; + #[inline] + fn deref(&self) -> &T { + // SAFETY: We have shared access to the mutable access owned by this type, + // so we can give out a shared reference. + unsafe { &*self.lock.data.get() } } +} - impl<'a, T: 'a> DerefMut for LockGuard<'a, T> { - #[inline] - fn deref_mut(&mut self) -> &mut T { - // SAFETY: We have mutable access to the data so we can give out a mutable reference. - unsafe { &mut *self.lock.data.get() } - } +impl<'a, T: 'a> DerefMut for LockGuard<'a, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + // SAFETY: We have mutable access to the data so we can give out a mutable reference. + unsafe { &mut *self.lock.data.get() } } +} - impl<'a, T: 'a> Drop for LockGuard<'a, T> { - #[inline] - fn drop(&mut self) { - // SAFETY (union access): We get `self.mode` from the lock operation so it is consistent - // with the `lock.mode` state. This means we access the right union fields. - match self.mode { - Mode::NoSync => { - let cell = unsafe { &self.lock.mode_union.no_sync }; - debug_assert!(cell.get()); - cell.set(false); - } - // SAFETY (unlock): We know that the lock is locked as this type is a proof of that. - Mode::Sync => unsafe { self.lock.mode_union.sync.unlock() }, +impl<'a, T: 'a> Drop for LockGuard<'a, T> { + #[inline] + fn drop(&mut self) { + // SAFETY (union access): We get `self.mode` from the lock operation so it is consistent + // with the `lock.mode` state. This means we access the right union fields. + match self.mode { + Mode::NoSync => { + let cell = unsafe { &self.lock.mode_union.no_sync }; + debug_assert!(cell.get()); + cell.set(false); } + // SAFETY (unlock): We know that the lock is locked as this type is a proof of that. + Mode::Sync => unsafe { self.lock.mode_union.sync.unlock() }, } } +} - union ModeUnion { - /// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`. - no_sync: ManuallyDrop<Cell<bool>>, +union ModeUnion { + /// Indicates if the cell is locked. Only used if `Lock.mode` is `NoSync`. + no_sync: ManuallyDrop<Cell<bool>>, - /// A lock implementation that's only used if `Lock.mode` is `Sync`. - sync: ManuallyDrop<RawMutex>, - } + /// A lock implementation that's only used if `Lock.mode` is `Sync`. + sync: ManuallyDrop<RawMutex>, +} - /// The value representing a locked state for the `Cell`. - const LOCKED: bool = true; +/// The value representing a locked state for the `Cell`. +const LOCKED: bool = true; - /// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true. - /// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`. - pub struct Lock<T> { - /// Indicates if synchronization is used via `mode_union.sync` if it's `Sync`, or if a - /// not thread safe cell is used via `mode_union.no_sync` if it's `NoSync`. - /// This is set on initialization and never changed. - mode: Mode, +/// A lock which only uses synchronization if `might_be_dyn_thread_safe` is true. +/// It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync`. +pub struct Lock<T> { + /// Indicates if synchronization is used via `mode_union.sync` if it's `Sync`, or if a + /// not thread safe cell is used via `mode_union.no_sync` if it's `NoSync`. + /// This is set on initialization and never changed. + mode: Mode, - mode_union: ModeUnion, - data: UnsafeCell<T>, - } + mode_union: ModeUnion, + data: UnsafeCell<T>, +} - impl<T> Lock<T> { - #[inline(always)] - pub fn new(inner: T) -> Self { - let (mode, mode_union) = if unlikely(mode::might_be_dyn_thread_safe()) { - // Create the lock with synchronization enabled using the `RawMutex` type. - (Mode::Sync, ModeUnion { sync: ManuallyDrop::new(RawMutex::INIT) }) - } else { - // Create the lock with synchronization disabled. - (Mode::NoSync, ModeUnion { no_sync: ManuallyDrop::new(Cell::new(!LOCKED)) }) - }; - Lock { mode, mode_union, data: UnsafeCell::new(inner) } - } +impl<T> Lock<T> { + #[inline(always)] + pub fn new(inner: T) -> Self { + let (mode, mode_union) = if unlikely(mode::might_be_dyn_thread_safe()) { + // Create the lock with synchronization enabled using the `RawMutex` type. + (Mode::Sync, ModeUnion { sync: ManuallyDrop::new(RawMutex::INIT) }) + } else { + // Create the lock with synchronization disabled. + (Mode::NoSync, ModeUnion { no_sync: ManuallyDrop::new(Cell::new(!LOCKED)) }) + }; + Lock { mode, mode_union, data: UnsafeCell::new(inner) } + } - #[inline(always)] - pub fn into_inner(self) -> T { - self.data.into_inner() - } + #[inline(always)] + pub fn into_inner(self) -> T { + self.data.into_inner() + } - #[inline(always)] - pub fn get_mut(&mut self) -> &mut T { - self.data.get_mut() - } + #[inline(always)] + pub fn get_mut(&mut self) -> &mut T { + self.data.get_mut() + } - #[inline(always)] - pub fn try_lock(&self) -> Option<LockGuard<'_, T>> { - let mode = self.mode; - // SAFETY: This is safe since the union fields are used in accordance with `self.mode`. - match mode { - Mode::NoSync => { - let cell = unsafe { &self.mode_union.no_sync }; - let was_unlocked = cell.get() != LOCKED; - if was_unlocked { - cell.set(LOCKED); - } - was_unlocked + #[inline(always)] + pub fn try_lock(&self) -> Option<LockGuard<'_, T>> { + let mode = self.mode; + // SAFETY: This is safe since the union fields are used in accordance with `self.mode`. + match mode { + Mode::NoSync => { + let cell = unsafe { &self.mode_union.no_sync }; + let was_unlocked = cell.get() != LOCKED; + if was_unlocked { + cell.set(LOCKED); } - Mode::Sync => unsafe { self.mode_union.sync.try_lock() }, + was_unlocked } - .then(|| LockGuard { lock: self, marker: PhantomData, mode }) + Mode::Sync => unsafe { self.mode_union.sync.try_lock() }, } + .then(|| LockGuard { lock: self, marker: PhantomData, mode }) + } - /// This acquires the lock assuming synchronization is in a specific mode. - /// - /// Safety - /// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was - /// true on lock creation. - #[inline(always)] + /// This acquires the lock assuming synchronization is in a specific mode. + /// + /// Safety + /// This method must only be called with `Mode::Sync` if `might_be_dyn_thread_safe` was + /// true on lock creation. + #[inline(always)] + #[track_caller] + pub unsafe fn lock_assume(&self, mode: Mode) -> LockGuard<'_, T> { + #[inline(never)] #[track_caller] - pub unsafe fn lock_assume(&self, mode: Mode) -> LockGuard<'_, T> { - #[inline(never)] - #[track_caller] - #[cold] - fn lock_held() -> ! { - panic!("lock was already held") - } + #[cold] + fn lock_held() -> ! { + panic!("lock was already held") + } - // SAFETY: This is safe since the union fields are used in accordance with `mode` - // which also must match `self.mode` due to the safety precondition. - unsafe { - match mode { - Mode::NoSync => { - if unlikely(self.mode_union.no_sync.replace(LOCKED) == LOCKED) { - lock_held() - } + // SAFETY: This is safe since the union fields are used in accordance with `mode` + // which also must match `self.mode` due to the safety precondition. + unsafe { + match mode { + Mode::NoSync => { + if unlikely(self.mode_union.no_sync.replace(LOCKED) == LOCKED) { + lock_held() } - Mode::Sync => self.mode_union.sync.lock(), } + Mode::Sync => self.mode_union.sync.lock(), } - LockGuard { lock: self, marker: PhantomData, mode } - } - - #[inline(always)] - #[track_caller] - pub fn lock(&self) -> LockGuard<'_, T> { - unsafe { self.lock_assume(self.mode) } } + LockGuard { lock: self, marker: PhantomData, mode } } - #[cfg(parallel_compiler)] - unsafe impl<T: DynSend> DynSend for Lock<T> {} - #[cfg(parallel_compiler)] - unsafe impl<T: DynSend> DynSync for Lock<T> {} -} - -mod no_sync { - use std::cell::RefCell; - #[doc(no_inline)] - pub use std::cell::RefMut as LockGuard; - - use super::Mode; - - pub struct Lock<T>(RefCell<T>); - - impl<T> Lock<T> { - #[inline(always)] - pub fn new(inner: T) -> Self { - Lock(RefCell::new(inner)) - } - - #[inline(always)] - pub fn into_inner(self) -> T { - self.0.into_inner() - } - - #[inline(always)] - pub fn get_mut(&mut self) -> &mut T { - self.0.get_mut() - } - - #[inline(always)] - pub fn try_lock(&self) -> Option<LockGuard<'_, T>> { - self.0.try_borrow_mut().ok() - } - - #[inline(always)] - #[track_caller] - // This is unsafe to match the API for the `parallel_compiler` case. - pub unsafe fn lock_assume(&self, _mode: Mode) -> LockGuard<'_, T> { - self.0.borrow_mut() - } - - #[inline(always)] - #[track_caller] - pub fn lock(&self) -> LockGuard<'_, T> { - self.0.borrow_mut() - } + #[inline(always)] + #[track_caller] + pub fn lock(&self) -> LockGuard<'_, T> { + unsafe { self.lock_assume(self.mode) } } } +unsafe impl<T: DynSend> DynSend for Lock<T> {} +unsafe impl<T: DynSend> DynSync for Lock<T> {} + impl<T> Lock<T> { #[inline(always)] #[track_caller] diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs index c7df19842d6..1ba631b8623 100644 --- a/compiler/rustc_data_structures/src/sync/parallel.rs +++ b/compiler/rustc_data_structures/src/sync/parallel.rs @@ -6,14 +6,11 @@ use std::any::Any; use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; -#[cfg(not(parallel_compiler))] -pub use disabled::*; -#[cfg(parallel_compiler)] -pub use enabled::*; use parking_lot::Mutex; +use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator}; use crate::FatalErrorMarker; -use crate::sync::IntoDynSyncSend; +use crate::sync::{DynSend, DynSync, FromDyn, IntoDynSyncSend, mode}; /// A guard used to hold panics that occur during a parallel section to later by unwound. /// This is used for the parallel compiler to prevent fatal errors from non-deterministically @@ -49,65 +46,23 @@ pub fn parallel_guard<R>(f: impl FnOnce(&ParallelGuard) -> R) -> R { ret } -mod disabled { - use crate::sync::parallel_guard; - - #[macro_export] - #[cfg(not(parallel_compiler))] - macro_rules! parallel { - ($($blocks:block),*) => {{ - $crate::sync::parallel_guard(|guard| { - $(guard.run(|| $blocks);)* - }); - }} - } - - pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB) - where - A: FnOnce() -> RA, - B: FnOnce() -> RB, - { - let (a, b) = parallel_guard(|guard| { - let a = guard.run(oper_a); - let b = guard.run(oper_b); - (a, b) - }); - (a.unwrap(), b.unwrap()) - } - - pub fn par_for_each_in<T: IntoIterator>(t: T, mut for_each: impl FnMut(T::Item)) { - parallel_guard(|guard| { - t.into_iter().for_each(|i| { - guard.run(|| for_each(i)); - }); - }) - } - - pub fn try_par_for_each_in<T: IntoIterator, E>( - t: T, - mut for_each: impl FnMut(T::Item) -> Result<(), E>, - ) -> Result<(), E> { - parallel_guard(|guard| { - t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and) - }) - } - - pub fn par_map<T: IntoIterator, R, C: FromIterator<R>>( - t: T, - mut map: impl FnMut(<<T as IntoIterator>::IntoIter as Iterator>::Item) -> R, - ) -> C { - parallel_guard(|guard| t.into_iter().filter_map(|i| guard.run(|| map(i))).collect()) - } +pub fn serial_join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB) +where + A: FnOnce() -> RA, + B: FnOnce() -> RB, +{ + let (a, b) = parallel_guard(|guard| { + let a = guard.run(oper_a); + let b = guard.run(oper_b); + (a, b) + }); + (a.unwrap(), b.unwrap()) } -#[cfg(parallel_compiler)] -mod enabled { - use crate::sync::{DynSend, DynSync, FromDyn, mode, parallel_guard}; - - /// Runs a list of blocks in parallel. The first block is executed immediately on - /// the current thread. Use that for the longest running block. - #[macro_export] - macro_rules! parallel { +/// Runs a list of blocks in parallel. The first block is executed immediately on +/// the current thread. Use that for the longest running block. +#[macro_export] +macro_rules! parallel { (impl $fblock:block [$($c:expr,)*] [$block:expr $(, $rest:expr)*]) => { parallel!(impl $fblock [$block, $($c,)*] [$($rest),*]) }; @@ -139,92 +94,89 @@ mod enabled { }; } - // This function only works when `mode::is_dyn_thread_safe()`. - pub fn scope<'scope, OP, R>(op: OP) -> R - where - OP: FnOnce(&rayon::Scope<'scope>) -> R + DynSend, - R: DynSend, - { - let op = FromDyn::from(op); - rayon::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner() +// This function only works when `mode::is_dyn_thread_safe()`. +pub fn scope<'scope, OP, R>(op: OP) -> R +where + OP: FnOnce(&rayon::Scope<'scope>) -> R + DynSend, + R: DynSend, +{ + let op = FromDyn::from(op); + rayon::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner() +} + +#[inline] +pub fn join<A, B, RA: DynSend, RB: DynSend>(oper_a: A, oper_b: B) -> (RA, RB) +where + A: FnOnce() -> RA + DynSend, + B: FnOnce() -> RB + DynSend, +{ + if mode::is_dyn_thread_safe() { + let oper_a = FromDyn::from(oper_a); + let oper_b = FromDyn::from(oper_b); + let (a, b) = parallel_guard(|guard| { + rayon::join( + move || guard.run(move || FromDyn::from(oper_a.into_inner()())), + move || guard.run(move || FromDyn::from(oper_b.into_inner()())), + ) + }); + (a.unwrap().into_inner(), b.unwrap().into_inner()) + } else { + serial_join(oper_a, oper_b) } +} - #[inline] - pub fn join<A, B, RA: DynSend, RB: DynSend>(oper_a: A, oper_b: B) -> (RA, RB) - where - A: FnOnce() -> RA + DynSend, - B: FnOnce() -> RB + DynSend, - { +pub fn par_for_each_in<I, T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>>( + t: T, + for_each: impl Fn(I) + DynSync + DynSend, +) { + parallel_guard(|guard| { if mode::is_dyn_thread_safe() { - let oper_a = FromDyn::from(oper_a); - let oper_b = FromDyn::from(oper_b); - let (a, b) = parallel_guard(|guard| { - rayon::join( - move || guard.run(move || FromDyn::from(oper_a.into_inner()())), - move || guard.run(move || FromDyn::from(oper_b.into_inner()())), - ) + let for_each = FromDyn::from(for_each); + t.into_par_iter().for_each(|i| { + guard.run(|| for_each(i)); }); - (a.unwrap().into_inner(), b.unwrap().into_inner()) } else { - super::disabled::join(oper_a, oper_b) + t.into_iter().for_each(|i| { + guard.run(|| for_each(i)); + }); } - } - - use rayon::iter::{FromParallelIterator, IntoParallelIterator, ParallelIterator}; - - pub fn par_for_each_in<I, T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>>( - t: T, - for_each: impl Fn(I) + DynSync + DynSend, - ) { - parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { - let for_each = FromDyn::from(for_each); - t.into_par_iter().for_each(|i| { - guard.run(|| for_each(i)); - }); - } else { - t.into_iter().for_each(|i| { - guard.run(|| for_each(i)); - }); - } - }); - } + }); +} - pub fn try_par_for_each_in< - T: IntoIterator + IntoParallelIterator<Item = <T as IntoIterator>::Item>, - E: Send, - >( - t: T, - for_each: impl Fn(<T as IntoIterator>::Item) -> Result<(), E> + DynSync + DynSend, - ) -> Result<(), E> { - parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { - let for_each = FromDyn::from(for_each); - t.into_par_iter() - .filter_map(|i| guard.run(|| for_each(i))) - .reduce(|| Ok(()), Result::and) - } else { - t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and) - } - }) - } +pub fn try_par_for_each_in< + T: IntoIterator + IntoParallelIterator<Item = <T as IntoIterator>::Item>, + E: Send, +>( + t: T, + for_each: impl Fn(<T as IntoIterator>::Item) -> Result<(), E> + DynSync + DynSend, +) -> Result<(), E> { + parallel_guard(|guard| { + if mode::is_dyn_thread_safe() { + let for_each = FromDyn::from(for_each); + t.into_par_iter() + .filter_map(|i| guard.run(|| for_each(i))) + .reduce(|| Ok(()), Result::and) + } else { + t.into_iter().filter_map(|i| guard.run(|| for_each(i))).fold(Ok(()), Result::and) + } + }) +} - pub fn par_map< - I, - T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>, - R: std::marker::Send, - C: FromIterator<R> + FromParallelIterator<R>, - >( - t: T, - map: impl Fn(I) -> R + DynSync + DynSend, - ) -> C { - parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { - let map = FromDyn::from(map); - t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect() - } else { - t.into_iter().filter_map(|i| guard.run(|| map(i))).collect() - } - }) - } +pub fn par_map< + I, + T: IntoIterator<Item = I> + IntoParallelIterator<Item = I>, + R: std::marker::Send, + C: FromIterator<R> + FromParallelIterator<R>, +>( + t: T, + map: impl Fn(I) -> R + DynSync + DynSend, +) -> C { + parallel_guard(|guard| { + if mode::is_dyn_thread_safe() { + let map = FromDyn::from(map); + t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect() + } else { + t.into_iter().filter_map(|i| guard.run(|| map(i))).collect() + } + }) } diff --git a/compiler/rustc_data_structures/src/sync/vec.rs b/compiler/rustc_data_structures/src/sync/vec.rs index 314496ce9f0..21ec5cf6c13 100644 --- a/compiler/rustc_data_structures/src/sync/vec.rs +++ b/compiler/rustc_data_structures/src/sync/vec.rs @@ -4,40 +4,23 @@ use rustc_index::Idx; #[derive(Default)] pub struct AppendOnlyIndexVec<I: Idx, T: Copy> { - #[cfg(not(parallel_compiler))] - vec: elsa::vec::FrozenVec<T>, - #[cfg(parallel_compiler)] vec: elsa::sync::LockFreeFrozenVec<T>, _marker: PhantomData<fn(&I)>, } impl<I: Idx, T: Copy> AppendOnlyIndexVec<I, T> { pub fn new() -> Self { - Self { - #[cfg(not(parallel_compiler))] - vec: elsa::vec::FrozenVec::new(), - #[cfg(parallel_compiler)] - vec: elsa::sync::LockFreeFrozenVec::new(), - _marker: PhantomData, - } + Self { vec: elsa::sync::LockFreeFrozenVec::new(), _marker: PhantomData } } pub fn push(&self, val: T) -> I { - #[cfg(not(parallel_compiler))] - let i = self.vec.len(); - #[cfg(not(parallel_compiler))] - self.vec.push(val); - #[cfg(parallel_compiler)] let i = self.vec.push(val); I::new(i) } pub fn get(&self, i: I) -> Option<T> { let i = i.index(); - #[cfg(not(parallel_compiler))] - return self.vec.get_copy(i); - #[cfg(parallel_compiler)] - return self.vec.get(i); + self.vec.get(i) } } diff --git a/compiler/rustc_data_structures/src/sync/worker_local.rs b/compiler/rustc_data_structures/src/sync/worker_local.rs index b6efcada10b..d75af009850 100644 --- a/compiler/rustc_data_structures/src/sync/worker_local.rs +++ b/compiler/rustc_data_structures/src/sync/worker_local.rs @@ -5,8 +5,9 @@ use std::ptr; use std::sync::Arc; use parking_lot::Mutex; -#[cfg(parallel_compiler)] -use {crate::outline, crate::sync::CacheAligned}; + +use crate::outline; +use crate::sync::CacheAligned; /// A pointer to the `RegistryData` which uniquely identifies a registry. /// This identifier can be reused if the registry gets freed. @@ -21,7 +22,6 @@ impl RegistryId { /// /// Note that there's a race possible where the identifier in `THREAD_DATA` could be reused /// so this can succeed from a different registry. - #[cfg(parallel_compiler)] fn verify(self) -> usize { let (id, index) = THREAD_DATA.with(|data| (data.registry_id.get(), data.index.get())); @@ -102,11 +102,7 @@ impl Registry { /// worker local value through the `Deref` impl on the registry associated with the thread it was /// created on. It will panic otherwise. pub struct WorkerLocal<T> { - #[cfg(not(parallel_compiler))] - local: T, - #[cfg(parallel_compiler)] locals: Box<[CacheAligned<T>]>, - #[cfg(parallel_compiler)] registry: Registry, } @@ -114,7 +110,6 @@ pub struct WorkerLocal<T> { // or it will panic for threads without an associated local. So there isn't a need for `T` to do // it's own synchronization. The `verify` method on `RegistryId` has an issue where the id // can be reused, but `WorkerLocal` has a reference to `Registry` which will prevent any reuse. -#[cfg(parallel_compiler)] unsafe impl<T: Send> Sync for WorkerLocal<T> {} impl<T> WorkerLocal<T> { @@ -122,33 +117,17 @@ impl<T> WorkerLocal<T> { /// value this worker local should take for each thread in the registry. #[inline] pub fn new<F: FnMut(usize) -> T>(mut initial: F) -> WorkerLocal<T> { - #[cfg(parallel_compiler)] - { - let registry = Registry::current(); - WorkerLocal { - locals: (0..registry.0.thread_limit.get()) - .map(|i| CacheAligned(initial(i))) - .collect(), - registry, - } - } - #[cfg(not(parallel_compiler))] - { - WorkerLocal { local: initial(0) } + let registry = Registry::current(); + WorkerLocal { + locals: (0..registry.0.thread_limit.get()).map(|i| CacheAligned(initial(i))).collect(), + registry, } } /// Returns the worker-local values for each thread #[inline] pub fn into_inner(self) -> impl Iterator<Item = T> { - #[cfg(parallel_compiler)] - { - self.locals.into_vec().into_iter().map(|local| local.0) - } - #[cfg(not(parallel_compiler))] - { - std::iter::once(self.local) - } + self.locals.into_vec().into_iter().map(|local| local.0) } } @@ -156,13 +135,6 @@ impl<T> Deref for WorkerLocal<T> { type Target = T; #[inline(always)] - #[cfg(not(parallel_compiler))] - fn deref(&self) -> &T { - &self.local - } - - #[inline(always)] - #[cfg(parallel_compiler)] fn deref(&self) -> &T { // This is safe because `verify` will only return values less than // `self.registry.thread_limit` which is the size of the `self.locals` array. diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 74da0d11e4b..81f15ebcbf8 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -77,9 +77,4 @@ rustc_randomized_layouts = [ 'rustc_index/rustc_randomized_layouts', 'rustc_middle/rustc_randomized_layouts' ] -rustc_use_parallel_compiler = [ - 'rustc_data_structures/rustc_use_parallel_compiler', - 'rustc_interface/rustc_use_parallel_compiler', - 'rustc_middle/rustc_use_parallel_compiler' -] # tidy-alphabetical-end diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 78ba841d89f..b6f7abed6f3 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -934,9 +934,12 @@ pub fn version_at_macro_invocation( } fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) { - let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() }; let mut options = getopts::Options::new(); - for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) { + for option in config::rustc_optgroups() + .iter() + .filter(|x| verbose || !x.is_verbose_help_only) + .filter(|x| include_unstable_options || x.is_stable()) + { option.apply(&mut options); } let message = "Usage: rustc [OPTIONS] INPUT"; diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 5b6b8b3f183..6974c12f994 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -19,8 +19,3 @@ rustc_span = { path = "../rustc_span" } tracing = "0.1" unic-langid = { version = "0.9.0", features = ["macros"] } # tidy-alphabetical-end - -[features] -# tidy-alphabetical-start -rustc_use_parallel_compiler = ['rustc_baked_icu_data/rustc_use_parallel_compiler'] -# tidy-alphabetical-end diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 2ede7d805fa..74b6d63365a 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -8,12 +8,9 @@ // tidy-alphabetical-end use std::borrow::Cow; -#[cfg(not(parallel_compiler))] -use std::cell::LazyCell as Lazy; use std::error::Error; use std::path::{Path, PathBuf}; -#[cfg(parallel_compiler)] -use std::sync::LazyLock as Lazy; +use std::sync::LazyLock; use std::{fmt, fs, io}; use fluent_bundle::FluentResource; @@ -21,9 +18,6 @@ pub use fluent_bundle::types::FluentType; pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue}; use fluent_syntax::parser::ParserError; use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker}; -#[cfg(not(parallel_compiler))] -use intl_memoizer::IntlLangMemoizer; -#[cfg(parallel_compiler)] use intl_memoizer::concurrent::IntlLangMemoizer; use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; use rustc_macros::{Decodable, Encodable}; @@ -34,12 +28,6 @@ pub use unic_langid::{LanguageIdentifier, langid}; pub type FluentBundle = IntoDynSyncSend<fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>>; -#[cfg(not(parallel_compiler))] -fn new_bundle(locales: Vec<LanguageIdentifier>) -> FluentBundle { - IntoDynSyncSend(fluent_bundle::bundle::FluentBundle::new(locales)) -} - -#[cfg(parallel_compiler)] fn new_bundle(locales: Vec<LanguageIdentifier>) -> FluentBundle { IntoDynSyncSend(fluent_bundle::bundle::FluentBundle::new_concurrent(locales)) } @@ -217,7 +205,7 @@ fn register_functions(bundle: &mut FluentBundle) { /// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily /// evaluated fluent bundle. -pub type LazyFallbackBundle = Lrc<Lazy<FluentBundle, impl FnOnce() -> FluentBundle>>; +pub type LazyFallbackBundle = Lrc<LazyLock<FluentBundle, impl FnOnce() -> FluentBundle>>; /// Return the default `FluentBundle` with standard "en-US" diagnostic messages. #[instrument(level = "trace", skip(resources))] @@ -225,7 +213,7 @@ pub fn fallback_fluent_bundle( resources: Vec<&'static str>, with_directionality_markers: bool, ) -> LazyFallbackBundle { - Lrc::new(Lazy::new(move || { + Lrc::new(LazyLock::new(move || { let mut fallback_bundle = new_bundle(vec![langid!("en-US")]); register_functions(&mut fallback_bundle); @@ -548,15 +536,6 @@ pub fn fluent_value_from_str_list_sep_by_and(l: Vec<Cow<'_, str>>) -> FluentValu Cow::Owned(result) } - #[cfg(not(parallel_compiler))] - fn as_string_threadsafe( - &self, - _intls: &intl_memoizer::concurrent::IntlLangMemoizer, - ) -> Cow<'static, str> { - unreachable!("`as_string_threadsafe` is not used in non-parallel rustc") - } - - #[cfg(parallel_compiler)] fn as_string_threadsafe( &self, intls: &intl_memoizer::concurrent::IntlLangMemoizer, diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 41ebe4ae267..06bae57638f 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -36,8 +36,3 @@ features = [ "Win32_Security", "Win32_System_Threading", ] - -[features] -# tidy-alphabetical-start -rustc_use_parallel_compiler = ['rustc_error_messages/rustc_use_parallel_compiler'] -# tidy-alphabetical-end diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 6552cf224ea..a386129e814 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -22,7 +22,7 @@ use rustc_error_messages::{FluentArgs, SpanLabel}; use rustc_lint_defs::pluralize; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; -use rustc_span::{FileLines, FileName, SourceFile, Span, char_width}; +use rustc_span::{FileLines, FileName, SourceFile, Span, char_width, str_width}; use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use tracing::{debug, instrument, trace, warn}; @@ -44,6 +44,7 @@ const DEFAULT_COLUMN_WIDTH: usize = 140; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HumanReadableErrorType { Default, + Unicode, AnnotateSnippet, Short, } @@ -112,8 +113,12 @@ impl Margin { fn was_cut_right(&self, line_len: usize) -> bool { let right = if self.computed_right == self.span_right || self.computed_right == self.label_right { + // FIXME: This comment refers to the only callsite of this method. + // Rephrase it or refactor it, so it can stand on its own. // Account for the "..." padding given above. Otherwise we end up with code lines // that do fit but end in "..." as if they were trimmed. + // FIXME: Don't hard-code this offset. Is this meant to represent + // `2 * str_width(self.margin())`? self.computed_right - 6 } else { self.computed_right @@ -595,6 +600,12 @@ impl ColorConfig { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OutputTheme { + Ascii, + Unicode, +} + /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` #[derive(Setters)] pub struct HumanEmitter { @@ -613,6 +624,7 @@ pub struct HumanEmitter { macro_backtrace: bool, track_diagnostics: bool, terminal_url: TerminalUrl, + theme: OutputTheme, } #[derive(Debug)] @@ -637,6 +649,7 @@ impl HumanEmitter { macro_backtrace: false, track_diagnostics: false, terminal_url: TerminalUrl::No, + theme: OutputTheme::Ascii, } } @@ -664,6 +677,7 @@ impl HumanEmitter { // Create the source line we will highlight. let left = margin.left(line_len); let right = margin.right(line_len); + // FIXME: The following code looks fishy. See #132860. // On long lines, we strip the source line, accounting for unicode. let mut taken = 0; let code: String = source_string @@ -680,17 +694,19 @@ impl HumanEmitter { }) .collect(); buffer.puts(line_offset, code_offset, &code, Style::Quotation); + let placeholder = self.margin(); if margin.was_cut_left() { // We have stripped some code/whitespace from the beginning, make it clear. - buffer.puts(line_offset, code_offset, "...", Style::LineNumber); + buffer.puts(line_offset, code_offset, placeholder, Style::LineNumber); } if margin.was_cut_right(line_len) { + let padding = str_width(placeholder); // We have stripped some code after the rightmost span end, make it clear we did so. - buffer.puts(line_offset, code_offset + taken - 3, "...", Style::LineNumber); + buffer.puts(line_offset, code_offset + taken - padding, placeholder, Style::LineNumber); } buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber); - draw_col_separator_no_space(buffer, line_offset, width_offset - 2); + self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2); } #[instrument(level = "trace", skip(self), ret)] @@ -702,6 +718,7 @@ impl HumanEmitter { width_offset: usize, code_offset: usize, margin: Margin, + close_window: bool, ) -> Vec<(usize, Style)> { // Draw: // @@ -732,6 +749,7 @@ impl HumanEmitter { // Left trim let left = margin.left(source_string.len()); + // FIXME: This looks fishy. See #132860. // Account for unicode characters of width !=0 that were removed. let left = source_string.chars().take(left).map(|ch| char_width(ch)).sum(); @@ -767,13 +785,10 @@ impl HumanEmitter { for ann in &line.annotations { if let AnnotationType::MultilineStart(depth) = ann.annotation_type { if source_string.chars().take(ann.start_col.display).all(|c| c.is_whitespace()) { - let style = if ann.is_primary { - Style::UnderlinePrimary - } else { - Style::UnderlineSecondary - }; - annotations.push((depth, style)); - buffer_ops.push((line_offset, width_offset + depth - 1, '/', style)); + let uline = self.underline(ann.is_primary); + let chr = uline.multiline_whole_line; + annotations.push((depth, uline.style)); + buffer_ops.push((line_offset, width_offset + depth - 1, chr, uline.style)); } else { short_start = false; break; @@ -970,7 +985,7 @@ impl HumanEmitter { // 3 │ X0 Y0 Z0 // │ ┏━━━━━┛ │ │ < We are writing these lines // │ ┃┌───────┘ │ < by reverting the "depth" of - // │ ┃│┌─────────┘ < their multilne spans. + // │ ┃│┌─────────┘ < their multiline spans. // 4 │ ┃││ X1 Y1 Z1 // 5 │ ┃││ X2 Y2 Z2 // │ ┃│└────╿──│──┘ `Z` label @@ -997,7 +1012,10 @@ impl HumanEmitter { // 4 | } // | for pos in 0..=line_len { - draw_col_separator_no_space(buffer, line_offset + pos + 1, width_offset - 2); + self.draw_col_separator_no_space(buffer, line_offset + pos + 1, width_offset - 2); + } + if close_window { + self.draw_col_separator_end(buffer, line_offset + line_len + 1, width_offset - 2); } // Write the horizontal lines for multiline annotations @@ -1013,21 +1031,17 @@ impl HumanEmitter { // 4 | } // | _ for &(pos, annotation) in &annotations_position { - let style = if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::UnderlineSecondary - }; + let underline = self.underline(annotation.is_primary); let pos = pos + 1; match annotation.annotation_type { AnnotationType::MultilineStart(depth) | AnnotationType::MultilineEnd(depth) => { - draw_range( + self.draw_range( buffer, - '_', + underline.multiline_horizontal, line_offset + pos, width_offset + depth, (code_offset + annotation.start_col.display).saturating_sub(left), - style, + underline.style, ); } _ if self.teach => { @@ -1035,7 +1049,7 @@ impl HumanEmitter { line_offset, (code_offset + annotation.start_col.display).saturating_sub(left), (code_offset + annotation.end_col.display).saturating_sub(left), - style, + underline.style, annotation.is_primary, ); } @@ -1055,11 +1069,7 @@ impl HumanEmitter { // 4 | | } // | |_ for &(pos, annotation) in &annotations_position { - let style = if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::UnderlineSecondary - }; + let underline = self.underline(annotation.is_primary); let pos = pos + 1; if pos > 1 && (annotation.has_label() || annotation.takes_space()) { @@ -1067,21 +1077,64 @@ impl HumanEmitter { buffer.putc( p, (code_offset + annotation.start_col.display).saturating_sub(left), - '|', - style, + match annotation.annotation_type { + AnnotationType::MultilineLine(_) => underline.multiline_vertical, + _ => underline.vertical_text_line, + }, + underline.style, + ); + } + if let AnnotationType::MultilineStart(_) = annotation.annotation_type { + buffer.putc( + line_offset + pos, + (code_offset + annotation.start_col.display).saturating_sub(left), + underline.bottom_right, + underline.style, + ); + } + if let AnnotationType::MultilineEnd(_) = annotation.annotation_type + && annotation.has_label() + { + buffer.putc( + line_offset + pos, + (code_offset + annotation.start_col.display).saturating_sub(left), + underline.multiline_bottom_right_with_text, + underline.style, ); } } match annotation.annotation_type { AnnotationType::MultilineStart(depth) => { + buffer.putc( + line_offset + pos, + width_offset + depth - 1, + underline.top_left, + underline.style, + ); for p in line_offset + pos + 1..line_offset + line_len + 2 { - buffer.putc(p, width_offset + depth - 1, '|', style); + buffer.putc( + p, + width_offset + depth - 1, + underline.multiline_vertical, + underline.style, + ); } } AnnotationType::MultilineEnd(depth) => { - for p in line_offset..=line_offset + pos { - buffer.putc(p, width_offset + depth - 1, '|', style); + for p in line_offset..line_offset + pos { + buffer.putc( + p, + width_offset + depth - 1, + underline.multiline_vertical, + underline.style, + ); } + buffer.putc( + line_offset + pos, + width_offset + depth - 1, + underline.bottom_left, + underline.style, + ); } _ => (), } @@ -1102,7 +1155,11 @@ impl HumanEmitter { let style = if annotation.is_primary { Style::LabelPrimary } else { Style::LabelSecondary }; let (pos, col) = if pos == 0 { - (pos + 1, (annotation.end_col.display + 1).saturating_sub(left)) + if annotation.end_col.display == 0 { + (pos + 1, (annotation.end_col.display + 2).saturating_sub(left)) + } else { + (pos + 1, (annotation.end_col.display + 1).saturating_sub(left)) + } } else { (pos + 2, annotation.start_col.display.saturating_sub(left)) }; @@ -1135,18 +1192,60 @@ impl HumanEmitter { // 3 | // 4 | } // | _^ test - for &(_, annotation) in &annotations_position { - let (underline, style) = if annotation.is_primary { - ('^', Style::UnderlinePrimary) - } else { - ('-', Style::UnderlineSecondary) - }; + for &(pos, annotation) in &annotations_position { + let uline = self.underline(annotation.is_primary); for p in annotation.start_col.display..annotation.end_col.display { + // The default span label underline. buffer.putc( line_offset + 1, (code_offset + p).saturating_sub(left), - underline, - style, + uline.underline, + uline.style, + ); + } + + if pos == 0 + && matches!( + annotation.annotation_type, + AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) + ) + { + // The beginning of a multiline span with its leftward moving line on the same line. + buffer.putc( + line_offset + 1, + (code_offset + annotation.start_col.display).saturating_sub(left), + match annotation.annotation_type { + AnnotationType::MultilineStart(_) => uline.top_right_flat, + AnnotationType::MultilineEnd(_) => uline.multiline_end_same_line, + _ => panic!("unexpected annotation type: {annotation:?}"), + }, + uline.style, + ); + } else if pos != 0 + && matches!( + annotation.annotation_type, + AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) + ) + { + // The beginning of a multiline span with its leftward moving line on another line, + // so we start going down first. + buffer.putc( + line_offset + 1, + (code_offset + annotation.start_col.display).saturating_sub(left), + match annotation.annotation_type { + AnnotationType::MultilineStart(_) => uline.multiline_start_down, + AnnotationType::MultilineEnd(_) => uline.multiline_end_up, + _ => panic!("unexpected annotation type: {annotation:?}"), + }, + uline.style, + ); + } else if pos != 0 && annotation.has_label() { + // The beginning of a span label with an actual label, we'll point down. + buffer.putc( + line_offset + 1, + (code_offset + annotation.start_col.display).saturating_sub(left), + uline.label_start, + uline.style, ); } } @@ -1217,7 +1316,7 @@ impl HumanEmitter { padding: usize, label: &str, override_style: Option<Style>, - ) { + ) -> usize { // The extra 5 ` ` is padding that's always needed to align to the `note: `: // // error: message @@ -1281,6 +1380,7 @@ impl HumanEmitter { buffer.append(line_number, text, style_or_override(*style, override_style)); } } + line_number } #[instrument(level = "trace", skip(self, args), ret)] @@ -1294,6 +1394,7 @@ impl HumanEmitter { max_line_num_len: usize, is_secondary: bool, emitted_at: Option<&DiagLocation>, + is_cont: bool, ) -> io::Result<()> { let mut buffer = StyledBuffer::new(); @@ -1303,12 +1404,29 @@ impl HumanEmitter { for _ in 0..max_line_num_len { buffer.prepend(0, " ", Style::NoStyle); } - draw_note_separator(&mut buffer, 0, max_line_num_len + 1); + self.draw_note_separator(&mut buffer, 0, max_line_num_len + 1, is_cont); if *level != Level::FailureNote { buffer.append(0, level.to_str(), Style::MainHeaderMsg); buffer.append(0, ": ", Style::NoStyle); } - self.msgs_to_buffer(&mut buffer, msgs, args, max_line_num_len, "note", None); + let printed_lines = + self.msgs_to_buffer(&mut buffer, msgs, args, max_line_num_len, "note", None); + if is_cont && matches!(self.theme, OutputTheme::Unicode) { + // There's another note after this one, associated to the subwindow above. + // We write additional vertical lines to join them: + // ╭▸ test.rs:3:3 + // │ + // 3 │ code + // │ ━━━━ + // │ + // ├ note: foo + // │ bar + // ╰ note: foo + // bar + for i in 1..=printed_lines { + self.draw_col_separator_no_space(&mut buffer, i, max_line_num_len + 1); + } + } } else { let mut label_width = 0; // The failure note level itself does not provide any useful diagnostic information @@ -1439,9 +1557,13 @@ impl HumanEmitter { Style::LineAndColumn, ); if annotation_id == 0 { - buffer.prepend(line_idx, "--> ", Style::LineNumber); + buffer.prepend(line_idx, self.file_start(), Style::LineNumber); } else { - buffer.prepend(line_idx, "::: ", Style::LineNumber); + buffer.prepend( + line_idx, + self.secondary_file_start(), + Style::LineNumber, + ); } for _ in 0..max_line_num_len { buffer.prepend(line_idx, " ", Style::NoStyle); @@ -1454,12 +1576,14 @@ impl HumanEmitter { } else { Style::LabelSecondary }; - buffer.prepend(line_idx, " |", Style::LineNumber); + let pipe = self.col_separator(); + buffer.prepend(line_idx, &format!(" {pipe}"), Style::LineNumber); for _ in 0..max_line_num_len { buffer.prepend(line_idx, " ", Style::NoStyle); } line_idx += 1; - buffer.append(line_idx, " = note: ", style); + let chr = self.note_separator(); + buffer.append(line_idx, &format!(" {chr} note: "), style); for _ in 0..max_line_num_len { buffer.prepend(line_idx, " ", Style::NoStyle); } @@ -1480,7 +1604,7 @@ impl HumanEmitter { // remember where we are in the output buffer for easy reference let buffer_msg_line_offset = buffer.num_lines(); - buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber); + buffer.prepend(buffer_msg_line_offset, self.file_start(), Style::LineNumber); buffer.append( buffer_msg_line_offset, &format!( @@ -1510,15 +1634,28 @@ impl HumanEmitter { // remember where we are in the output buffer for easy reference let buffer_msg_line_offset = buffer.num_lines(); - // Add spacing line - draw_col_separator_no_space( + // Add spacing line, as shown: + // --> $DIR/file:54:15 + // | + // LL | code + // | ^^^^ + // | (<- It prints *this* line) + // ::: $DIR/other_file.rs:15:5 + // | + // LL | code + // | ---- + self.draw_col_separator_no_space( &mut buffer, buffer_msg_line_offset, max_line_num_len + 1, ); // Then, the secondary file indicator - buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber); + buffer.prepend( + buffer_msg_line_offset + 1, + self.secondary_file_start(), + Style::LineNumber, + ); let loc = if let Some(first_line) = annotated_file.lines.first() { let col = if let Some(first_annotation) = first_line.annotations.first() { format!(":{}", first_annotation.start_col.file + 1) @@ -1543,7 +1680,7 @@ impl HumanEmitter { if !self.short_message { // Put in the spacer between the location and annotated source let buffer_msg_line_offset = buffer.num_lines(); - draw_col_separator_no_space( + self.draw_col_separator_no_space( &mut buffer, buffer_msg_line_offset, max_line_num_len + 1, @@ -1651,6 +1788,7 @@ impl HumanEmitter { width_offset, code_offset, margin, + !is_cont && line_idx + 1 == annotated_file.lines.len(), ); let mut to_add = FxHashMap::default(); @@ -1666,7 +1804,13 @@ impl HumanEmitter { // the code in this line. for (depth, style) in &multilines { for line in previous_buffer_line..buffer.num_lines() { - draw_multiline_line(&mut buffer, line, width_offset, *depth, *style); + self.draw_multiline_line( + &mut buffer, + line, + width_offset, + *depth, + *style, + ); } } // check to see if we need to print out or elide lines that come between @@ -1676,11 +1820,15 @@ impl HumanEmitter { - annotated_file.lines[line_idx].line_index; if line_idx_delta > 2 { let last_buffer_line_num = buffer.num_lines(); - buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber); + self.draw_line_separator( + &mut buffer, + last_buffer_line_num, + width_offset, + ); // Set the multiline annotation vertical lines on `...` bridging line. for (depth, style) in &multilines { - draw_multiline_line( + self.draw_multiline_line( &mut buffer, last_buffer_line_num, width_offset, @@ -1695,7 +1843,7 @@ impl HumanEmitter { // 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( + self.draw_multiline_line( &mut buffer, last_buffer_line_num, width_offset, @@ -1728,7 +1876,7 @@ impl HumanEmitter { ); for (depth, style) in &multilines { - draw_multiline_line( + self.draw_multiline_line( &mut buffer, last_buffer_line_num, width_offset, @@ -1740,7 +1888,7 @@ impl HumanEmitter { for ann in &line.annotations { if let AnnotationType::MultilineStart(pos) = ann.annotation_type { - draw_multiline_line( + self.draw_multiline_line( &mut buffer, last_buffer_line_num, width_offset, @@ -1824,13 +1972,24 @@ impl HumanEmitter { ); let mut row_num = 2; - draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); - for (complete, parts, highlights, _) in suggestions.iter().take(MAX_SUGGESTIONS) { + for (i, (complete, parts, highlights, _)) in + suggestions.iter().enumerate().take(MAX_SUGGESTIONS) + { debug!(?complete, ?parts, ?highlights); let has_deletion = parts.iter().any(|p| p.is_deletion(sm)); let is_multiline = complete.lines().count() > 1; + if i == 0 { + self.draw_col_separator_start(&mut buffer, row_num - 1, max_line_num_len + 1); + } else { + buffer.puts( + row_num - 1, + max_line_num_len + 1, + self.multi_suggestion_separator(), + Style::LineNumber, + ); + } if let Some(span) = span.primary_span() { // Compare the primary span of the diagnostic with the span of the suggestion // being emitted. If they belong to the same file, we don't *need* to show the @@ -1838,7 +1997,9 @@ impl HumanEmitter { // telling users to make a change but not clarifying *where*. let loc = sm.lookup_char_pos(parts[0].span.lo()); if loc.file.name != sm.span_to_filename(span) && loc.file.name.is_real() { - let arrow = "--> "; + // --> file.rs:line:col + // | + let arrow = self.file_start(); buffer.puts(row_num - 1, 0, arrow, Style::LineNumber); let filename = sm.filename_for_diagnostics(&loc.file.name); let offset = sm.doctest_offset_line(&loc.file.name, loc.line); @@ -1852,6 +2013,7 @@ impl HumanEmitter { for _ in 0..max_line_num_len { buffer.prepend(row_num - 1, " ", Style::NoStyle); } + self.draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1); row_num += 1; } } @@ -1882,7 +2044,6 @@ impl HumanEmitter { assert!(!file_lines.lines.is_empty() || parts[0].span.is_dummy()); let line_start = sm.lookup_char_pos(parts[0].span.lo()).line; - draw_col_separator_no_space(&mut buffer, row_num - 1, max_line_num_len + 1); let mut lines = complete.lines(); if lines.clone().next().is_none() { // Account for a suggestion to completely remove a line(s) with whitespace (#94192). @@ -1972,7 +2133,14 @@ impl HumanEmitter { ) } - buffer.puts(row_num, 0, "...", Style::LineNumber); + let placeholder = self.margin(); + let padding = str_width(placeholder); + buffer.puts( + row_num, + max_line_num_len.saturating_sub(padding), + placeholder, + Style::LineNumber, + ); row_num += 1; if let Some((p, l)) = last_line { @@ -2040,7 +2208,6 @@ impl HumanEmitter { if let DisplaySuggestion::Diff | DisplaySuggestion::Underline | DisplaySuggestion::Add = show_code_change { - draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1); for part in parts { let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display; let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display; @@ -2057,11 +2224,11 @@ impl HumanEmitter { }; // ...or trailing spaces. Account for substitutions containing unicode // characters. - let sub_len: usize = - if is_whitespace_addition { &part.snippet } else { part.snippet.trim() } - .chars() - .map(|ch| char_width(ch)) - .sum(); + let sub_len: usize = str_width(if is_whitespace_addition { + &part.snippet + } else { + part.snippet.trim() + }); let offset: isize = offsets .iter() @@ -2082,7 +2249,7 @@ impl HumanEmitter { buffer.putc( row_num, (padding as isize + p) as usize, - if part.is_addition(sm) { '+' } else { '~' }, + if part.is_addition(sm) { '+' } else { self.diff() }, Style::Addition, ); } @@ -2099,8 +2266,7 @@ impl HumanEmitter { } // length of the code after substitution - let full_sub_len = - part.snippet.chars().map(|ch| char_width(ch)).sum::<usize>() as isize; + let full_sub_len = str_width(&part.snippet) as isize; // length of the code to be substituted let snippet_len = span_end_pos as isize - span_start_pos as isize; @@ -2114,10 +2280,23 @@ impl HumanEmitter { // if we elided some lines, add an ellipsis if lines.next().is_some() { - buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber); - } else if let DisplaySuggestion::None = show_code_change { - draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1); - row_num += 1; + let placeholder = self.margin(); + let padding = str_width(placeholder); + buffer.puts( + row_num, + max_line_num_len.saturating_sub(padding), + placeholder, + Style::LineNumber, + ); + } else { + let row = match show_code_change { + DisplaySuggestion::Diff + | DisplaySuggestion::Add + | DisplaySuggestion::Underline => row_num - 1, + DisplaySuggestion::None => row_num, + }; + self.draw_col_separator_end(&mut buffer, row, max_line_num_len + 1); + row_num = row + 1; } } if suggestions.len() > MAX_SUGGESTIONS { @@ -2125,6 +2304,7 @@ impl HumanEmitter { let msg = format!("and {} other candidate{}", others, pluralize!(others)); buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle); } + emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?; Ok(()) } @@ -2157,6 +2337,8 @@ impl HumanEmitter { max_line_num_len, false, emitted_at, + !children.is_empty() + || suggestions.iter().any(|s| s.style != SuggestionStyle::CompletelyHidden), ) { Ok(()) => { if !children.is_empty() @@ -2164,7 +2346,15 @@ impl HumanEmitter { { let mut buffer = StyledBuffer::new(); if !self.short_message { - draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1); + if let Some(child) = children.iter().next() + && child.span.primary_spans().is_empty() + { + // We'll continue the vertical bar to point into the next note. + self.draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1); + } else { + // We'll close the vertical bar to visually end the code window. + self.draw_col_separator_end(&mut buffer, 0, max_line_num_len + 1); + } } if let Err(e) = emit_to_destination( &buffer.render(), @@ -2176,9 +2366,14 @@ impl HumanEmitter { } } if !self.short_message { - for child in children { + for (i, child) in children.iter().enumerate() { assert!(child.level.can_be_subdiag()); let span = &child.span; + // FIXME: audit that this behaves correctly with suggestions. + let should_close = match children.get(i + 1) { + Some(c) => !c.span.primary_spans().is_empty(), + None => i + 1 == children.len(), + }; if let Err(err) = self.emit_messages_default_inner( span, &child.messages, @@ -2188,11 +2383,12 @@ impl HumanEmitter { max_line_num_len, true, None, + !should_close, ) { panic!("failed to emit error: {err}"); } } - for sugg in suggestions { + for (i, sugg) in suggestions.iter().enumerate() { match sugg.style { SuggestionStyle::CompletelyHidden => { // do not display this suggestion, it is meant only for tools @@ -2207,6 +2403,9 @@ impl HumanEmitter { max_line_num_len, true, None, + // FIXME: this needs to account for the suggestion type, + // some don't take any space. + i + 1 != suggestions.len(), ) { panic!("failed to emit error: {e}"); } @@ -2323,10 +2522,17 @@ impl HumanEmitter { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); } [] => { - draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1); + // FIXME: needed? Doesn't get excercised in any test. + self.draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1); } _ => { - buffer.puts(*row_num, max_line_num_len + 1, "~ ", Style::Addition); + let diff = self.diff(); + buffer.puts( + *row_num, + max_line_num_len + 1, + &format!("{diff} "), + Style::Addition, + ); } } // LL | line_to_add @@ -2346,7 +2552,7 @@ impl HumanEmitter { buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); - draw_col_separator(buffer, *row_num, max_line_num_len + 1); + self.draw_col_separator(buffer, *row_num, max_line_num_len + 1); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } @@ -2374,6 +2580,306 @@ impl HumanEmitter { } *row_num += 1; } + + fn underline(&self, is_primary: bool) -> UnderlineParts { + // X0 Y0 + // label_start > ┯━━━━ < underline + // │ < vertical_text_line + // text + + // multiline_start_down ⤷ X0 Y0 + // top_left > ┌───╿──┘ < top_right_flat + // top_left > ┏│━━━┙ < top_right + // multiline_vertical > ┃│ + // ┃│ X1 Y1 + // ┃│ X2 Y2 + // ┃└────╿──┘ < multiline_end_same_line + // bottom_left > ┗━━━━━┥ < bottom_right_with_text + // multiline_horizontal ^ `X` is a good letter + + // multiline_whole_line > ┏ X0 Y0 + // ┃ X1 Y1 + // ┗━━━━┛ < multiline_end_same_line + + // multiline_whole_line > ┏ X0 Y0 + // ┃ X1 Y1 + // ┃ ╿ < multiline_end_up + // ┗━━┛ < bottom_right + + match (self.theme, is_primary) { + (OutputTheme::Ascii, true) => UnderlineParts { + style: Style::UnderlinePrimary, + underline: '^', + label_start: '^', + vertical_text_line: '|', + multiline_vertical: '|', + multiline_horizontal: '_', + multiline_whole_line: '/', + multiline_start_down: '^', + bottom_right: '|', + top_left: ' ', + top_right_flat: '^', + bottom_left: '|', + multiline_end_up: '^', + multiline_end_same_line: '^', + multiline_bottom_right_with_text: '|', + }, + (OutputTheme::Ascii, false) => UnderlineParts { + style: Style::UnderlineSecondary, + underline: '-', + label_start: '-', + vertical_text_line: '|', + multiline_vertical: '|', + multiline_horizontal: '_', + multiline_whole_line: '/', + multiline_start_down: '-', + bottom_right: '|', + top_left: ' ', + top_right_flat: '-', + bottom_left: '|', + multiline_end_up: '-', + multiline_end_same_line: '-', + multiline_bottom_right_with_text: '|', + }, + (OutputTheme::Unicode, true) => UnderlineParts { + style: Style::UnderlinePrimary, + underline: '━', + label_start: '┯', + vertical_text_line: '│', + multiline_vertical: '┃', + multiline_horizontal: '━', + multiline_whole_line: '┏', + multiline_start_down: '╿', + bottom_right: '┙', + top_left: '┏', + top_right_flat: '┛', + bottom_left: '┗', + multiline_end_up: '╿', + multiline_end_same_line: '┛', + multiline_bottom_right_with_text: '┥', + }, + (OutputTheme::Unicode, false) => UnderlineParts { + style: Style::UnderlineSecondary, + underline: '─', + label_start: '┬', + vertical_text_line: '│', + multiline_vertical: '│', + multiline_horizontal: '─', + multiline_whole_line: '┌', + multiline_start_down: '│', + bottom_right: '┘', + top_left: '┌', + top_right_flat: '┘', + bottom_left: '└', + multiline_end_up: '│', + multiline_end_same_line: '┘', + multiline_bottom_right_with_text: '┤', + }, + } + } + + fn col_separator(&self) -> char { + match self.theme { + OutputTheme::Ascii => '|', + OutputTheme::Unicode => '│', + } + } + + fn note_separator(&self) -> char { + match self.theme { + OutputTheme::Ascii => '=', + OutputTheme::Unicode => '╰', + } + } + + fn multi_suggestion_separator(&self) -> &'static str { + match self.theme { + OutputTheme::Ascii => "|", + OutputTheme::Unicode => "├╴", + } + } + + fn draw_col_separator(&self, buffer: &mut StyledBuffer, line: usize, col: usize) { + let chr = self.col_separator(); + buffer.puts(line, col, &format!("{chr} "), Style::LineNumber); + } + + fn draw_col_separator_no_space(&self, buffer: &mut StyledBuffer, line: usize, col: usize) { + let chr = self.col_separator(); + self.draw_col_separator_no_space_with_style(buffer, chr, line, col, Style::LineNumber); + } + + fn draw_col_separator_start(&self, buffer: &mut StyledBuffer, line: usize, col: usize) { + match self.theme { + OutputTheme::Ascii => { + self.draw_col_separator_no_space_with_style( + buffer, + '|', + line, + col, + Style::LineNumber, + ); + } + OutputTheme::Unicode => { + self.draw_col_separator_no_space_with_style( + buffer, + '╭', + line, + col, + Style::LineNumber, + ); + self.draw_col_separator_no_space_with_style( + buffer, + '╴', + line, + col + 1, + Style::LineNumber, + ); + } + } + } + + fn draw_col_separator_end(&self, buffer: &mut StyledBuffer, line: usize, col: usize) { + match self.theme { + OutputTheme::Ascii => { + self.draw_col_separator_no_space_with_style( + buffer, + '|', + line, + col, + Style::LineNumber, + ); + } + OutputTheme::Unicode => { + self.draw_col_separator_no_space_with_style( + buffer, + '╰', + line, + col, + Style::LineNumber, + ); + self.draw_col_separator_no_space_with_style( + buffer, + '╴', + line, + col + 1, + Style::LineNumber, + ); + } + } + } + + fn draw_col_separator_no_space_with_style( + &self, + buffer: &mut StyledBuffer, + chr: char, + line: usize, + col: usize, + style: Style, + ) { + buffer.putc(line, col, chr, style); + } + + fn draw_range( + &self, + buffer: &mut StyledBuffer, + symbol: char, + line: usize, + col_from: usize, + col_to: usize, + style: Style, + ) { + for col in col_from..col_to { + buffer.putc(line, col, symbol, style); + } + } + + fn draw_note_separator( + &self, + buffer: &mut StyledBuffer, + line: usize, + col: usize, + is_cont: bool, + ) { + let chr = match self.theme { + OutputTheme::Ascii => "= ", + OutputTheme::Unicode if is_cont => "├ ", + OutputTheme::Unicode => "╰ ", + }; + buffer.puts(line, col, chr, Style::LineNumber); + } + + fn draw_multiline_line( + &self, + buffer: &mut StyledBuffer, + line: usize, + offset: usize, + depth: usize, + style: Style, + ) { + let chr = match (style, self.theme) { + (Style::UnderlinePrimary | Style::LabelPrimary, OutputTheme::Ascii) => '|', + (_, OutputTheme::Ascii) => '|', + (Style::UnderlinePrimary | Style::LabelPrimary, OutputTheme::Unicode) => '┃', + (_, OutputTheme::Unicode) => '│', + }; + buffer.putc(line, offset + depth - 1, chr, style); + } + + fn file_start(&self) -> &'static str { + match self.theme { + OutputTheme::Ascii => "--> ", + OutputTheme::Unicode => " ╭▸ ", + } + } + + fn secondary_file_start(&self) -> &'static str { + match self.theme { + OutputTheme::Ascii => "::: ", + OutputTheme::Unicode => " ⸬ ", + } + } + + fn diff(&self) -> char { + match self.theme { + OutputTheme::Ascii => '~', + OutputTheme::Unicode => '±', + } + } + + fn draw_line_separator(&self, buffer: &mut StyledBuffer, line: usize, col: usize) { + let (column, dots) = match self.theme { + OutputTheme::Ascii => (0, "..."), + OutputTheme::Unicode => (col - 2, "‡"), + }; + buffer.puts(line, column, dots, Style::LineNumber); + } + + fn margin(&self) -> &'static str { + match self.theme { + OutputTheme::Ascii => "...", + OutputTheme::Unicode => "…", + } + } +} + +#[derive(Debug, Clone, Copy)] +struct UnderlineParts { + style: Style, + underline: char, + label_start: char, + vertical_text_line: char, + multiline_vertical: char, + multiline_horizontal: char, + multiline_whole_line: char, + multiline_start_down: char, + bottom_right: char, + top_left: char, + top_right_flat: char, + bottom_left: char, + multiline_end_up: char, + multiline_end_same_line: char, + multiline_bottom_right_with_text: char, } #[derive(Clone, Copy, Debug)] @@ -2666,50 +3172,6 @@ fn normalize_whitespace(s: &str) -> String { }) } -fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { - buffer.puts(line, col, "| ", Style::LineNumber); -} - -fn draw_col_separator_no_space(buffer: &mut StyledBuffer, line: usize, col: usize) { - draw_col_separator_no_space_with_style(buffer, line, col, Style::LineNumber); -} - -fn draw_col_separator_no_space_with_style( - buffer: &mut StyledBuffer, - line: usize, - col: usize, - style: Style, -) { - buffer.putc(line, col, '|', style); -} - -fn draw_range( - buffer: &mut StyledBuffer, - symbol: char, - line: usize, - col_from: usize, - col_to: usize, - style: Style, -) { - for col in col_from..col_to { - buffer.putc(line, col, symbol, style); - } -} - -fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { - buffer.puts(line, col, "= ", Style::LineNumber); -} - -fn draw_multiline_line( - buffer: &mut StyledBuffer, - line: usize, - offset: usize, - depth: usize, - style: Style, -) { - buffer.putc(line, offset + depth - 1, '|', style); -} - fn num_overlap( a_start: usize, a_end: usize, diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 91e2b9996cd..e3b6dcea892 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -27,7 +27,7 @@ use termcolor::{ColorSpec, WriteColor}; use crate::diagnostic::IsLint; use crate::emitter::{ - ColorConfig, Destination, Emitter, HumanEmitter, HumanReadableErrorType, + ColorConfig, Destination, Emitter, HumanEmitter, HumanReadableErrorType, OutputTheme, should_show_source_code, }; use crate::registry::Registry; @@ -377,6 +377,11 @@ impl Diagnostic { .terminal_url(je.terminal_url) .ui_testing(je.ui_testing) .ignored_directories_in_source_blocks(je.ignored_directories_in_source_blocks.clone()) + .theme(if let HumanReadableErrorType::Unicode = je.json_rendered { + OutputTheme::Unicode + } else { + OutputTheme::Ascii + }) .emit_diagnostic(diag); let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap(); let buf = String::from_utf8(buf).unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 94365a89adc..98200c367f9 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -623,12 +623,25 @@ impl Drop for DiagCtxtInner { self.flush_delayed() } + // Sanity check: did we use some of the expensive `trimmed_def_paths` functions + // unexpectedly, that is, without producing diagnostics? If so, for debugging purposes, we + // suggest where this happened and how to avoid it. if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() { if let Some(backtrace) = &self.must_produce_diag { + let suggestion = match backtrace.status() { + BacktraceStatus::Disabled => String::from( + "Backtraces are currently disabled: set `RUST_BACKTRACE=1` and re-run \ + to see where it happened.", + ), + BacktraceStatus::Captured => format!( + "This happened in the following `must_produce_diag` call's backtrace:\n\ + {backtrace}", + ), + _ => String::from("(impossible to capture backtrace where this happened)"), + }; panic!( - "must_produce_diag: `trimmed_def_paths` called but no diagnostics emitted; \ - `with_no_trimmed_paths` for debugging. \ - called at: {backtrace}" + "`trimmed_def_paths` called, diagnostics were expected but none were emitted. \ + Use `with_no_trimmed_paths` for debugging. {suggestion}" ); } } diff --git a/compiler/rustc_errors/src/tests.rs b/compiler/rustc_errors/src/tests.rs index 70179237e5d..376fd24d57b 100644 --- a/compiler/rustc_errors/src/tests.rs +++ b/compiler/rustc_errors/src/tests.rs @@ -26,16 +26,11 @@ fn make_dummy(ftl: &'static str) -> Dummy { let langid_en = langid!("en-US"); - #[cfg(parallel_compiler)] let mut bundle: FluentBundle = IntoDynSyncSend(crate::fluent_bundle::bundle::FluentBundle::new_concurrent(vec![ langid_en, ])); - #[cfg(not(parallel_compiler))] - let mut bundle: FluentBundle = - IntoDynSyncSend(crate::fluent_bundle::bundle::FluentBundle::new(vec![langid_en])); - bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle."); Dummy { bundle } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 7e4bc508e5c..bed500c3032 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -866,9 +866,7 @@ impl SyntaxExtension { }) .unwrap_or_else(|| (None, helper_attrs)); let stability = attr::find_stability(sess, attrs, span); - // We set `is_const_fn` false to avoid getting any implicit const stability. - let const_stability = - attr::find_const_stability(sess, attrs, span, /* is_const_fn */ false); + let const_stability = attr::find_const_stability(sess, attrs, span); let body_stability = attr::find_body_stability(sess, attrs); if let Some((_, sp)) = const_stability { sess.dcx().emit_err(errors::MacroConstStability { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c952d5f6d77..15cb331d07a 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -241,6 +241,8 @@ language_item_table! { 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; + ReceiverTarget, sym::receiver_target, receiver_target, Target::AssocTy, GenericRequirement::None; LegacyReceiver, sym::legacy_receiver, legacy_receiver_trait, Target::Trait, GenericRequirement::None; Fn, kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 3a6ea545741..1802f00bc1f 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1427,16 +1427,6 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id let predicates = tcx.predicates_of(def_id.to_def_id()); let generics = tcx.generics_of(def_id); - let is_our_default = |def: &ty::GenericParamDef| match def.kind { - GenericParamDefKind::Type { has_default, .. } - | GenericParamDefKind::Const { has_default, .. } => { - has_default && def.index >= generics.parent_count as u32 - } - GenericParamDefKind::Lifetime => { - span_bug!(tcx.def_span(def.def_id), "lifetime params can have no default") - } - }; - // Check that concrete defaults are well-formed. See test `type-check-defaults.rs`. // For example, this forbids the declaration: // @@ -1444,40 +1434,21 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id // // Here, the default `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold. for param in &generics.own_params { - match param.kind { - GenericParamDefKind::Type { .. } => { - if is_our_default(param) { - let ty = tcx.type_of(param.def_id).instantiate_identity(); - // Ignore dependent defaults -- that is, where the default of one type - // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't - // be sure if it will error or not as user might always specify the other. - if !ty.has_param() { - wfcx.register_wf_obligation( - tcx.def_span(param.def_id), - Some(WellFormedLoc::Ty(param.def_id.expect_local())), - ty.into(), - ); - } - } - } - GenericParamDefKind::Const { .. } => { - if is_our_default(param) { - // FIXME(const_generics_defaults): This - // is incorrect when dealing with unused args, for example - // for `struct Foo<const N: usize, const M: usize = { 1 - 2 }>` - // we should eagerly error. - let default_ct = tcx.const_param_default(param.def_id).instantiate_identity(); - if !default_ct.has_param() { - wfcx.register_wf_obligation( - tcx.def_span(param.def_id), - None, - default_ct.into(), - ); - } - } + if let Some(default) = param.default_value(tcx).map(ty::EarlyBinder::instantiate_identity) { + // Ignore dependent defaults -- that is, where the default of one type + // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't + // be sure if it will error or not as user might always specify the other. + // FIXME(generic_const_exprs): This is incorrect when dealing with unused const params. + // E.g: `struct Foo<const N: usize, const M: usize = { 1 - 2 }>;`. Here, we should + // eagerly error but we don't as we have `ConstKind::Unevaluated(.., [N, M])`. + if !default.has_param() { + wfcx.register_wf_obligation( + tcx.def_span(param.def_id), + matches!(param.kind, GenericParamDefKind::Type { .. }) + .then(|| WellFormedLoc::Ty(param.def_id.expect_local())), + default, + ); } - // Doesn't have defaults. - GenericParamDefKind::Lifetime => {} } } @@ -1490,39 +1461,16 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id // // First we build the defaulted generic parameters. let args = GenericArgs::for_item(tcx, def_id.to_def_id(), |param, _| { - match param.kind { - GenericParamDefKind::Lifetime => { - // All regions are identity. - tcx.mk_param_from_def(param) - } - - GenericParamDefKind::Type { .. } => { - // If the param has a default, ... - if is_our_default(param) { - let default_ty = tcx.type_of(param.def_id).instantiate_identity(); - // ... and it's not a dependent default, ... - if !default_ty.has_param() { - // ... then instantiate it with the default. - return default_ty.into(); - } - } - - tcx.mk_param_from_def(param) - } - GenericParamDefKind::Const { .. } => { - // If the param has a default, ... - if is_our_default(param) { - let default_ct = tcx.const_param_default(param.def_id).instantiate_identity(); - // ... and it's not a dependent default, ... - if !default_ct.has_param() { - // ... then instantiate it with the default. - return default_ct.into(); - } - } - - tcx.mk_param_from_def(param) - } + if param.index >= generics.parent_count as u32 + // If the param has a default, ... + && let Some(default) = param.default_value(tcx).map(ty::EarlyBinder::instantiate_identity) + // ... and it's not a dependent default, ... + && !default.has_param() + { + // ... then instantiate it with the default. + return default; } + tcx.mk_param_from_def(param) }); // Now we build the instantiated predicates. diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 1774772b50b..bfdf764d299 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -15,7 +15,7 @@ use crate::{Diverges, Expectation, FnCtxt, Needs}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug", ret)] - pub(crate) fn check_match( + pub(crate) fn check_expr_match( &self, expr: &'tcx hir::Expr<'tcx>, scrut: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 20502de38a2..e6e0f62b54d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -62,7 +62,7 @@ enum CallStep<'tcx> { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(crate) fn check_call( + pub(crate) fn check_expr_call( &self, call_expr: &'tcx hir::Expr<'tcx>, callee_expr: &'tcx hir::Expr<'tcx>, @@ -74,8 +74,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .check_expr_with_expectation_and_args( callee_expr, Expectation::NoExpectation, - arg_exprs, - Some(call_expr), + Some((call_expr, arg_exprs)), ), _ => self.check_expr(callee_expr), }; diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 2026fd9a614..51d78646373 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -137,7 +137,7 @@ pub(super) fn check_fn<'a, 'tcx>( } fcx.is_whole_body.set(true); - fcx.check_return_expr(body.value, false); + fcx.check_return_or_body_tail(body.value, false); // Finalize the return check by taking the LUB of the return types // we saw and assigning it to the expected return type. This isn't diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index b05731c6d52..354993513da 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -55,6 +55,9 @@ use crate::{ }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Check an expr with an expectation type, and also demand that the expr's + /// evaluated type is a subtype of the expectation at the end. This is a + /// *hard* requirement. pub(crate) fn check_expr_has_type_or_error( &self, expr: &'tcx hir::Expr<'tcx>, @@ -97,6 +100,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } + /// Check an expr with an expectation type, and also demand that the expr's + /// evaluated type is a coercible to the expectation at the end. This is a + /// *hard* requirement. pub(super) fn check_expr_coercible_to_type( &self, expr: &'tcx hir::Expr<'tcx>, @@ -128,6 +134,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Check an expr with an expectation type. Don't actually enforce that expectation + /// is related to the expr's evaluated type via subtyping or coercion. This is + /// usually called because we want to do that subtype/coerce call manually for better + /// diagnostics. pub(super) fn check_expr_with_hint( &self, expr: &'tcx hir::Expr<'tcx>, @@ -136,6 +146,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_expectation(expr, ExpectHasType(expected)) } + /// Check an expr with an expectation type, and also [`Needs`] which will + /// prompt typeck to convert any implicit immutable derefs to mutable derefs. fn check_expr_with_expectation_and_needs( &self, expr: &'tcx hir::Expr<'tcx>, @@ -153,10 +165,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } + /// Check an expr with no expectations. pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { self.check_expr_with_expectation(expr, NoExpectation) } + /// Check an expr with no expectations, but with [`Needs`] which will + /// prompt typeck to convert any implicit immutable derefs to mutable derefs. pub(super) fn check_expr_with_needs( &self, expr: &'tcx hir::Expr<'tcx>, @@ -165,33 +180,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs) } - /// Invariant: - /// If an expression has any sub-expressions that result in a type error, - /// inspecting that expression's type with `ty.references_error()` will return - /// true. Likewise, if an expression is known to diverge, inspecting its - /// type with `ty::type_is_bot` will return true (n.b.: since Rust is - /// strict, _|_ can appear in the type of an expression that does not, - /// itself, diverge: for example, fn() -> _|_.) - /// Note that inspecting a type's structure *directly* may expose the fact - /// that there are actually multiple representations for `Error`, so avoid - /// that when err needs to be handled differently. + /// Check an expr with an expectation type which may be used to eagerly + /// guide inference when evaluating that expr. #[instrument(skip(self, expr), level = "debug")] pub(super) fn check_expr_with_expectation( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_args(expr, expected, &[], None) + self.check_expr_with_expectation_and_args(expr, expected, None) } - /// Same as `check_expr_with_expectation`, but allows us to pass in the arguments of a - /// `ExprKind::Call` when evaluating its callee when it is an `ExprKind::Path`. + /// Same as [`Self::check_expr_with_expectation`], but allows us to pass in + /// the arguments of a [`ExprKind::Call`] when evaluating its callee that + /// is an [`ExprKind::Path`]. We use this to refine the spans for certain + /// well-formedness guarantees for the path expr. pub(super) fn check_expr_with_expectation_and_args( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - call: Option<&'tcx hir::Expr<'tcx>>, + call_expr_and_args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, ) -> Ty<'tcx> { if self.tcx().sess.verbose_internals() { // make this code only run with -Zverbose-internals because it is probably slow @@ -236,9 +244,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let ty = ensure_sufficient_stack(|| match &expr.kind { + // Intercept the callee path expr and give it better spans. hir::ExprKind::Path( qpath @ (hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)), - ) => self.check_expr_path(qpath, expr, Some(args), call), + ) => self.check_expr_path(qpath, expr, call_expr_and_args), _ => self.check_expr_kind(expr, expected), }); let ty = self.resolve_vars_if_possible(ty); @@ -472,28 +481,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; match expr.kind { - ExprKind::Lit(ref lit) => self.check_lit(lit, expected), - ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected), + ExprKind::Lit(ref lit) => self.check_expr_lit(lit, expected), + ExprKind::Binary(op, lhs, rhs) => self.check_expr_binop(expr, op, lhs, rhs, expected), ExprKind::Assign(lhs, rhs, span) => { self.check_expr_assign(expr, expected, lhs, rhs, span) } ExprKind::AssignOp(op, lhs, rhs) => { - self.check_binop_assign(expr, op, lhs, rhs, expected) + self.check_expr_binop_assign(expr, op, lhs, rhs, expected) } - ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), + ExprKind::Unary(unop, oprnd) => self.check_expr_unop(unop, oprnd, expected, expr), ExprKind::AddrOf(kind, mutbl, oprnd) => { self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) } ExprKind::Path(QPath::LangItem(lang_item, _)) => { self.check_lang_item_path(lang_item, expr) } - ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, None, None), + ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, None), ExprKind::InlineAsm(asm) => { // We defer some asm checks as we may not have resolved the input and output types yet (they may still be infer vars). self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id)); self.check_expr_asm(asm) } - ExprKind::OffsetOf(container, fields) => self.check_offset_of(container, fields, expr), + ExprKind::OffsetOf(container, fields) => { + self.check_expr_offset_of(container, fields, expr) + } ExprKind::Break(destination, ref expr_opt) => { self.check_expr_break(destination, expr_opt.as_deref(), expr) } @@ -512,13 +523,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_loop(body, source, expected, expr) } ExprKind::Match(discrim, arms, match_src) => { - self.check_match(expr, discrim, arms, expected, match_src) + self.check_expr_match(expr, discrim, arms, expected, match_src) } ExprKind::Closure(closure) => self.check_expr_closure(closure, expr.span, expected), - ExprKind::Block(body, _) => self.check_block_with_expected(body, expected), - ExprKind::Call(callee, args) => self.check_call(expr, callee, args, expected), + ExprKind::Block(body, _) => self.check_expr_block(body, expected), + ExprKind::Call(callee, args) => self.check_expr_call(expr, callee, args, expected), ExprKind::MethodCall(segment, receiver, args, _) => { - self.check_method_call(expr, segment, receiver, args, expected) + self.check_expr_method_call(expr, segment, receiver, args, expected) } ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), ExprKind::Type(e, t) => { @@ -528,7 +539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ascribed_ty } ExprKind::If(cond, then_expr, opt_else_expr) => { - self.check_then_else(cond, then_expr, opt_else_expr, expr.span, expected) + self.check_expr_if(cond, then_expr, opt_else_expr, expr.span, expected) } ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), @@ -540,7 +551,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Struct(qpath, fields, ref base_expr) => { self.check_expr_struct(expr, expected, qpath, fields, base_expr) } - ExprKind::Field(base, field) => self.check_field(expr, base, field, expected), + ExprKind::Field(base, field) => self.check_expr_field(expr, base, field, expected), ExprKind::Index(base, idx, brackets_span) => { self.check_expr_index(base, idx, expr, brackets_span) } @@ -549,7 +560,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_expr_unary( + fn check_expr_unop( &self, unop: hir::UnOp, oprnd: &'tcx hir::Expr<'tcx>, @@ -699,8 +710,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, qpath: &'tcx hir::QPath<'tcx>, expr: &'tcx hir::Expr<'tcx>, - args: Option<&'tcx [hir::Expr<'tcx>]>, - call: Option<&'tcx hir::Expr<'tcx>>, + call_expr_and_args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, ) -> Ty<'tcx> { let tcx = self.tcx; let (res, opt_ty, segs) = @@ -730,7 +740,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segs, opt_ty, res, - call.map_or(expr.span, |e| e.span), + call_expr_and_args.map_or(expr.span, |(e, _)| e.span), expr.span, expr.hir_id, ) @@ -769,7 +779,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We just want to check sizedness, so instead of introducing // placeholder lifetimes with probing, we just replace higher lifetimes // with fresh vars. - let span = args.and_then(|args| args.get(i)).map_or(expr.span, |arg| arg.span); + let span = call_expr_and_args + .and_then(|(_, args)| args.get(i)) + .map_or(expr.span, |arg| arg.span); let input = self.instantiate_binder_with_fresh_vars( span, infer::BoundRegionConversionTime::FnCall, @@ -795,7 +807,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.require_type_is_sized_deferred( output, - call.map_or(expr.span, |e| e.span), + call_expr_and_args.map_or(expr.span, |(e, _)| e.span), ObligationCauseCode::SizedCallReturnType, ); } @@ -972,7 +984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.ret_coercion_span.get().is_none() { self.ret_coercion_span.set(Some(e.span)); } - self.check_return_expr(e, true); + self.check_return_or_body_tail(e, true); } else { let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); if self.ret_coercion_span.get().is_none() { @@ -1035,7 +1047,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// `explicit_return` is `true` if we're checking an explicit `return expr`, /// and `false` if we're checking a trailing expression. - pub(super) fn check_return_expr( + pub(super) fn check_return_or_body_tail( &self, return_expr: &'tcx hir::Expr<'tcx>, explicit_return: bool, @@ -1259,7 +1271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A generic function for checking the 'then' and 'else' clauses in an 'if' // or 'if-else' expression. - fn check_then_else( + fn check_expr_if( &self, cond_expr: &'tcx hir::Expr<'tcx>, then_expr: &'tcx hir::Expr<'tcx>, @@ -1542,7 +1554,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Checks a method call. - fn check_method_call( + fn check_expr_method_call( &self, expr: &'tcx hir::Expr<'tcx>, segment: &'tcx hir::PathSegment<'tcx>, @@ -2594,7 +2606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Check field access expressions - fn check_field( + fn check_expr_field( &self, expr: &'tcx hir::Expr<'tcx>, base: &'tcx hir::Expr<'tcx>, @@ -3535,8 +3547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let previous_diverges = self.diverges.get(); // The label blocks should have unit return value or diverge. - let ty = - self.check_block_with_expected(block, ExpectHasType(self.tcx.types.unit)); + let ty = self.check_expr_block(block, ExpectHasType(self.tcx.types.unit)); if !ty.is_never() { self.demand_suptype(block.span, self.tcx.types.unit, ty); diverge = false; @@ -3551,7 +3562,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if diverge { self.tcx.types.never } else { self.tcx.types.unit } } - fn check_offset_of( + fn check_expr_offset_of( &self, container: &'tcx hir::Ty<'tcx>, fields: &[Ident], diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 97f3807c252..7719facccc7 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -621,7 +621,11 @@ impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> { .iter() .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) .count(); - for (idx, arg) in args.iter().enumerate() { + for (idx, arg) in args + .iter() + .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) + .enumerate() + { if let Some(ty) = arg.as_type() && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index ce0ab8a913b..f0738491609 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1306,30 +1306,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rustc_hir_analysis::hir_ty_lowering::RegionInferReason::Param(param), ) .into(), - GenericParamDefKind::Type { has_default, .. } => { - if !infer_args && has_default { - // If we have a default, then it doesn't matter that we're not - // inferring the type arguments: we provide the default where any - // is missing. - tcx.type_of(param.def_id).instantiate(tcx, preceding_args).into() - } else { - // If no type arguments were provided, we have to infer them. - // This case also occurs as a result of some malformed input, e.g. - // a lifetime argument being given instead of a type parameter. - // Using inference instead of `Error` gives better error messages. - self.fcx.var_for_def(self.span, param) + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + if !infer_args && let Some(default) = param.default_value(tcx) { + // If we have a default, then it doesn't matter that we're not inferring + // the type/const arguments: We provide the default where any is missing. + return default.instantiate(tcx, preceding_args); } - } - GenericParamDefKind::Const { has_default, .. } => { - if has_default { - if !infer_args { - return tcx - .const_param_default(param.def_id) - .instantiate(tcx, preceding_args) - .into(); - } - } - + // If no type/const arguments were provided, we have to infer them. + // This case also occurs as a result of some malformed input, e.g., + // a lifetime argument being given instead of a type/const parameter. + // Using inference instead of `Error` gives better error messages. self.fcx.var_for_def(self.span, param) } } @@ -1491,7 +1477,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } else if self.tcx.features().generic_const_exprs() { - ct.normalize_internal(self.tcx, self.param_env) + rustc_trait_selection::traits::evaluate_const(&self.infcx, ct, self.param_env) } else { ct } 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 f93d0e93406..b09d78614c3 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 @@ -316,12 +316,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .generics_of(def_id) .own_args(ty::GenericArgs::identity_for_item(self.tcx, def_id)); - let Some((index, _)) = - own_args.iter().enumerate().find(|(_, arg)| **arg == param_to_point_at) - else { + let Some(mut index) = own_args.iter().position(|arg| *arg == param_to_point_at) else { return false; }; - let Some(arg) = segment.args().args.get(index) else { + // SUBTLE: We may or may not turbofish lifetime arguments, which will + // otherwise be elided. if our "own args" starts with a lifetime, but + // the args list does not, then we should chop off all of the lifetimes, + // since they're all elided. + let segment_args = segment.args().args; + if matches!(own_args[0].unpack(), ty::GenericArgKind::Lifetime(_)) + && segment_args.first().is_some_and(|arg| arg.is_ty_or_const()) + && let Some(offset) = own_args.iter().position(|arg| { + matches!(arg.unpack(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_)) + }) + && let Some(new_index) = index.checked_sub(offset) + { + index = new_index; + } + let Some(arg) = segment_args.get(index) else { return false; }; error.obligation.cause.span = arg diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a6c249da103..50d1322eba6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1565,7 +1565,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // AST fragment checking - pub(in super::super) fn check_lit( + pub(in super::super) fn check_expr_lit( &self, lit: &hir::Lit, expected: Expectation<'tcx>, @@ -1747,7 +1747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(blk) = decl.origin.try_get_else() { let previous_diverges = self.diverges.get(); - let else_ty = self.check_block_with_expected(blk, NoExpectation); + let else_ty = self.check_expr_block(blk, NoExpectation); let cause = self.cause(blk.span, ObligationCauseCode::LetElse); if let Err(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) { @@ -1805,7 +1805,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { let unit = self.tcx.types.unit; - let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); + let ty = self.check_expr_block(blk, ExpectHasType(unit)); // if the block produces a `!` value, that can always be // (effectively) coerced to unit. @@ -1814,7 +1814,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(in super::super) fn check_block_with_expected( + pub(in super::super) fn check_expr_block( &self, blk: &'tcx hir::Block<'tcx>, expected: Expectation<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index f5987a11fa5..63dccf8b0ce 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -4,6 +4,7 @@ #![feature(array_windows)] #![feature(box_patterns)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] #![feature(try_blocks)] diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index cb20a1d7c7b..8772599e316 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -3874,22 +3874,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param.name.ident(), )); let bounds_span = hir_generics.bounds_span_for_suggestions(def_id); + let mut applicability = Applicability::MaybeIncorrect; + // Format the path of each suggested candidate, providing placeholders + // for any generic arguments without defaults. + let candidate_strs: Vec<_> = candidates + .iter() + .map(|cand| { + let cand_path = self.tcx.def_path_str(cand.def_id); + let cand_params = &self.tcx.generics_of(cand.def_id).own_params; + let cand_args: String = cand_params + .iter() + .skip(1) + .filter_map(|param| match param.kind { + ty::GenericParamDefKind::Type { + has_default: true, + .. + } + | ty::GenericParamDefKind::Const { + has_default: true, + .. + } => None, + _ => Some(param.name.as_str()), + }) + .intersperse(", ") + .collect(); + if cand_args.is_empty() { + cand_path + } else { + applicability = Applicability::HasPlaceholders; + format!("{cand_path}</* {cand_args} */>") + } + }) + .collect(); + if rcvr_ty.is_ref() && param.is_impl_trait() && let Some((bounds_span, _)) = bounds_span { err.multipart_suggestions( msg, - candidates.iter().map(|t| { + candidate_strs.iter().map(|cand| { vec![ (param.span.shrink_to_lo(), "(".to_string()), - ( - bounds_span, - format!(" + {})", self.tcx.def_path_str(t.def_id)), - ), + (bounds_span, format!(" + {cand})")), ] }), - Applicability::MaybeIncorrect, + applicability, ); return; } @@ -3905,16 +3935,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (param.span.shrink_to_hi(), Introducer::Colon, None) }; - 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 all_suggs = candidate_strs.iter().map(|cand| { + let suggestion = format!("{} {cand}", match introducer { + Introducer::Plus => " +", + Introducer::Colon => ":", + Introducer::Nothing => "", + },); let mut suggs = vec![]; @@ -3928,11 +3954,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggs }); - err.multipart_suggestions( - msg, - all_suggs, - Applicability::MaybeIncorrect, - ); + err.multipart_suggestions(msg, all_suggs, applicability); return; } diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 9a3492abc9f..72b930ee84d 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -26,7 +26,7 @@ use crate::Expectation; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a <op>= b` - pub(crate) fn check_binop_assign( + pub(crate) fn check_expr_binop_assign( &self, expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, @@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Checks a potentially overloaded binary operator. - pub(crate) fn check_binop( + pub(crate) fn check_expr_binop( &self, expr: &'tcx hir::Expr<'tcx>, op: hir::BinOp, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index fc54d69449f..12df4a10e63 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -25,10 +25,10 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; +use rustc_middle::bug; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::traits::select; pub use rustc_middle::ty::IntVarValue; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -40,7 +40,6 @@ use rustc_middle::ty::{ self, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, GenericArgsRef, GenericParamDefKind, InferConst, IntVid, Ty, TyCtxt, TyVid, TypingMode, }; -use rustc_middle::{bug, span_bug}; use rustc_span::Span; use rustc_span::symbol::Symbol; use rustc_type_ir::solve::Reveal; @@ -1279,84 +1278,6 @@ impl<'tcx> InferCtxt<'tcx> { u } - pub fn try_const_eval_resolve( - &self, - param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::UnevaluatedConst<'tcx>, - span: Span, - ) -> Result<ty::Const<'tcx>, ErrorHandled> { - match self.const_eval_resolve(param_env, unevaluated, span) { - Ok(Ok(val)) => Ok(ty::Const::new_value( - self.tcx, - val, - self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), - )), - Ok(Err(bad_ty)) => { - let tcx = self.tcx; - let def_id = unevaluated.def; - span_bug!( - tcx.def_span(def_id), - "unable to construct a valtree for the unevaluated constant {:?}: type {bad_ty} is not valtree-compatible", - unevaluated - ); - } - Err(err) => Err(err), - } - } - - /// Resolves and evaluates a constant. - /// - /// The constant can be located on a trait like `<A as B>::C`, in which case the given - /// generic parameters and environment are used to resolve the constant. Alternatively if the - /// constant has generic parameters in scope the instantiations are used to evaluate the value - /// of the constant. For example in `fn foo<T>() { let _ = [0; bar::<T>()]; }` the repeat count - /// constant `bar::<T>()` requires a instantiation for `T`, if the instantiation for `T` is - /// still too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is - /// returned. - /// - /// This handles inferences variables within both `param_env` and `args` by - /// performing the operation on their respective canonical forms. - #[instrument(skip(self), level = "debug")] - pub fn const_eval_resolve( - &self, - mut param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::UnevaluatedConst<'tcx>, - span: Span, - ) -> EvalToValTreeResult<'tcx> { - let mut args = self.resolve_vars_if_possible(unevaluated.args); - debug!(?args); - - // Postpone the evaluation of constants whose args depend on inference - // variables - let tcx = self.tcx; - if args.has_non_region_infer() { - 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)); - } else if ct.has_non_region_infer() || ct.has_non_region_param() { - return Err(ErrorHandled::TooGeneric(span)); - } else { - args = replace_param_and_infer_args_with_placeholder(tcx, args); - } - } else { - args = GenericArgs::identity_for_item(tcx, unevaluated.def); - param_env = tcx.param_env(unevaluated.def); - } - } - - let param_env_erased = tcx.erase_regions(param_env); - let args_erased = tcx.erase_regions(args); - debug!(?param_env_erased); - debug!(?args_erased); - - let unevaluated = ty::UnevaluatedConst { def: unevaluated.def, args: args_erased }; - - // The return value is the evaluated value which doesn't contain any reference to inference - // variables, thus we don't need to instantiate back the original values. - tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span) - } - /// The returned function is used in a fast path. If it returns `true` the variable is /// unchanged, `false` indicates that the status is unknown. #[inline] @@ -1622,61 +1543,6 @@ impl RegionVariableOrigin { } } -/// Replaces args that reference param or infer variables with suitable -/// placeholders. This function is meant to remove these param and infer -/// args when they're not actually needed to evaluate a constant. -fn replace_param_and_infer_args_with_placeholder<'tcx>( - tcx: TyCtxt<'tcx>, - args: GenericArgsRef<'tcx>, -) -> GenericArgsRef<'tcx> { - struct ReplaceParamAndInferWithPlaceholder<'tcx> { - tcx: TyCtxt<'tcx>, - idx: u32, - } - - impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceParamAndInferWithPlaceholder<'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Infer(_) = t.kind() { - let idx = { - let idx = self.idx; - self.idx += 1; - idx - }; - Ty::new_placeholder(self.tcx, ty::PlaceholderType { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundTy { - var: ty::BoundVar::from_u32(idx), - kind: ty::BoundTyKind::Anon, - }, - }) - } else { - t.super_fold_with(self) - } - } - - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Infer(_) = c.kind() { - ty::Const::new_placeholder(self.tcx, ty::PlaceholderConst { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundVar::from_u32({ - let idx = self.idx; - self.idx += 1; - idx - }), - }) - } else { - c.super_fold_with(self) - } - } - } - - args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 }) -} - impl<'tcx> InferCtxt<'tcx> { /// Given a [`hir::Block`], get the span of its last expression or /// statement, peeling off any inner blocks. diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index b5abf145d6b..7a2ba07ce87 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -rustc-rayon = { version = "0.5.0", optional = true } -rustc-rayon-core = { version = "0.5.0", optional = true } +rustc-rayon = { version = "0.5.0" } +rustc-rayon-core = { version = "0.5.0" } rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } @@ -54,10 +54,4 @@ tracing = "0.1" [features] # tidy-alphabetical-start llvm = ['dep:rustc_codegen_llvm'] -rustc_use_parallel_compiler = [ - 'dep:rustc-rayon', - 'dep:rustc-rayon-core', - 'rustc_query_impl/rustc_use_parallel_compiler', - 'rustc_errors/rustc_use_parallel_compiler' -] # tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index fd850d2f39a..7e629c1d18f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -175,7 +175,7 @@ fn configure_and_expand( if cfg!(windows) { old_path = env::var_os("PATH").unwrap_or(old_path); let mut new_path = Vec::from_iter( - sess.host_filesearch(PathKind::All).search_paths().map(|p| p.dir.clone()), + sess.host_filesearch().search_paths(PathKind::All).map(|p| p.dir.clone()), ); for path in env::split_paths(&old_path) { if !new_path.contains(&path) { diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index b5bddc4b21a..d3213b1263c 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -6,7 +6,6 @@ use std::{env, iter, thread}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; -#[cfg(parallel_compiler)] use rustc_data_structures::sync; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; @@ -117,19 +116,6 @@ fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>( }) } -#[cfg(not(parallel_compiler))] -pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>( - thread_builder_diag: &EarlyDiagCtxt, - edition: Edition, - _threads: usize, - sm_inputs: SourceMapInputs, - f: F, -) -> R { - let thread_stack_size = init_stack_size(thread_builder_diag); - run_in_thread_with_globals(thread_stack_size, edition, sm_inputs, f) -} - -#[cfg(parallel_compiler)] pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>( thread_builder_diag: &EarlyDiagCtxt, edition: Edition, diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 9187f6caad4..6e35d89b488 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -346,7 +346,6 @@ lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than pos *[other] these lifetimes are } in scope but not mentioned in the type's bounds .note2 = all lifetimes in scope will be captured by `impl Trait`s in edition 2024 - .suggestion = use the precise capturing `use<...>` syntax to make the captures explicit lint_impl_trait_redundant_captures = all possible in-scope parameters are already captured, so `use<...>` syntax is redundant .suggestion = remove the `use<...>` syntax diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 02d22ee49bd..130f3cb7c2a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1298,12 +1298,30 @@ impl UnreachablePub { let mut applicability = Applicability::MachineApplicable; if cx.tcx.visibility(def_id).is_public() && !cx.effective_visibilities.is_reachable(def_id) { + // prefer suggesting `pub(super)` instead of `pub(crate)` when possible, + // except when `pub(super) == pub(crate)` + let new_vis = if let Some(ty::Visibility::Restricted(restricted_did)) = + cx.effective_visibilities.effective_vis(def_id).map(|effective_vis| { + effective_vis.at_level(rustc_middle::middle::privacy::Level::Reachable) + }) + && let parent_parent = cx.tcx.parent_module_from_def_id( + cx.tcx.parent_module_from_def_id(def_id.into()).into(), + ) + && *restricted_did == parent_parent.to_local_def_id() + && !restricted_did.to_def_id().is_crate_root() + { + "pub(super)" + } else { + "pub(crate)" + }; + if vis_span.from_expansion() { applicability = Applicability::MaybeIncorrect; } let def_span = cx.tcx.def_span(def_id); cx.emit_span_lint(UNREACHABLE_PUB, def_span, BuiltinUnreachablePub { what, + new_vis, suggestion: (vis_span, applicability), help: exportable, }); diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 002ab027982..beab4d4e6a9 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -3,7 +3,7 @@ use std::cell::LazyCell; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{Applicability, LintDiagnostic}; +use rustc_errors::{LintDiagnostic, Subdiagnostic}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -22,6 +22,9 @@ use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; +use rustc_trait_selection::errors::{ + AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion, +}; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; @@ -259,7 +262,11 @@ where // If it's owned by this function && let opaque = self.tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty() - && let hir::OpaqueTyOrigin::FnReturn { parent, .. } = opaque.origin + // We want to recurse into RPITs and async fns, even though the latter + // doesn't overcapture on its own, it may mention additional RPITs + // in its bounds. + && let hir::OpaqueTyOrigin::FnReturn { parent, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent, .. } = opaque.origin && parent == self.parent_def_id { let opaque_span = self.tcx.def_span(opaque_def_id); @@ -334,32 +341,12 @@ where // If we have uncaptured args, and if the opaque doesn't already have // `use<>` syntax on it, and we're < edition 2024, then warn the user. if !uncaptured_args.is_empty() { - let suggestion = if let Ok(snippet) = - self.tcx.sess.source_map().span_to_snippet(opaque_span) - && snippet.starts_with("impl ") - { - let (lifetimes, others): (Vec<_>, Vec<_>) = - captured.into_iter().partition(|def_id| { - self.tcx.def_kind(*def_id) == DefKind::LifetimeParam - }); - // Take all lifetime params first, then all others (ty/ct). - let generics: Vec<_> = lifetimes - .into_iter() - .chain(others) - .map(|def_id| self.tcx.item_name(def_id).to_string()) - .collect(); - // Make sure that we're not trying to name any APITs - if generics.iter().all(|name| !name.starts_with("impl ")) { - Some(( - format!(" + use<{}>", generics.join(", ")), - opaque_span.shrink_to_hi(), - )) - } else { - None - } - } else { - None - }; + let suggestion = impl_trait_overcapture_suggestion( + self.tcx, + opaque_def_id, + self.parent_def_id, + captured, + ); let uncaptured_spans: Vec<_> = uncaptured_args .into_iter() @@ -451,7 +438,7 @@ struct ImplTraitOvercapturesLint<'tcx> { uncaptured_spans: Vec<Span>, self_ty: Ty<'tcx>, num_captured: usize, - suggestion: Option<(String, Span)>, + suggestion: Option<AddPreciseCapturingForOvercapture>, } impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { @@ -461,13 +448,8 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { .arg("num_captured", self.num_captured) .span_note(self.uncaptured_spans, fluent::lint_note) .note(fluent::lint_note2); - if let Some((suggestion, span)) = self.suggestion { - diag.span_suggestion( - span, - fluent::lint_suggestion, - suggestion, - Applicability::MachineApplicable, - ); + if let Some(suggestion) = self.suggestion { + suggestion.add_to_diag(diag); } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 38e52570e53..352155729e5 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -254,7 +254,8 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { #[diag(lint_builtin_unreachable_pub)] pub(crate) struct BuiltinUnreachablePub<'a> { pub what: &'a str, - #[suggestion(code = "pub(crate)")] + pub new_vis: &'a str, + #[suggestion(code = "{new_vis}")] pub suggestion: (Span, Applicability), #[help] pub help: bool, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 1ed702ab4cb..489c911d7ee 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -882,10 +882,12 @@ extern "C" LLVMRustResult LLVMRustOptimize( SanitizerOptions->SanitizeKernelAddress) { OptimizerLastEPCallbacks.push_back( #if LLVM_VERSION_GE(20, 0) - [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase phase) { + [SanitizerOptions, TM](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { #else - [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { + [SanitizerOptions, TM](ModulePassManager &MPM, + OptimizationLevel Level) { #endif auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; AddressSanitizerOptions opts = AddressSanitizerOptions{ @@ -895,7 +897,12 @@ extern "C" LLVMRustResult LLVMRustOptimize( /*UseAfterScope=*/true, AsanDetectStackUseAfterReturnMode::Runtime, }; - MPM.addPass(AddressSanitizerPass(opts)); + MPM.addPass(AddressSanitizerPass( + opts, + /*UseGlobalGC*/ true, + // UseOdrIndicator should be false on windows machines + // https://reviews.llvm.org/D137227 + !TM->getTargetTriple().isOSWindows())); }); } if (SanitizerOptions->SanitizeHWAddress) { diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index e525d94a0c1..ca16a66763a 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -507,7 +507,8 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { locator.is_proc_macro = true; locator.target = &self.sess.host; locator.tuple = TargetTuple::from_tuple(config::host_tuple()); - locator.filesearch = self.sess.host_filesearch(path_kind); + locator.filesearch = self.sess.host_filesearch(); + locator.path_kind = path_kind; let Some(host_result) = self.load(locator)? else { return Ok(None); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index ddd97fc66f6..0b53e5eeaa8 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -253,9 +253,10 @@ pub(crate) struct CrateLocator<'a> { extra_filename: Option<&'a str>, pub target: &'a Target, pub tuple: TargetTuple, - pub filesearch: FileSearch<'a>, + pub filesearch: &'a FileSearch, pub is_proc_macro: bool, + pub path_kind: PathKind, // Mutable in-progress state or output. crate_rejections: CrateRejections, } @@ -339,7 +340,8 @@ impl<'a> CrateLocator<'a> { extra_filename, target: &sess.target, tuple: sess.opts.target_triple.clone(), - filesearch: sess.target_filesearch(path_kind), + filesearch: sess.target_filesearch(), + path_kind, is_proc_macro: false, crate_rejections: CrateRejections::default(), } @@ -407,47 +409,49 @@ impl<'a> CrateLocator<'a> { // given that `extra_filename` comes from the `-C extra-filename` // option and thus can be anything, and the incorrect match will be // handled safely in `extract_one`. - for search_path in self.filesearch.search_paths() { + for search_path in self.filesearch.search_paths(self.path_kind) { debug!("searching {}", search_path.dir.display()); - for spf in search_path.files.iter() { - debug!("testing {}", spf.path.display()); + let spf = &search_path.files; - let f = &spf.file_name_str; - let (hash, kind) = if let Some(f) = f.strip_prefix(rlib_prefix) - && let Some(f) = f.strip_suffix(rlib_suffix) - { - (f, CrateFlavor::Rlib) - } else if let Some(f) = f.strip_prefix(rmeta_prefix) - && let Some(f) = f.strip_suffix(rmeta_suffix) - { - (f, CrateFlavor::Rmeta) - } else if let Some(f) = f.strip_prefix(dylib_prefix) - && let Some(f) = f.strip_suffix(dylib_suffix.as_ref()) - { - (f, CrateFlavor::Dylib) - } else { - if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix.as_ref()) { - self.crate_rejections.via_kind.push(CrateMismatch { - path: spf.path.clone(), - got: "static".to_string(), - }); - } - continue; - }; - - info!("lib candidate: {}", spf.path.display()); + let mut should_check_staticlibs = true; + for (prefix, suffix, kind) in [ + (rlib_prefix.as_str(), rlib_suffix, CrateFlavor::Rlib), + (rmeta_prefix.as_str(), rmeta_suffix, CrateFlavor::Rmeta), + (dylib_prefix, dylib_suffix, CrateFlavor::Dylib), + ] { + if prefix == staticlib_prefix && suffix == staticlib_suffix { + should_check_staticlibs = false; + } + if let Some(matches) = spf.query(prefix, suffix) { + for (hash, spf) in matches { + info!("lib candidate: {}", spf.path.display()); - let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default(); - let path = try_canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone()); - if seen_paths.contains(&path) { - continue; - }; - seen_paths.insert(path.clone()); - match kind { - CrateFlavor::Rlib => rlibs.insert(path, search_path.kind), - CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind), - CrateFlavor::Dylib => dylibs.insert(path, search_path.kind), - }; + let (rlibs, rmetas, dylibs) = + candidates.entry(hash.to_string()).or_default(); + let path = + try_canonicalize(&spf.path).unwrap_or_else(|_| spf.path.to_path_buf()); + if seen_paths.contains(&path) { + continue; + }; + seen_paths.insert(path.clone()); + match kind { + CrateFlavor::Rlib => rlibs.insert(path, search_path.kind), + CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind), + CrateFlavor::Dylib => dylibs.insert(path, search_path.kind), + }; + } + } + } + if let Some(static_matches) = should_check_staticlibs + .then(|| spf.query(staticlib_prefix, staticlib_suffix)) + .flatten() + { + for (_, spf) in static_matches { + self.crate_rejections.via_kind.push(CrateMismatch { + path: spf.path.to_path_buf(), + got: "static".to_string(), + }); + } } } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index b7695216f3c..493db498b7c 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -28,10 +28,10 @@ pub fn walk_native_lib_search_dirs<R>( mut f: impl FnMut(&Path, bool /*is_framework*/) -> ControlFlow<R>, ) -> ControlFlow<R> { // Library search paths explicitly supplied by user (`-L` on the command line). - for search_path in sess.target_filesearch(PathKind::Native).cli_search_paths() { + for search_path in sess.target_filesearch().cli_search_paths(PathKind::Native) { f(&search_path.dir, false)?; } - for search_path in sess.target_filesearch(PathKind::Framework).cli_search_paths() { + for search_path in sess.target_filesearch().cli_search_paths(PathKind::Framework) { // Frameworks are looked up strictly in framework-specific paths. if search_path.kind != PathKind::All { f(&search_path.dir, true)?; diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 485d1c14df3..3bda3a4aa63 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -11,7 +11,7 @@ either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" polonius-engine = "0.13.0" -rustc-rayon-core = { version = "0.5.0", optional = true } +rustc-rayon-core = { version = "0.5.0" } rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } @@ -43,5 +43,4 @@ tracing = "0.1" [features] # tidy-alphabetical-start rustc_randomized_layouts = [] -rustc_use_parallel_compiler = ["dep:rustc-rayon-core"] # tidy-alphabetical-end diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 1ae0bd6740d..f95635370dc 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -1,17 +1,17 @@ use std::fmt::{self, Debug, Display, Formatter}; -use either::Either; use rustc_abi::{HasDataLayout, Size}; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; use rustc_span::{DUMMY_SP, Span}; +use rustc_type_ir::visit::TypeVisitableExt; use crate::mir::interpret::{AllocId, ConstAllocation, ErrorHandled, Scalar, alloc_range}; use crate::mir::{Promoted, pretty_print_const_value}; use crate::ty::print::{pretty_print_const, with_no_trimmed_paths}; -use crate::ty::{self, GenericArgsRef, ScalarInt, Ty, TyCtxt}; +use crate::ty::{self, ConstKind, GenericArgsRef, ScalarInt, Ty, TyCtxt}; /////////////////////////////////////////////////////////////////////////// /// Evaluated Constants @@ -319,15 +319,16 @@ impl<'tcx> Const<'tcx> { ) -> Result<ConstValue<'tcx>, ErrorHandled> { match self { Const::Ty(_, c) => { - // We want to consistently have a "clean" value for type system constants (i.e., no - // data hidden in the padding), so we always go through a valtree here. - match c.eval_valtree(tcx, param_env, span) { - Ok((ty, val)) => Ok(tcx.valtree_to_const_val((ty, val))), - Err(Either::Left(_bad_ty)) => Err(tcx - .dcx() - .delayed_bug("`mir::Const::eval` called on a non-valtree-compatible type") - .into()), - Err(Either::Right(e)) => Err(e), + if c.has_non_region_param() { + return Err(ErrorHandled::TooGeneric(span)); + } + + match c.kind() { + ConstKind::Value(ty, val) => Ok(tcx.valtree_to_const_val((ty, val))), + ConstKind::Expr(_) => { + bug!("Normalization of `ty::ConstKind::Expr` is unimplemented") + } + _ => Err(tcx.dcx().delayed_bug("Unevaluated `ty::Const` in MIR body").into()), } } Const::Unevaluated(uneval, _) => { diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 4a876dc1228..11a4e7f89a7 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter}; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; rustc_index::newtype_index! { /// Used by [`CoverageKind::BlockMarker`] to mark blocks during THIR-to-MIR @@ -158,7 +158,6 @@ impl Debug for CoverageKind { #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, Eq, PartialOrd, Ord)] #[derive(TypeFoldable, TypeVisitable)] pub struct SourceRegion { - pub file_name: Symbol, pub start_line: u32, pub start_col: u32, pub end_line: u32, @@ -167,11 +166,8 @@ pub struct SourceRegion { impl Debug for SourceRegion { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - write!( - fmt, - "{}:{}:{} - {}:{}", - self.file_name, self.start_line, self.start_col, self.end_line, self.end_col - ) + let &Self { start_line, start_col, end_line, end_col } = self; + write!(fmt, "{start_line}:{start_col} - {end_line}:{end_col}") } } @@ -246,6 +242,7 @@ pub struct Mapping { #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct FunctionCoverageInfo { pub function_source_hash: u64, + pub body_span: Span, pub num_counters: usize, pub mcdc_bitmap_bits: usize, pub expressions: IndexVec<ExpressionId, Expression>, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index d0f93c19e44..f0e2b7a376c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -596,8 +596,10 @@ fn write_function_coverage_info( function_coverage_info: &coverage::FunctionCoverageInfo, w: &mut dyn io::Write, ) -> io::Result<()> { - let coverage::FunctionCoverageInfo { expressions, mappings, .. } = function_coverage_info; + let coverage::FunctionCoverageInfo { body_span, expressions, mappings, .. } = + function_coverage_info; + writeln!(w, "{INDENT}coverage body span: {body_span:?}")?; for (id, expression) in expressions.iter_enumerated() { writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 54ead9a7a75..4e3668822ec 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -463,7 +463,7 @@ rustc_queries! { separate_provide_extern } - /// Fetch the THIR for a given body. If typeck for that body failed, returns an empty `Thir`. + /// Fetch the THIR for a given body. query thir_body(key: LocalDefId) -> Result<(&'tcx Steal<thir::Thir<'tcx>>, thir::ExprId), ErrorGuaranteed> { // Perf tests revealed that hashing THIR is inefficient (see #85729). no_hash @@ -2326,13 +2326,19 @@ rustc_queries! { separate_provide_extern } - /// Check the signature of this function as well as all the call expressions inside of it - /// to ensure that any target features required by the ABI are enabled. - /// Should be called on a fully monomorphized instance. - query check_feature_dependent_abi(key: ty::Instance<'tcx>) { - desc { "check for feature-dependent ABI" } + /// Perform monomorphization-time checking on this item. + /// This is used for lints/errors that can only be checked once the instance is fully + /// monomorphized. + query check_mono_item(key: ty::Instance<'tcx>) { + desc { "monomorphization-time checking" } cache_on_disk_if { true } } + + /// Builds the set of functions that should be skipped for the move-size check. + query skip_move_check_fns(_: ()) -> &'tcx FxIndexSet<DefId> { + arena_cache + desc { "functions to skip for move-size check" } + } } rustc_query_append! { define_callbacks! } diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 57da5b39ba7..20ba1b27c0e 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -319,7 +319,7 @@ macro_rules! define_callbacks { pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache<Erase<$V>>; - // Ensure that keys grow no larger than 72 bytes by accident. + // Ensure that keys grow no larger than 80 bytes by accident. // Increase this limit if necessary, but do try to keep the size low if possible #[cfg(target_pointer_width = "64")] const _: () = { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 45ceb0a555d..70df2379016 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -645,7 +645,7 @@ impl<'tcx> Pat<'tcx> { | Binding { subpattern: Some(subpattern), .. } | Deref { subpattern } | DerefPattern { subpattern, .. } - | InlineConstant { subpattern, .. } => subpattern.walk_(it), + | ExpandedConstant { subpattern, .. } => subpattern.walk_(it), Leaf { subpatterns } | Variant { subpatterns, .. } => { subpatterns.iter().for_each(|field| field.pattern.walk_(it)) } @@ -788,12 +788,17 @@ pub enum PatKind<'tcx> { value: mir::Const<'tcx>, }, - /// Inline constant found while lowering a pattern. - InlineConstant { - /// [LocalDefId] of the constant, we need this so that we have a + /// Pattern obtained by converting a constant (inline or named) to its pattern + /// representation using `const_to_pat`. + ExpandedConstant { + /// [DefId] of the constant, we need this so that we have a /// reference that can be used by unsafety checking to visit nested - /// unevaluated constants. - def: LocalDefId, + /// unevaluated constants and for diagnostics. If the `DefId` doesn't + /// correspond to a local crate, it points at the `const` item. + def_id: DefId, + /// If `false`, then `def_id` points at a `const` item, otherwise it + /// corresponds to a local inline const. + is_inline: bool, /// If the inline constant is used in a range pattern, this subpattern /// represents the range (if both ends are inline constants, there will /// be multiple InlineConstant wrappers). diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 36f0e3d890c..81202a6eaad 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -247,7 +247,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( } } Constant { value: _ } => {} - InlineConstant { def: _, subpattern } => visitor.visit_pat(subpattern), + ExpandedConstant { def_id: _, is_inline: _, subpattern } => visitor.visit_pat(subpattern), Range(_) => {} Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => { for subpattern in prefix.iter() { diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 5ab85a69ce6..5689f3d4265 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,4 +1,3 @@ -use either::Either; use rustc_data_structures::intern::Interned; use rustc_error_messages::MultiSpan; use rustc_hir::def::{DefKind, Res}; @@ -9,7 +8,7 @@ use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; use tracing::{debug, instrument}; use crate::middle::resolve_bound_vars as rbv; -use crate::mir::interpret::{ErrorHandled, LitToConstInput, Scalar}; +use crate::mir::interpret::{LitToConstInput, Scalar}; use crate::ty::{self, GenericArgs, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; mod int; @@ -18,7 +17,7 @@ mod valtree; pub use int::*; pub use kind::*; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed}; pub use valtree::*; pub type ConstKind<'tcx> = ir::ConstKind<TyCtxt<'tcx>>; @@ -363,60 +362,6 @@ impl<'tcx> Const<'tcx> { Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) } - /// Returns the evaluated constant as a valtree; - /// if that fails due to a valtree-incompatible type, indicate which type that is - /// by returning `Err(Left(bad_type))`. - #[inline] - pub fn eval_valtree( - self, - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - span: Span, - ) -> Result<(Ty<'tcx>, ValTree<'tcx>), Either<Ty<'tcx>, ErrorHandled>> { - assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); - match self.kind() { - ConstKind::Unevaluated(unevaluated) => { - // FIXME(eddyb) maybe the `const_eval_*` methods should take - // `ty::ParamEnvAnd` instead of having them separate. - let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env); - // try to resolve e.g. associated constants to their definition on an impl, and then - // evaluate the const. - match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span) { - Ok(Ok(c)) => { - Ok((tcx.type_of(unevaluated.def).instantiate(tcx, unevaluated.args), c)) - } - Ok(Err(bad_ty)) => Err(Either::Left(bad_ty)), - Err(err) => Err(Either::Right(err)), - } - } - ConstKind::Value(ty, val) => Ok((ty, val)), - ConstKind::Error(g) => Err(Either::Right(g.into())), - ConstKind::Param(_) - | ConstKind::Infer(_) - | ConstKind::Bound(_, _) - | ConstKind::Placeholder(_) - | ConstKind::Expr(_) => Err(Either::Right(ErrorHandled::TooGeneric(span))), - } - } - - /// Normalizes the constant to a value or an error if possible. - #[inline] - pub fn normalize_internal(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - match self.eval_valtree(tcx, param_env, DUMMY_SP) { - Ok((ty, val)) => Self::new_value(tcx, val, ty), - Err(Either::Left(_bad_ty)) => { - // This can happen when we run on ill-typed code. - Self::new_error( - tcx, - tcx.dcx() - .delayed_bug("`ty::Const::eval` called on a non-valtree-compatible type"), - ) - } - Err(Either::Right(ErrorHandled::Reported(r, _span))) => Self::new_error(tcx, r.into()), - Err(Either::Right(ErrorHandled::TooGeneric(_span))) => self, - } - } - /// Panics if self.kind != ty::ConstKind::Value pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) { match self.kind() { diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 91b764ae1d4..b3436550e8e 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -1,46 +1,12 @@ use std::assert_matches::assert_matches; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, extension}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use super::Const; use crate::mir; use crate::ty::abstract_const::CastKind; -use crate::ty::visit::TypeVisitableExt as _; use crate::ty::{self, Ty, TyCtxt}; -#[extension(pub(crate) trait UnevaluatedConstEvalExt<'tcx>)] -impl<'tcx> ty::UnevaluatedConst<'tcx> { - /// FIXME(RalfJung): I cannot explain what this does or why it makes sense, but not doing this - /// hurts performance. - #[inline] - fn prepare_for_eval( - self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> (ty::ParamEnv<'tcx>, Self) { - // HACK(eddyb) this erases lifetimes even though `const_eval_resolve` - // also does later, but we want to do it before checking for - // inference variables. - // Note that we erase regions *before* calling `with_reveal_all_normalized`, - // so that we don't try to invoke this query with - // any region variables. - - // HACK(eddyb) when the query key would contain inference variables, - // attempt using identity args and `ParamEnv` instead, that will succeed - // when the expression doesn't depend on any parameters. - // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that - // we can call `infcx.const_eval_resolve` which handles inference variables. - if (param_env, self).has_non_region_infer() { - (tcx.param_env(self.def), ty::UnevaluatedConst { - def: self.def, - args: ty::GenericArgs::identity_for_item(tcx, self.def), - }) - } else { - (tcx.erase_regions(param_env).with_reveal_all_normalized(tcx), tcx.erase_regions(self)) - } - } -} - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)] pub enum ExprKind { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c55733da7b3..84ac281c258 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -22,9 +22,9 @@ 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, RwLock, WorkerLocal}; -#[cfg(parallel_compiler)] -use rustc_data_structures::sync::{DynSend, DynSync}; +use rustc_data_structures::sync::{ + self, DynSend, DynSync, FreezeReadGuard, Lock, Lrc, RwLock, WorkerLocal, +}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, @@ -1259,9 +1259,7 @@ pub struct TyCtxt<'tcx> { } // Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. -#[cfg(parallel_compiler)] unsafe impl DynSend for TyCtxt<'_> {} -#[cfg(parallel_compiler)] unsafe impl DynSync for TyCtxt<'_> {} fn _assert_tcx_fields() { sync::assert_dyn_sync::<&'_ GlobalCtxt<'_>>(); @@ -1383,9 +1381,7 @@ pub struct CurrentGcx { value: Lrc<RwLock<Option<*const ()>>>, } -#[cfg(parallel_compiler)] unsafe impl DynSend for CurrentGcx {} -#[cfg(parallel_compiler)] unsafe impl DynSync for CurrentGcx {} impl CurrentGcx { diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index 6a5d3030646..eaab8474dd2 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -1,5 +1,3 @@ -#[cfg(not(parallel_compiler))] -use std::cell::Cell; use std::{mem, ptr}; use rustc_data_structures::sync::{self, Lock}; @@ -50,16 +48,8 @@ impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { } // Import the thread-local variable from Rayon, which is preserved for Rayon jobs. -#[cfg(parallel_compiler)] use rayon_core::tlv::TLV; -// Otherwise define our own -#[cfg(not(parallel_compiler))] -thread_local! { - /// A thread local variable that stores a pointer to the current `ImplicitCtxt`. - static TLV: Cell<*const ()> = const { Cell::new(ptr::null()) }; -} - #[inline] fn erase(context: &ImplicitCtxt<'_, '_>) -> *const () { context as *const _ as *const () diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 737f1362b34..fd84d75b53f 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -143,12 +143,10 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for GenericArg<'tcx> { } } -#[cfg(parallel_compiler)] unsafe impl<'tcx> rustc_data_structures::sync::DynSend for GenericArg<'tcx> where &'tcx (Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>): rustc_data_structures::sync::DynSend { } -#[cfg(parallel_compiler)] unsafe impl<'tcx> rustc_data_structures::sync::DynSync for GenericArg<'tcx> where &'tcx (Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>): rustc_data_structures::sync::DynSync { diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 19779740227..ce40ab18261 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -86,10 +86,10 @@ impl GenericParamDef { tcx: TyCtxt<'tcx>, ) -> Option<EarlyBinder<'tcx, ty::GenericArg<'tcx>>> { match self.kind { - GenericParamDefKind::Type { has_default, .. } if has_default => { + GenericParamDefKind::Type { has_default: true, .. } => { Some(tcx.type_of(self.def_id).map_bound(|t| t.into())) } - GenericParamDefKind::Const { has_default, .. } if has_default => { + GenericParamDefKind::Const { has_default: true, .. } => { Some(tcx.const_param_default(self.def_id).map_bound(|c| c.into())) } _ => None, diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index ea07cd3a1b9..30a5586f59c 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -5,7 +5,6 @@ use std::ops::Deref; use std::{fmt, iter, mem, ptr, slice}; use rustc_data_structures::aligned::{Aligned, align_of}; -#[cfg(parallel_compiler)] use rustc_data_structures::sync::DynSync; use rustc_serialize::{Encodable, Encoder}; @@ -259,7 +258,6 @@ impl<'a, H, T: Copy> IntoIterator for &'a RawList<H, T> { unsafe impl<H: Sync, T: Sync> Sync for RawList<H, T> {} // We need this since `List` uses extern type `OpaqueListContents`. -#[cfg(parallel_compiler)] unsafe impl<H: DynSync, T: DynSync> DynSync for RawList<H, T> {} // Safety: diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 1a3128ed936..7fda0662a34 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -487,12 +487,10 @@ impl<'tcx> rustc_type_ir::inherent::IntoKind for Term<'tcx> { } } -#[cfg(parallel_compiler)] unsafe impl<'tcx> rustc_data_structures::sync::DynSend for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): rustc_data_structures::sync::DynSend { } -#[cfg(parallel_compiler)] unsafe impl<'tcx> rustc_data_structures::sync::DynSync for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): rustc_data_structures::sync::DynSync { diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index 07964e304b9..62d173987fc 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -144,12 +144,20 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let mut targets = Vec::new(); for arm in rest { let arm = &self.thir[*arm]; - let PatKind::Constant { value } = arm.pattern.kind else { - return Err(ParseError { - span: arm.pattern.span, - item_description: format!("{:?}", arm.pattern.kind), - expected: "constant pattern".to_string(), - }); + let value = match arm.pattern.kind { + PatKind::Constant { value } => value, + PatKind::ExpandedConstant { ref subpattern, def_id: _, is_inline: false } + if let PatKind::Constant { value } = subpattern.kind => + { + value + } + _ => { + return Err(ParseError { + span: arm.pattern.span, + item_description: format!("{:?}", arm.pattern.kind), + expected: "constant pattern".to_string(), + }); + } }; values.push(value.eval_bits(self.tcx, self.param_env)); targets.push(self.parse_block(arm.body)?); diff --git a/compiler/rustc_mir_build/src/build/matches/match_pair.rs b/compiler/rustc_mir_build/src/build/matches/match_pair.rs index 6df50057ee8..6beabb5ccdb 100644 --- a/compiler/rustc_mir_build/src/build/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/build/matches/match_pair.rs @@ -162,7 +162,11 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { TestCase::Irrefutable { ascription: None, binding } } - PatKind::InlineConstant { subpattern: ref pattern, def, .. } => { + PatKind::ExpandedConstant { subpattern: ref pattern, def_id: _, is_inline: false } => { + subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx)); + default_irrefutable() + } + PatKind::ExpandedConstant { subpattern: ref pattern, def_id, is_inline: true } => { // Apply a type ascription for the inline constant to the value at `match_pair.place` let ascription = place.map(|source| { let span = pattern.span; @@ -173,7 +177,7 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { }) .args; let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf( - def.to_def_id(), + def_id, ty::UserArgs { args, user_self_ty: None }, )); let annotation = ty::CanonicalUserTypeAnnotation { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index a62d4e9d873..9f81e1052d6 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -917,7 +917,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_primary_bindings(subpattern, subpattern_user_ty, f) } - PatKind::InlineConstant { ref subpattern, .. } => { + PatKind::ExpandedConstant { ref subpattern, .. } => { self.visit_primary_bindings(subpattern, pattern_user_ty, f) } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index f3e6301d9d1..52e5f2950a5 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -332,7 +332,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { PatKind::Wild | // these just wrap other patterns, which we recurse on below. PatKind::Or { .. } | - PatKind::InlineConstant { .. } | + PatKind::ExpandedConstant { .. } | PatKind::AscribeUserType { .. } | PatKind::Error(_) => {} } @@ -386,8 +386,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_pat(self, pat); self.inside_adt = old_inside_adt; } - PatKind::InlineConstant { def, .. } => { - self.visit_inner_body(*def); + PatKind::ExpandedConstant { def_id, is_inline, .. } => { + if let Some(def) = def_id.as_local() + && *is_inline + { + self.visit_inner_body(def); + } visit::walk_pat(self, pat); } _ => { @@ -1005,10 +1009,6 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { // Runs all other queries that depend on THIR. tcx.ensure_with_value().mir_built(def); let thir = &thir.steal(); - // If `thir` is empty, a type error occurred, skip this body. - if thir.exprs.is_empty() { - return; - } let hir_id = tcx.local_def_id_to_hir_id(def); let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| { diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 00f65e0c7d0..4443d9aebc6 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -860,8 +860,10 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { pub(crate) uncovered: Uncovered, #[subdiagnostic] pub(crate) inform: Option<Inform>, + #[label(mir_build_confused)] + pub(crate) interpreted_as_const: Option<Span>, #[subdiagnostic] - pub(crate) interpreted_as_const: Option<InterpretedAsConst>, + pub(crate) interpreted_as_const_sugg: Option<InterpretedAsConst>, #[subdiagnostic] pub(crate) adt_defined_here: Option<AdtDefinedHere<'tcx>>, #[note(mir_build_privately_uninhabited)] @@ -911,9 +913,9 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> { #[suggestion( mir_build_interpreted_as_const, code = "{variable}_var", - applicability = "maybe-incorrect" + applicability = "maybe-incorrect", + style = "verbose" )] -#[label(mir_build_confused)] pub(crate) struct InterpretedAsConst { #[primary_span] pub(crate) span: Span, 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 f222a869c03..4b872d9b7f7 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -668,8 +668,25 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { let mut let_suggestion = None; let mut misc_suggestion = None; let mut interpreted_as_const = None; + let mut interpreted_as_const_sugg = None; - if let PatKind::Constant { .. } + if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } + | PatKind::AscribeUserType { + subpattern: + box Pat { kind: PatKind::ExpandedConstant { def_id, is_inline: false, .. }, .. }, + .. + } = pat.kind + && let DefKind::Const = self.tcx.def_kind(def_id) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) + // We filter out paths with multiple path::segments. + && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') + { + let span = self.tcx.def_span(def_id); + let variable = self.tcx.item_name(def_id).to_string(); + // When we encounter a constant as the binding name, point at the `const` definition. + interpreted_as_const = Some(span); + interpreted_as_const_sugg = Some(InterpretedAsConst { span: pat.span, variable }); + } else if let PatKind::Constant { .. } | PatKind::AscribeUserType { subpattern: box Pat { kind: PatKind::Constant { .. }, .. }, .. @@ -682,9 +699,6 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral { start_span: pat.span.shrink_to_lo(), }); - } else if snippet.chars().all(|c| c.is_alphanumeric() || c == '_') { - interpreted_as_const = - Some(InterpretedAsConst { span: pat.span, variable: snippet }); } } @@ -733,6 +747,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { uncovered: Uncovered::new(pat.span, &cx, witnesses), inform, interpreted_as_const, + interpreted_as_const_sugg, witness_1_is_privately_uninhabited, _p: (), pattern_ty, @@ -1102,13 +1117,13 @@ fn report_non_exhaustive_match<'p, 'tcx>( if ty.is_ptr_sized_integral() { if ty.inner() == cx.tcx.types.usize { err.note(format!( - "`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \ - exhaustively", + "`{ty}` does not have a fixed maximum value, so half-open ranges are \ + necessary to match exhaustively", )); } else if ty.inner() == cx.tcx.types.isize { err.note(format!( - "`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \ - exhaustively", + "`{ty}` does not have fixed minimum and maximum values, so half-open \ + ranges are necessary to match exhaustively", )); } } else if ty.inner() == cx.tcx.types.str_ { @@ -1129,6 +1144,31 @@ fn report_non_exhaustive_match<'p, 'tcx>( } } + for &arm in arms { + let arm = &thir.arms[arm]; + if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = arm.pattern.kind + && let Ok(snippet) = cx.tcx.sess.source_map().span_to_snippet(arm.pattern.span) + // We filter out paths with multiple path::segments. + && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') + { + let const_name = cx.tcx.item_name(def_id); + err.span_label( + arm.pattern.span, + format!( + "this pattern doesn't introduce a new catch-all binding, but rather pattern \ + matches against the value of constant `{const_name}`", + ), + ); + err.span_note(cx.tcx.def_span(def_id), format!("constant `{const_name}` defined here")); + err.span_suggestion_verbose( + arm.pattern.span.shrink_to_hi(), + "if you meant to introduce a binding, use a different name", + "_var".to_string(), + Applicability::MaybeIncorrect, + ); + } + } + // Whether we suggest the actual missing patterns or `_`. let suggest_the_witnesses = witnesses.len() < 4; let suggested_arm = if suggest_the_witnesses { 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 983853d2de1..82632350af5 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 @@ -1,14 +1,13 @@ -use either::Either; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; use rustc_hir as hir; use rustc_index::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::Obligation; -use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode, ValTree}; +use rustc_middle::{mir, span_bug}; use rustc_span::Span; use rustc_trait_selection::traits::ObligationCause; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -40,7 +39,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // of opaques defined in this function here. let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let mut convert = ConstToPat::new(self, id, span, infcx); - convert.to_pat(c, ty) + + match c.kind() { + ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty), + ty::ConstKind::Value(_, val) => convert.valtree_to_pat(val, ty), + _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c), + } } } @@ -81,27 +85,42 @@ impl<'tcx> ConstToPat<'tcx> { ty.is_structural_eq_shallow(self.infcx.tcx) } - fn to_pat(&mut self, c: ty::Const<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> { + fn unevaluated_to_pat( + &mut self, + uv: ty::UnevaluatedConst<'tcx>, + ty: Ty<'tcx>, + ) -> Box<Pat<'tcx>> { trace!(self.treat_byte_string_as_slice); let pat_from_kind = |kind| Box::new(Pat { span: self.span, ty, kind }); - // Get a valtree. If that fails, this const is definitely not valid for use as a pattern. - let valtree = match c.eval_valtree(self.tcx(), self.param_env, self.span) { - Ok((_, valtree)) => valtree, - Err(Either::Right(e)) => { - let err = match e { - ErrorHandled::Reported(..) => { - // Let's tell the use where this failing const occurs. - self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span }) - } - ErrorHandled::TooGeneric(_) => self - .tcx() - .dcx() - .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }), - }; - return pat_from_kind(PatKind::Error(err)); + // It's not *technically* correct to be revealing opaque types here as borrowcheck has + // not run yet. However, CTFE itself uses `Reveal::All` unconditionally even during + // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). As a + // result we always use a revealed env when resolving the instance to evaluate. + // + // FIXME: `const_eval_resolve_for_typeck` should probably just set the env to `Reveal::All` + // instead of having this logic here + let param_env = + self.tcx().erase_regions(self.param_env).with_reveal_all_normalized(self.tcx()); + let uv = self.tcx().erase_regions(uv); + + // try to resolve e.g. associated constants to their definition on an impl, and then + // evaluate the const. + let valtree = match self.infcx.tcx.const_eval_resolve_for_typeck(param_env, uv, self.span) { + Ok(Ok(c)) => c, + Err(ErrorHandled::Reported(_, _)) => { + // Let's tell the use where this failing const occurs. + let e = self.tcx().dcx().emit_err(CouldNotEvalConstPattern { span: self.span }); + return pat_from_kind(PatKind::Error(e)); } - Err(Either::Left(bad_ty)) => { + Err(ErrorHandled::TooGeneric(_)) => { + let e = self + .tcx() + .dcx() + .emit_err(ConstPatternDependsOnGenericParameter { span: self.span }); + return pat_from_kind(PatKind::Error(e)); + } + Ok(Err(bad_ty)) => { // The pattern cannot be turned into a valtree. let e = match bad_ty.kind() { ty::Adt(def, ..) => { @@ -128,8 +147,7 @@ impl<'tcx> ConstToPat<'tcx> { if !self.type_has_partial_eq_impl(ty) { let err = TypeNotPartialEq { span: self.span, non_peq_ty: ty }; let e = self.tcx().dcx().emit_err(err); - let kind = PatKind::Error(e); - return Box::new(Pat { span: self.span, ty, kind }); + return pat_from_kind(PatKind::Error(e)); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index ec852add94d..9b63d788194 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -149,21 +149,30 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { None => Ok((None, None, None)), Some(expr) => { let (kind, ascr, inline_const) = match self.lower_lit(expr) { - PatKind::InlineConstant { subpattern, def } => { - (subpattern.kind, None, Some(def)) + PatKind::ExpandedConstant { subpattern, def_id, is_inline: true } => { + (subpattern.kind, None, def_id.as_local()) + } + PatKind::ExpandedConstant { subpattern, is_inline: false, .. } => { + (subpattern.kind, None, None) } PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => { (kind, Some(ascription), None) } kind => (kind, None, None), }; - let value = if let PatKind::Constant { value } = kind { - value - } else { - let msg = format!( - "found bad range pattern endpoint `{expr:?}` outside of error recovery" - ); - return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg)); + let value = match kind { + PatKind::Constant { value } => value, + PatKind::ExpandedConstant { subpattern, .. } + if let PatKind::Constant { value } = subpattern.kind => + { + value + } + _ => { + let msg = format!( + "found bad range pattern endpoint `{expr:?}` outside of error recovery" + ); + return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg)); + } }; Ok((Some(PatRangeBoundary::Finite(value)), ascr, inline_const)) } @@ -288,7 +297,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }; } for def in [lo_inline, hi_inline].into_iter().flatten() { - kind = PatKind::InlineConstant { def, subpattern: Box::new(Pat { span, ty, kind }) }; + kind = PatKind::ExpandedConstant { + def_id: def.to_def_id(), + is_inline: true, + subpattern: Box::new(Pat { span, ty, kind }), + }; } Ok(kind) } @@ -562,7 +575,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let args = self.typeck_results.node_args(id); let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args }); - let pattern = self.const_to_pat(c, ty, id, span); + let subpattern = self.const_to_pat(c, ty, id, span); + let pattern = Box::new(Pat { + span, + ty, + kind: PatKind::ExpandedConstant { subpattern, def_id, is_inline: false }, + }); if !is_associated_const { return pattern; @@ -637,7 +655,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args }; let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span); - PatKind::InlineConstant { subpattern, def: def_id } + PatKind::ExpandedConstant { subpattern, def_id: def_id.to_def_id(), is_inline: true } } /// Converts literals, paths and negation of literals to patterns. diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index dae13df4054..6be0ed5fb31 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -707,9 +707,10 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, format!("value: {:?}", value), depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } - PatKind::InlineConstant { def, subpattern } => { - print_indented!(self, "InlineConstant {", depth_lvl + 1); - print_indented!(self, format!("def: {:?}", def), depth_lvl + 2); + PatKind::ExpandedConstant { def_id, is_inline, subpattern } => { + print_indented!(self, "ExpandedConstant {", depth_lvl + 1); + print_indented!(self, format!("def_id: {def_id:?}"), depth_lvl + 2); + print_indented!(self, format!("is_inline: {is_inline:?}"), depth_lvl + 2); print_indented!(self, "subpattern:", depth_lvl + 2); self.print_pat(subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index c8992b8b834..9bbfae17fd9 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -34,3 +34,5 @@ mir_transform_undefined_transmute = pointers cannot be transmuted to integers du .note = at compile-time, pointers do not have an integer value .note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior .help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html + +mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 2e4c503f3ce..e8b3d80be02 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -22,8 +22,8 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, RelativeBytePos, Span, Symbol}; -use tracing::{debug, debug_span, instrument, trace}; +use rustc_span::{BytePos, Pos, SourceFile, Span}; +use tracing::{debug, debug_span, trace}; use crate::coverage::counters::{CounterIncrementSite, CoverageCounters}; use crate::coverage::graph::CoverageGraph; @@ -122,6 +122,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { function_source_hash: hir_info.function_source_hash, + body_span: hir_info.body_span, num_counters: coverage_counters.num_counters(), mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits, expressions: coverage_counters.into_expressions(), @@ -142,19 +143,11 @@ fn create_mappings<'tcx>( coverage_counters: &CoverageCounters, ) -> Vec<Mapping> { let source_map = tcx.sess.source_map(); - let body_span = hir_info.body_span; - - let source_file = source_map.lookup_source_file(body_span.lo()); - - use rustc_session::RemapFileNameExt; - use rustc_session::config::RemapPathScopeComponents; - let file_name = Symbol::intern( - &source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(), - ); + let file = source_map.lookup_source_file(hir_info.body_span.lo()); let term_for_bcb = |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters"); - let region_for_span = |span: Span| make_source_region(source_map, file_name, span, body_span); + let region_for_span = |span: Span| make_source_region(source_map, hir_info, &file, span); // Fully destructure the mappings struct to make sure we don't miss any kinds. let ExtractedMappings { @@ -398,7 +391,42 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb data.statements.insert(0, statement); } -/// Convert the Span into its file name, start line and column, and end line and column. +fn ensure_non_empty_span( + source_map: &SourceMap, + hir_info: &ExtractedHirInfo, + span: Span, +) -> Option<Span> { + if !span.is_empty() { + return Some(span); + } + + let lo = span.lo(); + let hi = span.hi(); + + // The span is empty, so try to expand it to cover an adjacent '{' or '}', + // but only within the bounds of the body span. + let try_next = hi < hir_info.body_span.hi(); + let try_prev = hir_info.body_span.lo() < lo; + if !(try_next || try_prev) { + return None; + } + + source_map + .span_to_source(span, |src, start, end| try { + // We're only checking for specific ASCII characters, so we don't + // have to worry about multi-byte code points. + if try_next && src.as_bytes()[end] == b'{' { + Some(span.with_hi(hi + BytePos(1))) + } else if try_prev && src.as_bytes()[start - 1] == b'}' { + Some(span.with_lo(lo - BytePos(1))) + } else { + None + } + }) + .ok()? +} + +/// Converts the span into its start line and column, and end line and column. /// /// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by /// the compiler, these column numbers are denoted in **bytes**, because that's what @@ -408,56 +436,29 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb /// but it's hard to rule out entirely (especially in the presence of complex macros /// or other expansions), and if it does happen then skipping a span or function is /// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. -#[instrument(level = "debug", skip(source_map))] fn make_source_region( source_map: &SourceMap, - file_name: Symbol, + hir_info: &ExtractedHirInfo, + file: &SourceFile, span: Span, - body_span: Span, ) -> Option<SourceRegion> { + let span = ensure_non_empty_span(source_map, hir_info, span)?; + let lo = span.lo(); let hi = span.hi(); - let file = source_map.lookup_source_file(lo); - if !file.contains(hi) { - debug!(?span, ?file, ?lo, ?hi, "span crosses multiple files; skipping"); - return None; - } - // Column numbers need to be in bytes, so we can't use the more convenient // `SourceMap` methods for looking up file coordinates. - let rpos_and_line_and_byte_column = |pos: BytePos| -> Option<(RelativeBytePos, usize, usize)> { + let line_and_byte_column = |pos: BytePos| -> Option<(usize, usize)> { let rpos = file.relative_position(pos); let line_index = file.lookup_line(rpos)?; let line_start = file.lines()[line_index]; // Line numbers and column numbers are 1-based, so add 1 to each. - Some((rpos, line_index + 1, (rpos - line_start).to_usize() + 1)) + Some((line_index + 1, (rpos - line_start).to_usize() + 1)) }; - let (lo_rpos, mut start_line, mut start_col) = rpos_and_line_and_byte_column(lo)?; - let (hi_rpos, mut end_line, mut end_col) = rpos_and_line_and_byte_column(hi)?; - - // If the span is empty, try to expand it horizontally by one character's - // worth of bytes, so that it is more visible in `llvm-cov` reports. - // We do this after resolving line/column numbers, so that empty spans at the - // end of a line get an extra column instead of wrapping to the next line. - if span.is_empty() - && body_span.contains(span) - && let Some(src) = &file.src - { - // Prefer to expand the end position, if it won't go outside the body span. - if hi < body_span.hi() { - let hi_rpos = hi_rpos.to_usize(); - let nudge_bytes = src.ceil_char_boundary(hi_rpos + 1) - hi_rpos; - end_col += nudge_bytes; - } else if lo > body_span.lo() { - let lo_rpos = lo_rpos.to_usize(); - let nudge_bytes = lo_rpos - src.floor_char_boundary(lo_rpos - 1); - // Subtract the nudge, but don't go below column 1. - start_col = start_col.saturating_sub(nudge_bytes).max(1); - } - // If neither nudge could be applied, stick with the empty span coordinates. - } + let (mut start_line, start_col) = line_and_byte_column(lo)?; + let (mut end_line, end_col) = line_and_byte_column(hi)?; // Apply an offset so that code in doctests has correct line numbers. // FIXME(#79417): Currently we have no way to offset doctest _columns_. @@ -465,7 +466,6 @@ fn make_source_region( end_line = source_map.doctest_offset_line(&file.name, end_line); check_source_region(SourceRegion { - file_name, start_line: start_line as u32, start_col: start_col as u32, end_line: end_line as u32, @@ -478,7 +478,7 @@ fn make_source_region( /// discard regions that are improperly ordered, or might be interpreted in a /// way that makes them improperly ordered. fn check_source_region(source_region: SourceRegion) -> Option<SourceRegion> { - let SourceRegion { file_name: _, start_line, start_col, end_line, end_col } = source_region; + let SourceRegion { start_line, start_col, end_line, end_col } = source_region; // Line/column coordinates are supposed to be 1-based. If we ever emit // coordinates of 0, `llvm-cov` might misinterpret them. diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 42cbece32d8..589be81558c 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -50,6 +50,16 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { _ => {} } + let sig = tcx.fn_sig(def_id).instantiate_identity(); + for ty in sig.inputs().skip_binder().iter().chain(std::iter::once(&sig.output().skip_binder())) + { + // FIXME(f16_f128): in order to avoid crashes building `core`, always inline to skip + // codegen if the function is not used. + if ty == &tcx.types.f16 || ty == &tcx.types.f128 { + return true; + } + } + // Don't do any inference when incremental compilation is enabled; the additional inlining that // inference permits also creates more work for small edits. if tcx.sess.opts.incremental.is_some() { diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 8b309147c64..2d9eeddea2e 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -38,6 +38,12 @@ pub(crate) struct UnalignedPackedRef { pub span: Span, } +#[derive(Diagnostic)] +#[diag(mir_transform_unknown_pass_name)] +pub(crate) struct UnknownPassName<'a> { + pub(crate) name: &'a str, +} + pub(crate) struct AssertLint<P> { pub span: Span, pub assert_kind: AssertKind<P>, diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index d184328748f..d2d5facbbdc 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -9,7 +9,6 @@ #![feature(let_chains)] #![feature(map_try_insert)] #![feature(never_type)] -#![feature(round_char_boundary)] #![feature(try_blocks)] #![feature(yeet_expr)] #![warn(unreachable_pub)] @@ -41,77 +40,158 @@ use tracing::{debug, trace}; #[macro_use] mod pass_manager; +use std::sync::LazyLock; + use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel}; -mod abort_unwinding_calls; -mod add_call_guards; -mod add_moves_for_packed_drops; -mod add_retag; -mod add_subtyping_projections; -mod check_alignment; -mod check_const_item_mutation; -mod check_packed_ref; -mod check_undefined_transmutes; -// This pass is public to allow external drivers to perform MIR cleanup -pub mod cleanup_post_borrowck; -mod copy_prop; -mod coroutine; mod cost_checker; -mod coverage; mod cross_crate_inline; -mod ctfe_limit; -mod dataflow_const_prop; -mod dead_store_elimination; mod deduce_param_attrs; -mod deduplicate_blocks; -mod deref_separator; -mod dest_prop; -pub mod dump_mir; -mod early_otherwise_branch; -mod elaborate_box_derefs; -mod elaborate_drops; mod errors; mod ffi_unwind_calls; -mod function_item_references; -mod gvn; -// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden -// by custom rustc drivers, running all the steps by themselves. See #114628. -pub mod inline; -mod instsimplify; -mod jump_threading; -mod known_panics_lint; -mod large_enums; mod lint; -mod lower_intrinsics; -mod lower_slice_len; -mod match_branches; -mod mentioned_items; -mod multiple_return_terminators; -mod nrvo; -mod post_drop_elaboration; -mod prettify; -mod promote_consts; -mod ref_prop; -mod remove_noop_landing_pads; -mod remove_place_mention; -mod remove_storage_markers; -mod remove_uninit_drops; -mod remove_unneeded_drops; -mod remove_zsts; -mod required_consts; -mod reveal_all; -mod sanity_check; mod shim; mod ssa; -// This pass is public to allow external drivers to perform MIR cleanup -pub mod simplify; -mod simplify_branches; -mod simplify_comparison_integral; -mod single_use_consts; -mod sroa; -mod unreachable_enum_branching; -mod unreachable_prop; -mod validate; + +/// We import passes via this macro so that we can have a static list of pass names +/// (used to verify CLI arguments). It takes a list of modules, followed by the passes +/// declared within them. +/// ```ignore,macro-test +/// declare_passes! { +/// // Declare a single pass from the module `abort_unwinding_calls` +/// mod abort_unwinding_calls : AbortUnwindingCalls; +/// // When passes are grouped together as an enum, declare the two constituent passes +/// mod add_call_guards : AddCallGuards { +/// AllCallEdges, +/// CriticalCallEdges +/// }; +/// // Declares multiple pass groups, each containing their own constituent passes +/// mod simplify : SimplifyCfg { +/// Initial, +/// /* omitted */ +/// }, SimplifyLocals { +/// BeforeConstProp, +/// /* omitted */ +/// }; +/// } +/// ``` +macro_rules! declare_passes { + ( + $( + $vis:vis mod $mod_name:ident : $($pass_name:ident $( { $($ident:ident),* } )?),+ $(,)?; + )* + ) => { + $( + $vis mod $mod_name; + $( + // Make sure the type name is correct + #[allow(unused_imports)] + use $mod_name::$pass_name as _; + )+ + )* + + static PASS_NAMES: LazyLock<FxIndexSet<&str>> = LazyLock::new(|| [ + // Fake marker pass + "PreCodegen", + $( + $( + stringify!($pass_name), + $( + $( + $mod_name::$pass_name::$ident.name(), + )* + )? + )+ + )* + ].into_iter().collect()); + }; +} + +declare_passes! { + mod abort_unwinding_calls : AbortUnwindingCalls; + mod add_call_guards : AddCallGuards { AllCallEdges, CriticalCallEdges }; + mod add_moves_for_packed_drops : AddMovesForPackedDrops; + mod add_retag : AddRetag; + mod add_subtyping_projections : Subtyper; + mod check_alignment : CheckAlignment; + mod check_const_item_mutation : CheckConstItemMutation; + mod check_packed_ref : CheckPackedRef; + mod check_undefined_transmutes : CheckUndefinedTransmutes; + // This pass is public to allow external drivers to perform MIR cleanup + pub mod cleanup_post_borrowck : CleanupPostBorrowck; + + mod copy_prop : CopyProp; + mod coroutine : StateTransform; + mod coverage : InstrumentCoverage; + mod ctfe_limit : CtfeLimit; + mod dataflow_const_prop : DataflowConstProp; + mod dead_store_elimination : DeadStoreElimination { + Initial, + Final + }; + mod deduplicate_blocks : DeduplicateBlocks; + mod deref_separator : Derefer; + mod dest_prop : DestinationPropagation; + pub mod dump_mir : Marker; + mod early_otherwise_branch : EarlyOtherwiseBranch; + mod elaborate_box_derefs : ElaborateBoxDerefs; + mod elaborate_drops : ElaborateDrops; + mod function_item_references : FunctionItemReferences; + mod gvn : GVN; + // Made public so that `mir_drops_elaborated_and_const_checked` can be overridden + // by custom rustc drivers, running all the steps by themselves. See #114628. + pub mod inline : Inline; + mod instsimplify : InstSimplify { BeforeInline, AfterSimplifyCfg }; + mod jump_threading : JumpThreading; + mod known_panics_lint : KnownPanicsLint; + mod large_enums : EnumSizeOpt; + mod lower_intrinsics : LowerIntrinsics; + mod lower_slice_len : LowerSliceLenCalls; + mod match_branches : MatchBranchSimplification; + mod mentioned_items : MentionedItems; + mod multiple_return_terminators : MultipleReturnTerminators; + mod nrvo : RenameReturnPlace; + mod post_drop_elaboration : CheckLiveDrops; + mod prettify : ReorderBasicBlocks, ReorderLocals; + mod promote_consts : PromoteTemps; + mod ref_prop : ReferencePropagation; + mod remove_noop_landing_pads : RemoveNoopLandingPads; + mod remove_place_mention : RemovePlaceMention; + mod remove_storage_markers : RemoveStorageMarkers; + mod remove_uninit_drops : RemoveUninitDrops; + mod remove_unneeded_drops : RemoveUnneededDrops; + mod remove_zsts : RemoveZsts; + mod required_consts : RequiredConstsVisitor; + mod reveal_all : RevealAll; + mod sanity_check : SanityCheck; + // This pass is public to allow external drivers to perform MIR cleanup + pub mod simplify : + SimplifyCfg { + Initial, + PromoteConsts, + RemoveFalseEdges, + PostAnalysis, + PreOptimizations, + Final, + MakeShim, + AfterUnreachableEnumBranching + }, + SimplifyLocals { + BeforeConstProp, + AfterGVN, + Final + }; + mod simplify_branches : SimplifyConstCondition { + AfterConstProp, + Final + }; + mod simplify_comparison_integral : SimplifyComparisonIntegral; + mod single_use_consts : SingleUseConsts; + mod sroa : ScalarReplacementOfAggregates; + mod unreachable_enum_branching : UnreachableEnumBranching; + mod unreachable_prop : UnreachablePropagation; + mod validate : Validator; +} rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 29f8b4f6e4d..779e7f22101 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -1,24 +1,25 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use tracing::trace; use crate::lint::lint_body; -use crate::validate; +use crate::{errors, validate}; thread_local! { - static PASS_NAMES: RefCell<FxHashMap<&'static str, &'static str>> = { + /// Maps MIR pass names to a snake case form to match profiling naming style + static PASS_TO_PROFILER_NAMES: RefCell<FxHashMap<&'static str, &'static str>> = { RefCell::new(FxHashMap::default()) }; } /// Converts a MIR pass name into a snake case form to match the profiling naming style. fn to_profiler_name(type_name: &'static str) -> &'static str { - PASS_NAMES.with(|names| match names.borrow_mut().entry(type_name) { + PASS_TO_PROFILER_NAMES.with(|names| match names.borrow_mut().entry(type_name) { Entry::Occupied(e) => *e.get(), Entry::Vacant(e) => { let snake_case: String = type_name @@ -198,6 +199,31 @@ fn run_passes_inner<'tcx>( let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); + let named_passes: FxIndexSet<_> = + overridden_passes.iter().map(|(name, _)| name.as_str()).collect(); + + for &name in named_passes.difference(&*crate::PASS_NAMES) { + tcx.dcx().emit_warn(errors::UnknownPassName { name }); + } + + // Verify that no passes are missing from the `declare_passes` invocation + #[cfg(debug_assertions)] + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] + { + let used_passes: FxIndexSet<_> = passes.iter().map(|p| p.name()).collect(); + + let undeclared = used_passes.difference(&*crate::PASS_NAMES).collect::<Vec<_>>(); + if let Some((name, rest)) = undeclared.split_first() { + let mut err = + tcx.dcx().struct_bug(format!("pass `{name}` is not declared in `PASS_NAMES`")); + for name in rest { + err.note(format!("pass `{name}` is also not declared in `PASS_NAMES`")); + } + err.emit(); + } + } + let prof_arg = tcx.sess.prof.enabled().then(|| format!("{:?}", body.source.def_id())); if !body.should_skip() { diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index 6da387bbebc..8528a2e68c0 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -1,12 +1,19 @@ monomorphize_abi_error_disabled_vector_type_call = - ABI error: this function call uses a vector type that requires the `{$required_feature}` target feature, which is not enabled in the caller + this function call uses a SIMD vector type that (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled in the caller .label = function called here .help = consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable="{$required_feature}")]`) monomorphize_abi_error_disabled_vector_type_def = - ABI error: this function definition uses a vector type that requires the `{$required_feature}` target feature, which is not enabled + this function definition uses a SIMD vector type that (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled .label = function defined here .help = consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable="{$required_feature}")]`) +monomorphize_abi_error_unsupported_vector_type_call = + this function call uses a SIMD vector type that is not currently supported with the chosen ABI + .label = function called here +monomorphize_abi_error_unsupported_vector_type_def = + this function definition uses a SIMD vector type that is not currently supported with the chosen ABI + .label = function defined here + monomorphize_couldnt_dump_mono_stats = unexpected error occurred while dumping monomorphization stats: {$error} diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 85151e5f093..429e31b2c88 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -205,13 +205,8 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. -mod abi_check; -mod move_check; - use std::path::PathBuf; -use move_check::MoveCheckState; -use rustc_abi::Size; use rustc_data_structures::sync::{LRef, MTLock, par_for_each_in}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir as hir; @@ -228,15 +223,15 @@ use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::print::{shrunk_instance_name, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, AssocKind, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, - TypeFoldable, TypeVisitableExt, VtblEntry, + self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, + TypeVisitableExt, VtblEntry, }; use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; use rustc_session::Limit; use rustc_session::config::EntryFnType; use rustc_span::source_map::{Spanned, dummy_spanned, respan}; -use rustc_span::symbol::{Ident, sym}; +use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, trace}; @@ -612,8 +607,6 @@ struct MirUsedCollector<'a, 'tcx> { /// Note that this contains *not-monomorphized* items! used_mentioned_items: &'a mut UnordSet<MentionedItem<'tcx>>, instance: Instance<'tcx>, - visiting_call_terminator: bool, - move_check: move_check::MoveCheckState, } impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { @@ -760,13 +753,12 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { }; match terminator.kind { - mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } - | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => { + mir::TerminatorKind::Call { ref func, .. } + | mir::TerminatorKind::TailCall { ref func, .. } => { 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.used_items) } mir::TerminatorKind::Drop { ref place, .. } => { @@ -826,14 +818,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { push_mono_lang_item(self, reason.lang_item()); } - self.visiting_call_terminator = matches!(terminator.kind, mir::TerminatorKind::Call { .. }); self.super_terminator(terminator, location); - self.visiting_call_terminator = false; - } - - fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) { - self.super_operand(operand, location); - self.check_operand_move_size(operand, location); } } @@ -1183,20 +1168,6 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt } } -fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> { - for impl_def_id in tcx.inherent_impls(def_id) { - 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); - } - } - 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. @@ -1208,7 +1179,8 @@ fn collect_items_of_instance<'tcx>( mentioned_items: &mut MonoItems<'tcx>, mode: CollectionMode, ) { - tcx.ensure().check_feature_dependent_abi(instance); + // This item is getting monomorphized, do mono-time checks. + tcx.ensure().check_mono_item(instance); let body = tcx.instance_mir(instance.def); // Naively, in "used" collection mode, all functions get added to *both* `used_items` and @@ -1228,8 +1200,6 @@ fn collect_items_of_instance<'tcx>( used_items, used_mentioned_items: &mut used_mentioned_items, instance, - visiting_call_terminator: false, - move_check: MoveCheckState::new(), }; if mode == CollectionMode::UsedItems { @@ -1626,5 +1596,4 @@ pub(crate) fn collect_crate_mono_items<'tcx>( pub(crate) fn provide(providers: &mut Providers) { providers.hooks.should_codegen_locally = should_codegen_locally; - abi_check::provide(providers); } diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 5048a8d5d99..02865cad302 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -110,3 +110,17 @@ pub(crate) struct AbiErrorDisabledVectorTypeCall<'a> { pub span: Span, pub required_feature: &'a str, } + +#[derive(LintDiagnostic)] +#[diag(monomorphize_abi_error_unsupported_vector_type_def)] +pub(crate) struct AbiErrorUnsupportedVectorTypeDef { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(monomorphize_abi_error_unsupported_vector_type_call)] +pub(crate) struct AbiErrorUnsupportedVectorTypeCall { + #[label] + pub span: Span, +} diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index e92e6978d0f..0cfc4371db5 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -16,6 +16,7 @@ use rustc_span::ErrorGuaranteed; mod collector; mod errors; +mod mono_checks; mod partitioning; mod polymorphize; mod util; @@ -47,4 +48,5 @@ fn custom_coerce_unsize_info<'tcx>( pub fn provide(providers: &mut Providers) { partitioning::provide(providers); polymorphize::provide(providers); + mono_checks::provide(providers); } diff --git a/compiler/rustc_monomorphize/src/collector/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index f1c57f1e997..d53595929e7 100644 --- a/compiler/rustc_monomorphize/src/collector/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -1,9 +1,7 @@ //! This module ensures that if a function's ABI requires a particular target feature, //! that target feature is enabled both on the callee and all callers. use rustc_hir::CRATE_HIR_ID; -use rustc_middle::mir::visit::Visitor as MirVisitor; -use rustc_middle::mir::{self, Location, traversal}; -use rustc_middle::query::Providers; +use rustc_middle::mir::{self, traversal}; use rustc_middle::ty::inherent::*; use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt}; use rustc_session::lint::builtin::ABI_UNSUPPORTED_VECTOR_TYPES; @@ -12,7 +10,10 @@ use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_target::abi::call::{FnAbi, PassMode}; use rustc_target::abi::{BackendRepr, RegKind}; -use crate::errors::{AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef}; +use crate::errors::{ + AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef, + AbiErrorUnsupportedVectorTypeCall, AbiErrorUnsupportedVectorTypeDef, +}; fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool { match mode { @@ -25,11 +26,15 @@ fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool { } } +/// Checks whether a certain function ABI is compatible with the target features currently enabled +/// for a certain function. +/// If not, `emit_err` is called, with `Some(feature)` if a certain feature should be enabled and +/// with `None` if no feature is known that would make the ABI compatible. fn do_check_abi<'tcx>( tcx: TyCtxt<'tcx>, abi: &FnAbi<'tcx, Ty<'tcx>>, target_feature_def: DefId, - mut emit_err: impl FnMut(&'static str), + mut emit_err: impl FnMut(Option<&'static str>), ) { let Some(feature_def) = tcx.sess.target.features_for_correct_vector_abi() else { return; @@ -42,7 +47,7 @@ fn do_check_abi<'tcx>( let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) { Some((_, feature)) => feature, None => { - emit_err("<no available feature for this size>"); + emit_err(None); continue; } }; @@ -50,7 +55,7 @@ fn do_check_abi<'tcx>( if !tcx.sess.unstable_target_features.contains(&feature_sym) && !codegen_attrs.target_features.iter().any(|x| x.name == feature_sym) { - emit_err(feature); + emit_err(Some(&feature)); } } } @@ -67,12 +72,21 @@ fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { }; do_check_abi(tcx, abi, instance.def_id(), |required_feature| { let span = tcx.def_span(instance.def_id()); - tcx.emit_node_span_lint( - ABI_UNSUPPORTED_VECTOR_TYPES, - CRATE_HIR_ID, - span, - AbiErrorDisabledVectorTypeDef { span, required_feature }, - ); + if let Some(required_feature) = required_feature { + tcx.emit_node_span_lint( + ABI_UNSUPPORTED_VECTOR_TYPES, + CRATE_HIR_ID, + span, + AbiErrorDisabledVectorTypeDef { span, required_feature }, + ); + } else { + tcx.emit_node_span_lint( + ABI_UNSUPPORTED_VECTOR_TYPES, + CRATE_HIR_ID, + span, + AbiErrorUnsupportedVectorTypeDef { span }, + ); + } }) } @@ -111,52 +125,49 @@ fn check_call_site_abi<'tcx>( return; }; do_check_abi(tcx, callee_abi, caller.def_id(), |required_feature| { - tcx.emit_node_span_lint( - ABI_UNSUPPORTED_VECTOR_TYPES, - CRATE_HIR_ID, - span, - AbiErrorDisabledVectorTypeCall { span, required_feature }, - ); + if let Some(required_feature) = required_feature { + tcx.emit_node_span_lint( + ABI_UNSUPPORTED_VECTOR_TYPES, + CRATE_HIR_ID, + span, + AbiErrorDisabledVectorTypeCall { span, required_feature }, + ); + } else { + tcx.emit_node_span_lint( + ABI_UNSUPPORTED_VECTOR_TYPES, + CRATE_HIR_ID, + span, + AbiErrorUnsupportedVectorTypeCall { span }, + ); + } }); } -struct MirCallesAbiCheck<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, - instance: Instance<'tcx>, -} - -impl<'a, 'tcx> MirVisitor<'tcx> for MirCallesAbiCheck<'a, 'tcx> { - fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, _: Location) { +fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) { + // Check all function call terminators. + for (bb, _data) in traversal::mono_reachable(body, tcx, instance) { + let terminator = body.basic_blocks[bb].terminator(); match terminator.kind { mir::TerminatorKind::Call { ref func, ref fn_span, .. } | mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => { - let callee_ty = func.ty(self.body, self.tcx); - let callee_ty = self.instance.instantiate_mir_and_normalize_erasing_regions( - self.tcx, + let callee_ty = func.ty(body, tcx); + let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions( + tcx, ty::ParamEnv::reveal_all(), ty::EarlyBinder::bind(callee_ty), ); - check_call_site_abi(self.tcx, callee_ty, *fn_span, self.body.source.instance); + check_call_site_abi(tcx, callee_ty, *fn_span, body.source.instance); } _ => {} } } } -fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { - let body = tcx.instance_mir(instance.def); - let mut visitor = MirCallesAbiCheck { tcx, body, instance }; - for (bb, data) in traversal::mono_reachable(body, tcx, instance) { - visitor.visit_basic_block_data(bb, data) - } -} - -fn check_feature_dependent_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { +pub(crate) fn check_feature_dependent_abi<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, +) { check_instance_abi(tcx, instance); - check_callees_abi(tcx, instance); -} - -pub(super) fn provide(providers: &mut Providers) { - *providers = Providers { check_feature_dependent_abi, ..*providers } + check_callees_abi(tcx, instance, body); } diff --git a/compiler/rustc_monomorphize/src/mono_checks/mod.rs b/compiler/rustc_monomorphize/src/mono_checks/mod.rs new file mode 100644 index 00000000000..1ecda824fb8 --- /dev/null +++ b/compiler/rustc_monomorphize/src/mono_checks/mod.rs @@ -0,0 +1,23 @@ +//! This implements a single query, `check_mono_fn`, that gets fired for each +//! monomorphization of all functions. This lets us implement monomorphization-time +//! checks in a way that is friendly to incremental compilation. + +use rustc_middle::query::Providers; +use rustc_middle::ty::{Instance, TyCtxt}; + +mod abi_check; +mod move_check; + +fn check_mono_item<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { + let body = tcx.instance_mir(instance.def); + abi_check::check_feature_dependent_abi(tcx, instance, body); + move_check::check_moves(tcx, instance, body); +} + +pub(super) fn provide(providers: &mut Providers) { + *providers = Providers { + check_mono_item, + skip_move_check_fns: move_check::skip_move_check_fns, + ..*providers + } +} diff --git a/compiler/rustc_monomorphize/src/collector/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index b86ef1e7373..7f04bdf46f1 100644 --- a/compiler/rustc_monomorphize/src/collector/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -1,38 +1,74 @@ +use rustc_abi::Size; +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::visit::Visitor as MirVisitor; +use rustc_middle::mir::{self, Location, traversal}; +use rustc_middle::ty::{self, AssocKind, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_session::Limit; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; -use tracing::debug; +use rustc_span::Span; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::{Ident, sym}; +use tracing::{debug, trace}; -use super::*; use crate::errors::LargeAssignmentsLint; -pub(super) struct MoveCheckState { +struct MoveCheckVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, /// Spans for move size lints already emitted. Helps avoid duplicate lints. move_size_spans: Vec<Span>, - /// Set of functions for which it is OK to move large data into. - skip_move_check_fns: Option<Vec<DefId>>, } -impl MoveCheckState { - pub(super) fn new() -> Self { - MoveCheckState { move_size_spans: vec![], skip_move_check_fns: None } +pub(crate) fn check_moves<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, +) { + let mut visitor = MoveCheckVisitor { tcx, instance, body, move_size_spans: vec![] }; + for (bb, data) in traversal::mono_reachable(body, tcx, instance) { + visitor.visit_basic_block_data(bb, data) } } -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; +impl<'tcx> MirVisitor<'tcx> for MoveCheckVisitor<'tcx> { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + match terminator.kind { + mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } + | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => { + let callee_ty = func.ty(self.body, self.tcx); + let callee_ty = self.monomorphize(callee_ty); + self.check_fn_args_move_size(callee_ty, args, *fn_span, location); + } + _ => {} } - // 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 { + // We deliberately do *not* visit the nested operands here, to avoid + // hitting `visit_operand` for function arguments. + } + + fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) { + self.check_operand_move_size(operand, location); + } +} + +impl<'tcx> MoveCheckVisitor<'tcx> { + fn monomorphize<T>(&self, value: T) -> T + where + T: TypeFoldable<TyCtxt<'tcx>>, + { + trace!("monomorphize: self.instance={:?}", self.instance); + self.instance.instantiate_mir_and_normalize_erasing_regions( + self.tcx, + ty::ParamEnv::reveal_all(), + ty::EarlyBinder::bind(value), + ) + } + + 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; } @@ -64,12 +100,7 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { 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) - { + if self.tcx.skip_move_check_fns(()).contains(&def_id) { return; } @@ -116,14 +147,12 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { span: Span, ) { let source_info = self.body.source_info(location); - debug!(?source_info); - for reported_span in &self.move_check.move_size_spans { + for reported_span in &self.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 @@ -137,11 +166,25 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { size: too_large_size.bytes(), limit: limit as u64, }); - self.move_check.move_size_spans.push(span); + self.move_size_spans.push(span); + } +} + +fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> { + for impl_def_id in tcx.inherent_impls(def_id) { + 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); + } } + None } -fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec<DefId> { +pub(crate) fn skip_move_check_fns(tcx: TyCtxt<'_>, _: ()) -> FxIndexSet<DefId> { let fns = [ (tcx.lang_items().owned_box(), "new"), (tcx.get_diagnostic_item(sym::Rc), "new"), @@ -151,5 +194,5 @@ fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec<DefId> { .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::<Vec<_>>() + .collect() } diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index f7fbfed5b8e..4ba54a2e0bf 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -29,10 +29,10 @@ pub trait SolverDelegate: Deref<Target = <Self as SolverDelegate>::Infcx> + Size // FIXME: Uplift the leak check into this crate. fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>; - fn try_const_eval_resolve( + fn evaluate_const( &self, param_env: <Self::Interner as Interner>::ParamEnv, - unevaluated: ty::UnevaluatedConst<Self::Interner>, + uv: ty::UnevaluatedConst<Self::Interner>, ) -> Option<<Self::Interner as Interner>::Const>; // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 8685896715e..979a3794748 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1001,12 +1001,12 @@ where // Try to evaluate a const, or return `None` if the const is too generic. // This doesn't mean the const isn't evaluatable, though, and should be treated // as an ambiguity rather than no-solution. - pub(super) fn try_const_eval_resolve( + pub(super) fn evaluate_const( &self, param_env: I::ParamEnv, - unevaluated: ty::UnevaluatedConst<I>, + uv: ty::UnevaluatedConst<I>, ) -> Option<I::Const> { - self.delegate.try_const_eval_resolve(param_env, unevaluated) + self.delegate.evaluate_const(param_env, uv) } pub(super) fn is_transmutable( diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 6793779b205..5c54656cc59 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -143,7 +143,7 @@ where ) -> QueryResult<I> { match ct.kind() { ty::ConstKind::Unevaluated(uv) => { - // We never return `NoSolution` here as `try_const_eval_resolve` emits an + // We never return `NoSolution` here as `evaluate_const` emits an // error itself when failing to evaluate, so emitting an additional fulfillment // error in that case is unnecessary noise. This may change in the future once // evaluation failures are allowed to impact selection, e.g. generic const @@ -151,7 +151,7 @@ where // FIXME(generic_const_exprs): Implement handling for generic // const expressions here. - if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv) { + if let Some(_normalized) = self.evaluate_const(param_env, uv) { 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_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index 5d5597429da..8ad0bf5cdf9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -14,7 +14,7 @@ where &mut self, goal: Goal<I, ty::NormalizesTo<I>>, ) -> QueryResult<I> { - if let Some(normalized_const) = self.try_const_eval_resolve( + if let Some(normalized_const) = self.evaluate_const( goal.param_env, ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), ) { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7f114013320..3546e5b0f04 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -683,7 +683,9 @@ impl<'a> Parser<'a> { }) { self.bump(); - self.dcx().emit_err(RemoveLet { span: lo }); + // Trim extra space after the `let` + let span = lo.with_hi(self.token.span.lo()); + self.dcx().emit_err(RemoveLet { span }); lo = self.token.span; } diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 92684505ab0..decaecd2682 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -12,7 +12,7 @@ use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, Toke use rustc_ast::{self as ast, PatKind, visit}; use rustc_ast_pretty::pprust::item_to_string; use rustc_data_structures::sync::Lrc; -use rustc_errors::emitter::HumanEmitter; +use rustc_errors::emitter::{HumanEmitter, OutputTheme}; use rustc_errors::{DiagCtxt, MultiSpan, PResult}; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FilePathMapping, SourceMap}; @@ -36,16 +36,17 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { )) } -fn create_test_handler() -> (DiagCtxt, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) { +fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) { let output = Arc::new(Mutex::new(Vec::new())); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle( vec![crate::DEFAULT_LOCALE_RESOURCE, crate::DEFAULT_LOCALE_RESOURCE], false, ); - let emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) + let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) .sm(Some(source_map.clone())) .diagnostic_width(Some(140)); + emitter = emitter.theme(theme); let dcx = DiagCtxt::new(Box::new(emitter)); (dcx, source_map, output) } @@ -69,7 +70,7 @@ fn with_expected_parse_error<T, F>(source_str: &str, expected_output: &str, f: F where F: for<'a> FnOnce(&mut Parser<'a>) -> PResult<'a, T>, { - let (handler, source_map, output) = create_test_handler(); + let (handler, source_map, output) = create_test_handler(OutputTheme::Ascii); let psess = ParseSess::with_dcx(handler, source_map); let mut p = string_to_parser(&psess, source_str.to_string()); let result = f(&mut p); @@ -189,34 +190,55 @@ impl<T: Write> Write for Shared<T> { } #[allow(rustc::untranslatable_diagnostic)] // no translation needed for tests -fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) { +fn test_harness( + file_text: &str, + span_labels: Vec<SpanLabel>, + notes: Vec<(Option<(Position, Position)>, &'static str)>, + expected_output_ascii: &str, + expected_output_unicode: &str, +) { create_default_session_globals_then(|| { - let (dcx, source_map, output) = create_test_handler(); - source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); - - let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end); - let mut msp = MultiSpan::from_span(primary_span); - for span_label in span_labels { - let span = make_span(&file_text, &span_label.start, &span_label.end); - msp.push_span_label(span, span_label.label); - println!("span: {:?} label: {:?}", span, span_label.label); - println!("text: {:?}", source_map.span_to_snippet(span)); - } + for (theme, expected_output) in [ + (OutputTheme::Ascii, expected_output_ascii), + (OutputTheme::Unicode, expected_output_unicode), + ] { + let (dcx, source_map, output) = create_test_handler(theme); + source_map + .new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); + + let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end); + let mut msp = MultiSpan::from_span(primary_span); + for span_label in &span_labels { + let span = make_span(&file_text, &span_label.start, &span_label.end); + msp.push_span_label(span, span_label.label); + println!("span: {:?} label: {:?}", span, span_label.label); + println!("text: {:?}", source_map.span_to_snippet(span)); + } - dcx.handle().span_err(msp, "foo"); + let mut err = dcx.handle().struct_span_err(msp, "foo"); + for (position, note) in ¬es { + if let Some((start, end)) = position { + let span = make_span(&file_text, &start, &end); + err.span_note(span, *note); + } else { + err.note(*note); + } + } + err.emit(); - assert!( - expected_output.chars().next() == Some('\n'), - "expected output should begin with newline" - ); - let expected_output = &expected_output[1..]; + assert!( + expected_output.chars().next() == Some('\n'), + "expected output should begin with newline" + ); + let expected_output = &expected_output[1..]; - let bytes = output.lock().unwrap(); - let actual_output = str::from_utf8(&bytes).unwrap(); - println!("expected output:\n------\n{}------", expected_output); - println!("actual output:\n------\n{}------", actual_output); + let bytes = output.lock().unwrap(); + let actual_output = str::from_utf8(&bytes).unwrap(); + println!("expected output:\n------\n{}------", expected_output); + println!("actual output:\n------\n{}------", actual_output); - assert!(expected_output == actual_output) + assert!(expected_output == actual_output) + } }) } @@ -253,6 +275,7 @@ fn foo() { end: Position { string: "}", count: 1 }, label: "test", }], + vec![], r#" error: foo --> test.rs:2:10 @@ -263,6 +286,16 @@ error: foo | |_^ test "#, + r#" +error: foo + ╭▸ test.rs:2:10 + │ +2 │ fn foo() { + │ ┏━━━━━━━━━━┛ +3 │ ┃ } + ╰╴┗━┛ test + +"#, ); } @@ -280,6 +313,7 @@ fn foo() { end: Position { string: "}", count: 1 }, label: "test", }], + vec![], r#" error: foo --> test.rs:2:10 @@ -291,6 +325,17 @@ error: foo | |___^ test "#, + r#" +error: foo + ╭▸ test.rs:2:10 + │ +2 │ fn foo() { + │ ┏━━━━━━━━━━┛ + ‡ ┃ +5 │ ┃ } + ╰╴┗━━━┛ test + +"#, ); } #[test] @@ -315,6 +360,7 @@ fn foo() { label: "`Y` is a good letter too", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -329,6 +375,20 @@ error: foo | `X` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ X0 Y0 + │ ┏━━━━┛ │ + │ ┃┌──────┘ +4 │ ┃│ X1 Y1 +5 │ ┃│ X2 Y2 + │ ┃└────╿──┘ `Y` is a good letter too + │ ┗━━━━━┥ + ╰╴ `X` is a good letter + +"#, ); } @@ -353,6 +413,7 @@ fn foo() { label: "`Y` is a good letter too", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -366,6 +427,72 @@ error: foo | `Y` is a good letter too "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ X0 Y0 + │ ┏━━━━┛ │ + │ ┃┌──────┘ +4 │ ┃│ Y1 X1 + │ ┗│━━━━│━━┛ `X` is a good letter + │ └────┤ + ╰╴ `Y` is a good letter too + +"#, + ); +} + +#[test] +fn multiline_and_normal_overlap() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![ + SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "X2", count: 1 }, + label: "`X` is a good letter", + }, + SpanLabel { + start: Position { string: "X0", count: 1 }, + end: Position { string: "Y0", count: 1 }, + label: "`Y` is a good letter too", + }, + ], + vec![], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ___---^- + | | | + | | `Y` is a good letter too +4 | | X1 Y1 Z1 +5 | | X2 Y2 Z2 + | |____^ `X` is a good letter + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ┏━━━┬──┛─ + │ ┃ │ + │ ┃ `Y` is a good letter too +4 │ ┃ X1 Y1 Z1 +5 │ ┃ X2 Y2 Z2 + ╰╴┗━━━━┛ `X` is a good letter + +"#, ); } @@ -392,6 +519,7 @@ fn foo() { label: "`Y` is a good letter too", }, ], + vec![], r#" error: foo --> test.rs:3:6 @@ -406,6 +534,789 @@ error: foo | |____- `Y` is a good letter too "#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ┏━━━━━━━┛ +4 │ ┃ X1 Y1 Z1 + │ ┃┌─────────┘ +5 │ ┃│ X2 Y2 Z2 + │ ┗│━━━━┛ `X` is a good letter +6 │ │ X3 Y3 Z3 + ╰╴ └────┘ `Y` is a good letter too + +"#, + ); +} + +#[test] +fn different_note_1() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![(None, "bar")], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | + = note: bar + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + │ + ╰ note: bar + +"#, + ); +} + +#[test] +fn different_note_2() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![(None, "bar"), (None, "qux")], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | + = note: bar + = note: qux + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + │ + ├ note: bar + ╰ note: qux + +"#, + ); +} + +#[test] +fn different_note_3() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![(None, "bar"), (None, "baz"), (None, "qux")], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | + = note: bar + = note: baz + = note: qux + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + │ + ├ note: bar + ├ note: baz + ╰ note: qux + +"#, + ); +} + +#[test] +fn different_note_spanned_1() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "bar", + )], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | +note: bar + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + ╰╴ +note: bar + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ + +"#, + ); +} + +#[test] +fn different_note_spanned_2() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "bar", + ), + ( + Some((Position { string: "X2", count: 1 }, Position { string: "Y2", count: 1 })), + "qux", + ), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | +note: bar + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ +note: qux + --> test.rs:5:3 + | +5 | X2 Y2 Z2 + | ^^^^^ + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + ╰╴ +note: bar + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ +note: qux + ╭▸ test.rs:5:3 + │ +5 │ X2 Y2 Z2 + ╰╴ ━━━━━ + +"#, + ); +} + +#[test] +fn different_note_spanned_3() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "bar", + ), + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "baz", + ), + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "qux", + ), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | +note: bar + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ +note: baz + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ +note: qux + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + ╰╴ +note: bar + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ +note: baz + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ +note: qux + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ + +"#, + ); +} + +#[test] +fn different_note_spanned_4() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "bar", + ), + (None, "qux"), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | +note: bar + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + = note: qux + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + ╰╴ +note: bar + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + │ ━━━━━━━━ + ╰ note: qux + +"#, + ); +} + +#[test] +fn different_note_spanned_5() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + (None, "bar"), + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "qux", + ), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | + = note: bar +note: qux + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + │ + ╰ note: bar +note: qux + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ + +"#, + ); +} + +#[test] +fn different_note_spanned_6() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + (None, "bar"), + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "baz", + ), + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "qux", + ), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | + = note: bar +note: baz + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ +note: qux + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + │ + ╰ note: bar +note: baz + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ +note: qux + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ + +"#, + ); +} + +#[test] +fn different_note_spanned_7() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z3", count: 1 })), + "bar", + ), + (None, "baz"), + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "qux", + ), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | +note: bar + --> test.rs:4:3 + | +4 | / X1 Y1 Z1 +5 | | X2 Y2 Z2 +6 | | X3 Y3 Z3 + | |__________^ + = note: baz +note: qux + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + ╰╴ +note: bar + ╭▸ test.rs:4:3 + │ +4 │ ┏ X1 Y1 Z1 +5 │ ┃ X2 Y2 Z2 +6 │ ┃ X3 Y3 Z3 + │ ┗━━━━━━━━━━┛ + ╰ note: baz +note: qux + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ + +"#, + ); +} + +#[test] +fn different_note_spanned_8() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "bar", + ), + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "baz", + ), + (None, "qux"), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | +note: bar + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ +note: baz + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + = note: qux + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + ╰╴ +note: bar + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ +note: baz + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + │ ━━━━━━━━ + ╰ note: qux + +"#, + ); +} + +#[test] +fn different_note_spanned_9() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + (None, "bar"), + (None, "baz"), + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "qux", + ), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | + = note: bar + = note: baz +note: qux + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + │ + ├ note: bar + ╰ note: baz +note: qux + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + ╰╴ ━━━━━━━━ + +"#, + ); +} + +#[test] +fn different_note_spanned_10() { + test_harness( + r#" +fn foo() { + X0 Y0 Z0 + X1 Y1 Z1 + X2 Y2 Z2 + X3 Y3 Z3 +} +"#, + vec![SpanLabel { + start: Position { string: "Y0", count: 1 }, + end: Position { string: "Z0", count: 1 }, + label: "`X` is a good letter", + }], + vec![ + ( + Some((Position { string: "X1", count: 1 }, Position { string: "Z1", count: 1 })), + "bar", + ), + (None, "baz"), + (None, "qux"), + ], + r#" +error: foo + --> test.rs:3:6 + | +3 | X0 Y0 Z0 + | ^^^^^ `X` is a good letter + | +note: bar + --> test.rs:4:3 + | +4 | X1 Y1 Z1 + | ^^^^^^^^ + = note: baz + = note: qux + +"#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ━━━━━ `X` is a good letter + ╰╴ +note: bar + ╭▸ test.rs:4:3 + │ +4 │ X1 Y1 Z1 + │ ━━━━━━━━ + ├ note: baz + ╰ note: qux + +"#, ); } @@ -436,6 +1347,7 @@ fn foo() { label: "`Z` label", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -452,6 +1364,22 @@ error: foo | `X` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ X0 Y0 Z0 + │ ┏━━━━━┛ │ │ + │ ┃┌───────┘ │ + │ ┃│┌─────────┘ +4 │ ┃││ X1 Y1 Z1 +5 │ ┃││ X2 Y2 Z2 + │ ┃│└────╿──│──┘ `Z` label + │ ┃└─────│──┤ + │ ┗━━━━━━┥ `Y` is a good letter too + ╰╴ `X` is a good letter + +"#, ); } @@ -482,6 +1410,7 @@ fn foo() { label: "`Z` label", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -496,6 +1425,20 @@ error: foo | `Z` label "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ ┏ X0 Y0 Z0 +4 │ ┃ X1 Y1 Z1 +5 │ ┃ X2 Y2 Z2 + │ ┃ ╿ + │ ┃ │ + │ ┃ `X` is a good letter + │ ┗━━━━`Y` is a good letter too + ╰╴ `Z` label + +"#, ); } @@ -527,6 +1470,7 @@ fn foo() { label: "`Z`", }, ], + vec![], r#" error: foo --> test.rs:3:6 @@ -545,6 +1489,24 @@ error: foo | |_______- `Z` "#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ┏━━━━━━━┛ +4 │ ┃ X1 Y1 Z1 + │ ┃┌────╿─┘ + │ ┗│━━━━┥ + │ │ `X` is a good letter +5 │ │ X2 Y2 Z2 + │ └───│──────┘ `Y` is a good letter too + │ ┌───┘ + │ │ +6 │ │ X3 Y3 Z3 + ╰╴ └───────┘ `Z` + +"#, ); } @@ -571,6 +1533,7 @@ fn foo() { label: "`Y` is a good letter too", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -584,6 +1547,19 @@ error: foo | |__________- `Y` is a good letter too "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ ┏ X0 Y0 Z0 +4 │ ┃ X1 Y1 Z1 + │ ┗━━━━┛ `X` is a good letter +5 │ X2 Y2 Z2 + │ ┌──────┘ +6 │ │ X3 Y3 Z3 + ╰╴└──────────┘ `Y` is a good letter too + +"#, ); } @@ -610,6 +1586,7 @@ fn foo() { label: "`Y` is a good letter too", }, ], + vec![], r#" error: foo --> test.rs:3:6 @@ -625,6 +1602,21 @@ error: foo | |__________- `Y` is a good letter too "#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ┏━━━━━━━┛ +4 │ ┃ X1 Y1 Z1 + │ ┃┌────╿────┘ + │ ┗│━━━━┥ + │ │ `X` is a good letter +5 │ │ X2 Y2 Z2 +6 │ │ X3 Y3 Z3 + ╰╴ └──────────┘ `Y` is a good letter too + +"#, ); } @@ -653,6 +1645,7 @@ fn foo() { label: "", }, ], + vec![], r#" error: foo --> test.rs:3:7 @@ -661,6 +1654,57 @@ error: foo | ----^^^^-^^-- `a` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:7 + │ +3 │ a { b { c } d } + ╰╴ ────━━━━─━━── `a` is a good letter + +"#, + ); +} + +#[test] +fn multiline_notes() { + test_harness( + r#" +fn foo() { + a { b { c } d } +} +"#, + vec![SpanLabel { + start: Position { string: "a", count: 1 }, + end: Position { string: "d", count: 1 }, + label: "`a` is a good letter", + }], + vec![(None, "foo\nbar"), (None, "foo\nbar")], + r#" +error: foo + --> test.rs:3:3 + | +3 | a { b { c } d } + | ^^^^^^^^^^^^^ `a` is a good letter + | + = note: foo + bar + = note: foo + bar + +"#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ a { b { c } d } + │ ━━━━━━━━━━━━━ `a` is a good letter + │ + ├ note: foo + │ bar + ╰ note: foo + bar + +"#, ); } @@ -684,6 +1728,7 @@ fn foo() { label: "", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -692,6 +1737,14 @@ error: foo | ^^^^-------^^ `a` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ a { b { c } d } + ╰╴ ━━━━───────━━ `a` is a good letter + +"#, ); } @@ -720,6 +1773,7 @@ fn foo() { label: "", }, ], + vec![], r#" error: foo --> test.rs:3:7 @@ -730,6 +1784,16 @@ error: foo | `b` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:7 + │ +3 │ a { b { c } d } + │ ────┯━━━─━━── + │ │ + ╰╴ `b` is a good letter + +"#, ); } @@ -753,6 +1817,7 @@ fn foo() { label: "`b` is a good letter", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -763,6 +1828,16 @@ error: foo | `b` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ a { b { c } d } + │ ━━━━┬──────━━ + │ │ + ╰╴ `b` is a good letter + +"#, ); } @@ -786,6 +1861,7 @@ fn foo() { label: "", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -796,6 +1872,16 @@ error: foo | `a` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ a bc d + │ ┯━━━──── + │ │ + ╰╴ `a` is a good letter + +"#, ); } @@ -819,6 +1905,7 @@ fn foo() { label: "", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -827,6 +1914,14 @@ error: foo | ^^^^-------^^ "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ a { b { c } d } + ╰╴ ━━━━───────━━ + +"#, ); } @@ -855,6 +1950,7 @@ fn foo() { label: "", }, ], + vec![], r#" error: foo --> test.rs:3:7 @@ -863,6 +1959,14 @@ error: foo | ----^^^^-^^-- "#, + r#" +error: foo + ╭▸ test.rs:3:7 + │ +3 │ a { b { c } d } + ╰╴ ────━━━━─━━── + +"#, ); } @@ -886,6 +1990,7 @@ fn foo() { label: "`b` is a good letter", }, ], + vec![], r#" error: foo --> test.rs:3:3 @@ -897,6 +2002,17 @@ error: foo | `a` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ a { b { c } d } + │ ┯━━━┬──────━━ + │ │ │ + │ │ `b` is a good letter + ╰╴ `a` is a good letter + +"#, ); } @@ -913,6 +2029,7 @@ fn foo() { end: Position { string: "d", count: 1 }, label: "`a` is a good letter", }], + vec![], r#" error: foo --> test.rs:3:3 @@ -921,6 +2038,14 @@ error: foo | ^^^^^^^^^^^^^ `a` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ a { b { c } d } + ╰╴ ━━━━━━━━━━━━━ `a` is a good letter + +"#, ); } @@ -937,6 +2062,7 @@ fn foo() { end: Position { string: "d", count: 1 }, label: "", }], + vec![], r#" error: foo --> test.rs:3:3 @@ -945,6 +2071,14 @@ error: foo | ^^^^^^^^^^^^^ "#, + r#" +error: foo + ╭▸ test.rs:3:3 + │ +3 │ a { b { c } d } + ╰╴ ━━━━━━━━━━━━━ + +"#, ); } @@ -981,6 +2115,7 @@ fn foo() { label: "`Y` is a good letter too", }, ], + vec![], r#" error: foo --> test.rs:3:6 @@ -1000,6 +2135,25 @@ error: foo | |__________- `Y` is a good letter too "#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ┏━━━━━━━┛ +4 │ ┃ X1 Y1 Z1 + │ ┃┌────╿────┘ + │ ┗│━━━━┥ + │ │ `X` is a good letter +5 │ │ 1 +6 │ │ 2 +7 │ │ 3 + ‡ │ +15 │ │ X2 Y2 Z2 +16 │ │ X3 Y3 Z3 + ╰╴ └──────────┘ `Y` is a good letter too + +"#, ); } @@ -1036,6 +2190,7 @@ fn foo() { label: "`Z` is a good letter too", }, ], + vec![], r#" error: foo --> test.rs:3:6 @@ -1058,6 +2213,28 @@ error: foo | |________^ `Y` is a good letter "#, + r#" +error: foo + ╭▸ test.rs:3:6 + │ +3 │ X0 Y0 Z0 + │ ┏━━━━━━━┛ +4 │ ┃ 1 +5 │ ┃ 2 +6 │ ┃ 3 +7 │ ┃ X1 Y1 Z1 + │ ┃┌─────────┘ +8 │ ┃│ 4 +9 │ ┃│ 5 +10 │ ┃│ 6 +11 │ ┃│ X2 Y2 Z2 + │ ┃└──────────┘ `Z` is a good letter too + ‡ ┃ +15 │ ┃ 10 +16 │ ┃ X3 Y3 Z3 + ╰╴┗━━━━━━━━┛ `Y` is a good letter + +"#, ); } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 6f0bcf5c3f0..08098ae7f6c 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -234,6 +234,9 @@ passes_doc_masked_only_extern_crate = passes_doc_rust_logo = the `#[doc(rust_logo)]` attribute is used for Rust branding +passes_doc_search_unbox_invalid = + `#[doc(search_unbox)]` should be used on generic structs and enums + passes_doc_test_literal = `#![doc(test(...)]` does not take a literal passes_doc_test_takes_list = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 64a527ef106..836511325f4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -16,8 +16,8 @@ use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, B use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, self, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, Item, ItemKind, - MethodKind, Safety, Target, TraitItem, + self as hir, self, AssocItemKind, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, + Item, ItemKind, MethodKind, Safety, Target, TraitItem, }; use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; @@ -937,6 +937,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_doc_search_unbox(&self, meta: &MetaItemInner, hir_id: HirId) { + let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else { + self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() }); + return; + }; + match item.kind { + ItemKind::Enum(_, generics) | ItemKind::Struct(_, generics) + if generics.params.len() != 0 => {} + ItemKind::Trait(_, _, generics, _, items) + if generics.params.len() != 0 + || items.iter().any(|item| matches!(item.kind, AssocItemKind::Type)) => {} + _ => { + self.dcx().emit_err(errors::DocSearchUnboxInvalid { span: meta.span() }); + } + } + } + /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. /// /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or @@ -1149,6 +1166,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + sym::search_unbox => { + if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") { + self.check_doc_search_unbox(meta, hir_id); + } + } + sym::test => { if self.check_attr_crate_level(attr, meta, hir_id) { self.check_test_attr(meta, hir_id); diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 70c92f0144c..2d1734c0314 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -245,6 +245,13 @@ pub(crate) struct DocKeywordOnlyImpl { } #[derive(Diagnostic)] +#[diag(passes_doc_search_unbox_invalid)] +pub(crate) struct DocSearchUnboxInvalid { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(passes_doc_inline_conflict)] #[help] pub(crate) struct DocKeywordConflict { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index cd47c8ece60..4a793f1875e 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -16,7 +16,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Constness, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; +use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; @@ -149,6 +149,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let Some(stab) = self.parent_stab { if inherit_deprecation.yes() && stab.is_unstable() { self.index.stab_map.insert(def_id, stab); + if fn_sig.is_some_and(|s| s.header.is_const()) { + let const_stab = + attr::unmarked_crate_const_stab(self.tcx.sess, attrs, stab); + self.index.const_stab_map.insert(def_id, const_stab); + } } } @@ -161,68 +166,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { return; } + // # Regular and body stability + let stab = attr::find_stability(self.tcx.sess, attrs, item_sp); - let const_stab = attr::find_const_stability( - self.tcx.sess, - attrs, - item_sp, - fn_sig.is_some_and(|s| s.header.is_const()), - ); let body_stab = attr::find_body_stability(self.tcx.sess, attrs); - // If the current node is a function with const stability attributes (directly given or - // implied), check if the function/method is const or the parent impl block is const. - if let Some(fn_sig) = fn_sig - && !fn_sig.header.is_const() - && const_stab.is_some() - { - self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span }); - } - - // If this is marked const *stable*, it must also be regular-stable. - if let Some((const_stab, const_span)) = const_stab - && let Some(fn_sig) = fn_sig - && const_stab.is_const_stable() - && !stab.is_some_and(|(s, _)| s.is_stable()) - { - self.tcx - .dcx() - .emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span }); - } - - // Stable *language* features shouldn't be used as unstable library features. - // (Not doing this for stable library features is checked by tidy.) - if let Some(( - ConstStability { level: Unstable { .. }, feature: Some(feature), .. }, - const_span, - )) = const_stab - { - if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { - self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature { - span: const_span, - item_sp, - }); - } - } - - let const_stab = const_stab.map(|(const_stab, _span)| { - self.index.const_stab_map.insert(def_id, const_stab); - const_stab - }); - - // `impl const Trait for Type` items forward their const stability to their - // immediate children. - // FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`? - // Currently, once that is set, we do not inherit anything from the parent any more. - if const_stab.is_none() { - debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); - if let Some(parent) = self.parent_const_stab { - if parent.is_const_unstable() { - self.index.const_stab_map.insert(def_id, parent); - } - } - } - if let Some((depr, span)) = &depr && depr.is_since_rustc_version() && stab.is_none() @@ -289,15 +237,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.index.implications.insert(implied_by, feature); } - if let Some(ConstStability { - level: Unstable { implied_by: Some(implied_by), .. }, - feature, - .. - }) = const_stab - { - self.index.implications.insert(implied_by, feature.unwrap()); - } - self.index.stab_map.insert(def_id, stab); stab }); @@ -311,6 +250,91 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + let final_stab = self.index.stab_map.get(&def_id); + + // # Const stability + + let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item_sp); + + // If the current node is a function with const stability attributes (directly given or + // implied), check if the function/method is const. + if let Some(fn_sig) = fn_sig + && !fn_sig.header.is_const() + && const_stab.is_some() + { + self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span }); + } + + // If this is marked const *stable*, it must also be regular-stable. + if let Some((const_stab, const_span)) = const_stab + && let Some(fn_sig) = fn_sig + && const_stab.is_const_stable() + && !stab.is_some_and(|s| s.is_stable()) + { + self.tcx + .dcx() + .emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span }); + } + + // Stable *language* features shouldn't be used as unstable library features. + // (Not doing this for stable library features is checked by tidy.) + if let Some((ConstStability { level: Unstable { .. }, feature, .. }, const_span)) = + const_stab + { + if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { + self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature { + span: const_span, + item_sp, + }); + } + } + + // After checking the immediate attributes, get rid of the span and compute implied + // const stability: inherit feature gate from regular stability. + let mut const_stab = const_stab.map(|(stab, _span)| stab); + + // If this is a const fn but not annotated with stability markers, see if we can inherit regular stability. + if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() && + // We only ever inherit unstable features. + let Some(inherit_regular_stab) = + final_stab.filter(|s| s.is_unstable()) + { + const_stab = Some(ConstStability { + // We subject these implicitly-const functions to recursive const stability. + const_stable_indirect: true, + promotable: false, + level: inherit_regular_stab.level, + feature: inherit_regular_stab.feature, + }); + } + + // Now that everything is computed, insert it into the table. + const_stab.inspect(|const_stab| { + self.index.const_stab_map.insert(def_id, *const_stab); + }); + + if let Some(ConstStability { + level: Unstable { implied_by: Some(implied_by), .. }, + feature, + .. + }) = const_stab + { + self.index.implications.insert(implied_by, feature); + } + + // `impl const Trait for Type` items forward their const stability to their + // immediate children. + // FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`? + // Currently, once that is set, we do not inherit anything from the parent any more. + if const_stab.is_none() { + debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); + if let Some(parent) = self.parent_const_stab { + if parent.is_const_unstable() { + self.index.const_stab_map.insert(def_id, parent); + } + } + } + self.recurse_with_stability_attrs( depr.map(|(d, _)| DeprecationEntry::local(d, def_id)), stab, @@ -565,13 +589,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { } } - fn check_missing_or_wrong_const_stability(&self, def_id: LocalDefId, span: Span) { - // The visitor runs for "unstable-if-unmarked" crates, but we don't yet support - // that on the const side. - if !self.tcx.features().staged_api() { - return; - } - + fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) { // if the const impl is derived using the `derive_const` attribute, // then it would be "stable" at least for the impl. // We gate usages of it using `feature(const_trait_impl)` anyways @@ -582,12 +600,12 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { let is_const = self.tcx.is_const_fn(def_id.to_def_id()) || self.tcx.is_const_trait_impl(def_id.to_def_id()); - let is_stable = - self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable()); - let missing_const_stability_attribute = - self.tcx.lookup_const_stability(def_id).is_none_or(|s| s.feature.is_none()); - if is_const && is_stable && missing_const_stability_attribute { + // Reachable const fn must have a stability attribute. + if is_const + && self.effective_visibilities.is_reachable(def_id) + && self.tcx.lookup_const_stability(def_id).is_none() + { let descr = self.tcx.def_descr(def_id.to_def_id()); self.tcx.dcx().emit_err(errors::MissingConstStabAttr { span, descr }); } @@ -615,7 +633,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { } // Ensure stable `const fn` have a const stability attribute. - self.check_missing_or_wrong_const_stability(i.owner_id.def_id, i.span); + self.check_missing_const_stability(i.owner_id.def_id, i.span); intravisit::walk_item(self, i) } @@ -629,7 +647,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { let impl_def_id = self.tcx.hir().get_parent_item(ii.hir_id()); if self.tcx.impl_trait_ref(impl_def_id).is_none() { self.check_missing_stability(ii.owner_id.def_id, ii.span); - self.check_missing_or_wrong_const_stability(ii.owner_id.def_id, ii.span); + self.check_missing_const_stability(ii.owner_id.def_id, ii.span); } intravisit::walk_impl_item(self, ii); } @@ -760,23 +778,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl(hir::Impl { - constness, - of_trait: Some(ref t), - self_ty, - items, - .. - }) => { + hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir().attrs(item.hir_id()); let stab = attr::find_stability(self.tcx.sess, attrs, item.span); - let const_stab = attr::find_const_stability( - self.tcx.sess, - attrs, - item.span, - matches!(constness, Constness::Const), - ); + let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 9ea5023064c..7cc6ba24450 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -453,7 +453,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { let fields: Vec<_>; match &pat.kind { PatKind::AscribeUserType { subpattern, .. } - | PatKind::InlineConstant { subpattern, .. } => return self.lower_pat(subpattern), + | PatKind::ExpandedConstant { subpattern, .. } => return self.lower_pat(subpattern), PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1a5c29afdc9..c845d60ac07 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -782,21 +782,11 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { impl ReachEverythingInTheInterfaceVisitor<'_, '_> { fn generics(&mut self) -> &mut Self { for param in &self.ev.tcx.generics_of(self.item_def_id).own_params { - match param.kind { - GenericParamDefKind::Lifetime => {} - GenericParamDefKind::Type { has_default, .. } => { - if has_default { - self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity()); - } - } - GenericParamDefKind::Const { has_default, .. } => { - self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity()); - if has_default { - self.visit( - self.ev.tcx.const_param_default(param.def_id).instantiate_identity(), - ); - } - } + if let GenericParamDefKind::Const { .. } = param.kind { + self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity()); + } + if let Some(default) = param.default_value(self.ev.tcx) { + self.visit(default.instantiate_identity()); } } self diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 2bb1be22b98..6e8fd32610b 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -19,8 +19,3 @@ rustc_span = { path = "../rustc_span" } thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end - -[features] -# tidy-alphabetical-start -rustc_use_parallel_compiler = ["rustc_query_system/rustc_use_parallel_compiler"] -# tidy-alphabetical-end diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 2f42fa47728..96b210accdb 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start parking_lot = "0.12" -rustc-rayon-core = { version = "0.5.0", optional = true } +rustc-rayon-core = { version = "0.5.0" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -23,8 +23,3 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end - -[features] -# tidy-alphabetical-start -rustc_use_parallel_compiler = ["dep:rustc-rayon-core"] -# 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 e7ed8288499..9b26a5bbcc6 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -837,12 +837,6 @@ impl<D: Deps> DepGraphData<D> { ) -> Option<DepNodeIndex> { let frame = MarkFrame { index: prev_dep_node_index, parent: frame }; - #[cfg(not(parallel_compiler))] - { - debug_assert!(!self.dep_node_exists(dep_node)); - debug_assert!(self.colors.get(prev_dep_node_index).is_none()); - } - // We never try to mark eval_always nodes as green debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); @@ -871,13 +865,6 @@ impl<D: Deps> DepGraphData<D> { // Maybe store a list on disk and encode this fact in the DepNodeState let side_effects = qcx.load_side_effects(prev_dep_node_index); - #[cfg(not(parallel_compiler))] - debug_assert!( - self.colors.get(prev_dep_node_index).is_none(), - "DepGraph::try_mark_previous_green() - Duplicate DepNodeColor \ - insertion for {dep_node:?}" - ); - if side_effects.maybe_any() { qcx.dep_context().dep_graph().with_query_deserialization(|| { self.emit_side_effects(qcx, dep_node_index, side_effects) diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 5af41b9e687..2a7d759ab35 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -1,21 +1,16 @@ use std::hash::Hash; use std::io::Write; +use std::iter; use std::num::NonZero; +use std::sync::Arc; -use rustc_data_structures::fx::FxHashMap; +use parking_lot::{Condvar, Mutex}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::jobserver; use rustc_errors::{Diag, DiagCtxtHandle}; use rustc_hir::def::DefKind; use rustc_session::Session; -use rustc_span::Span; -#[cfg(parallel_compiler)] -use { - parking_lot::{Condvar, Mutex}, - rustc_data_structures::fx::FxHashSet, - rustc_data_structures::jobserver, - rustc_span::DUMMY_SP, - std::iter, - std::sync::Arc, -}; +use rustc_span::{DUMMY_SP, Span}; use crate::dep_graph::DepContext; use crate::error::CycleStack; @@ -41,17 +36,14 @@ impl QueryJobId { map.get(&self).unwrap().query.clone() } - #[cfg(parallel_compiler)] fn span(self, map: &QueryMap) -> Span { map.get(&self).unwrap().job.span } - #[cfg(parallel_compiler)] fn parent(self, map: &QueryMap) -> Option<QueryJobId> { map.get(&self).unwrap().job.parent } - #[cfg(parallel_compiler)] fn latch(self, map: &QueryMap) -> Option<&QueryLatch> { map.get(&self).unwrap().job.latch.as_ref() } @@ -75,7 +67,6 @@ pub struct QueryJob { pub parent: Option<QueryJobId>, /// The latch that is used to wait on this job. - #[cfg(parallel_compiler)] latch: Option<QueryLatch>, } @@ -83,16 +74,9 @@ impl QueryJob { /// Creates a new query job. #[inline] pub fn new(id: QueryJobId, span: Span, parent: Option<QueryJobId>) -> Self { - QueryJob { - id, - span, - parent, - #[cfg(parallel_compiler)] - latch: None, - } + QueryJob { id, span, parent, latch: None } } - #[cfg(parallel_compiler)] pub(super) fn latch(&mut self) -> QueryLatch { if self.latch.is_none() { self.latch = Some(QueryLatch::new()); @@ -106,11 +90,8 @@ impl QueryJob { /// as there are no concurrent jobs which could be waiting on us #[inline] pub fn signal_complete(self) { - #[cfg(parallel_compiler)] - { - if let Some(latch) = self.latch { - latch.set(); - } + if let Some(latch) = self.latch { + latch.set(); } } } @@ -176,7 +157,6 @@ impl QueryJobId { } } -#[cfg(parallel_compiler)] #[derive(Debug)] struct QueryWaiter { query: Option<QueryJobId>, @@ -185,7 +165,6 @@ struct QueryWaiter { cycle: Mutex<Option<CycleError>>, } -#[cfg(parallel_compiler)] impl QueryWaiter { fn notify(&self, registry: &rayon_core::Registry) { rayon_core::mark_unblocked(registry); @@ -193,20 +172,17 @@ impl QueryWaiter { } } -#[cfg(parallel_compiler)] #[derive(Debug)] struct QueryLatchInfo { complete: bool, waiters: Vec<Arc<QueryWaiter>>, } -#[cfg(parallel_compiler)] #[derive(Clone, Debug)] pub(super) struct QueryLatch { info: Arc<Mutex<QueryLatchInfo>>, } -#[cfg(parallel_compiler)] impl QueryLatch { fn new() -> Self { QueryLatch { @@ -273,7 +249,6 @@ impl QueryLatch { } /// A resumable waiter of a query. The usize is the index into waiters in the query's latch -#[cfg(parallel_compiler)] type Waiter = (QueryJobId, usize); /// Visits all the non-resumable and resumable waiters of a query. @@ -285,7 +260,6 @@ type Waiter = (QueryJobId, usize); /// For visits of resumable waiters it returns Some(Some(Waiter)) which has the /// required information to resume the waiter. /// If all `visit` calls returns None, this function also returns None. -#[cfg(parallel_compiler)] fn visit_waiters<F>(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option<Option<Waiter>> where F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>, @@ -316,7 +290,6 @@ where /// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. /// If a cycle is detected, this initial value is replaced with the span causing /// the cycle. -#[cfg(parallel_compiler)] fn cycle_check( query_map: &QueryMap, query: QueryJobId, @@ -357,7 +330,6 @@ fn cycle_check( /// Finds out if there's a path to the compiler root (aka. code which isn't in a query) /// from `query` without going through any of the queries in `visited`. /// This is achieved with a depth first search. -#[cfg(parallel_compiler)] fn connected_to_root( query_map: &QueryMap, query: QueryJobId, @@ -380,7 +352,6 @@ fn connected_to_root( } // Deterministically pick an query from a list -#[cfg(parallel_compiler)] fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T where F: Fn(&T) -> (Span, QueryJobId), @@ -406,7 +377,6 @@ where /// the function return true. /// If a cycle was not found, the starting query is removed from `jobs` and /// the function returns false. -#[cfg(parallel_compiler)] fn remove_cycle( query_map: &QueryMap, jobs: &mut Vec<QueryJobId>, @@ -511,7 +481,6 @@ fn remove_cycle( /// uses a query latch and then resuming that waiter. /// There may be multiple cycles involved in a deadlock, so this searches /// all active queries for cycles before finally resuming all the waiters at once. -#[cfg(parallel_compiler)] pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) { let mut wakelist = Vec::new(); let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect(); diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 3e35fdb77b3..b81386f06ec 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -2,10 +2,9 @@ mod plumbing; pub use self::plumbing::*; mod job; -#[cfg(parallel_compiler)] -pub use self::job::break_query_cycles; pub use self::job::{ - QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, print_query_stack, report_cycle, + QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, break_query_cycles, print_query_stack, + report_cycle, }; mod caches; @@ -38,7 +37,6 @@ pub struct QueryStackFrame { pub dep_kind: DepKind, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. - #[cfg(parallel_compiler)] hash: Hash64, } @@ -51,18 +49,9 @@ impl QueryStackFrame { def_kind: Option<DefKind>, dep_kind: DepKind, ty_def_id: Option<DefId>, - _hash: impl FnOnce() -> Hash64, + hash: impl FnOnce() -> Hash64, ) -> Self { - Self { - description, - span, - def_id, - def_kind, - ty_def_id, - dep_kind, - #[cfg(parallel_compiler)] - hash: _hash(), - } + Self { description, span, def_id, def_kind, ty_def_id, dep_kind, hash: hash() } } // FIXME(eddyb) Get more valid `Span`s on queries. diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 17486be04dc..aac8ab87c64 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -13,7 +13,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sharded::Sharded; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lock; -#[cfg(parallel_compiler)] use rustc_data_structures::{outline, sync}; use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; @@ -25,9 +24,7 @@ use crate::HandleCycleError; use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams}; use crate::ich::StableHashingContext; use crate::query::caches::QueryCache; -#[cfg(parallel_compiler)] -use crate::query::job::QueryLatch; -use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, report_cycle}; +use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle}; use crate::query::{ QueryContext, QueryMap, QuerySideEffects, QueryStackFrame, SerializedDepNodeIndex, }; @@ -263,7 +260,6 @@ where } #[inline(always)] -#[cfg(parallel_compiler)] fn wait_for_query<Q, Qcx>( query: Q, qcx: Qcx, @@ -334,7 +330,7 @@ where // re-executing the query since `try_start` only checks that the query is not currently // executing, but another thread may have already completed the query and stores it result // in the query cache. - if cfg!(parallel_compiler) && qcx.dep_context().sess().threads() > 1 { + if qcx.dep_context().sess().threads() > 1 { if let Some((value, index)) = query.query_cache(qcx).lookup(&key) { qcx.dep_context().profiler().query_cache_hit(index.into()); return (value, Some(index)); @@ -359,7 +355,6 @@ where Entry::Occupied(mut entry) => { match entry.get_mut() { QueryResult::Started(job) => { - #[cfg(parallel_compiler)] if sync::is_dyn_thread_safe() { // Get the latch out let latch = job.latch(); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index a825458dc89..bf27b767a49 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -130,18 +130,16 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { &self, anon_const: &'a AnonConst, ) -> Option<(PendingAnonConstInfo, NodeId)> { - let (block_was_stripped, expr) = anon_const.value.maybe_unwrap_block(); - match expr { - Expr { kind: ExprKind::MacCall(..), id, .. } => Some(( + anon_const.value.optionally_braced_mac_call(false).map(|(block_was_stripped, id)| { + ( PendingAnonConstInfo { id: anon_const.id, span: anon_const.value.span, block_was_stripped, }, - *id, - )), - _ => None, - } + id, + ) + }) } /// Determines whether the expression `const_arg_sub_expr` is a simple macro call, sometimes @@ -161,18 +159,11 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { panic!("Checking expr is trivial macro call without having entered anon const: `{const_arg_sub_expr:?}`"), ); - let (block_was_stripped, expr) = if pending_anon.block_was_stripped { - (true, const_arg_sub_expr) - } else { - const_arg_sub_expr.maybe_unwrap_block() - }; - - match expr { - Expr { kind: ExprKind::MacCall(..), id, .. } => { - Some((PendingAnonConstInfo { block_was_stripped, ..pending_anon }, *id)) - } - _ => None, - } + const_arg_sub_expr.optionally_braced_mac_call(pending_anon.block_was_stripped).map( + |(block_was_stripped, id)| { + (PendingAnonConstInfo { block_was_stripped, ..pending_anon }, id) + }, + ) } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index fa2403db925..44721bd889a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1398,9 +1398,25 @@ pub enum OptionKind { } pub struct RustcOptGroup { - apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>, + /// The "primary" name for this option. Normally equal to `long_name`, + /// except for options that don't have a long name, in which case + /// `short_name` is used. + /// + /// This is needed when interacting with `getopts` in some situations, + /// because if an option has both forms, that library treats the long name + /// as primary and the short name as an alias. pub name: &'static str, stability: OptionStability, + kind: OptionKind, + + short_name: &'static str, + long_name: &'static str, + desc: &'static str, + value_hint: &'static str, + + /// If true, this option should not be printed by `rustc --help`, but + /// should still be printed by `rustc --help -v`. + pub is_verbose_help_only: bool, } impl RustcOptGroup { @@ -1409,7 +1425,13 @@ impl RustcOptGroup { } pub fn apply(&self, options: &mut getopts::Options) { - (self.apply)(options); + let &Self { short_name, long_name, desc, value_hint, .. } = self; + match self.kind { + OptionKind::Opt => options.optopt(short_name, long_name, desc, value_hint), + OptionKind::Multi => options.optmulti(short_name, long_name, desc, value_hint), + OptionKind::Flag => options.optflag(short_name, long_name, desc), + OptionKind::FlagMulti => options.optflagmulti(short_name, long_name, desc), + }; } } @@ -1419,31 +1441,22 @@ pub fn make_opt( short_name: &'static str, long_name: &'static str, desc: &'static str, - hint: &'static str, + value_hint: &'static str, ) -> RustcOptGroup { + // "Flag" options don't have a value, and therefore don't have a value hint. + match kind { + OptionKind::Opt | OptionKind::Multi => {} + OptionKind::Flag | OptionKind::FlagMulti => assert_eq!(value_hint, ""), + } RustcOptGroup { name: cmp::max_by_key(short_name, long_name, |s| s.len()), stability, - apply: match kind { - OptionKind::Opt => Box::new(move |opts: &mut getopts::Options| { - opts.optopt(short_name, long_name, desc, hint) - }), - OptionKind::Multi => Box::new(move |opts: &mut getopts::Options| { - opts.optmulti(short_name, long_name, desc, hint) - }), - OptionKind::Flag => { - assert_eq!(hint, ""); - Box::new(move |opts: &mut getopts::Options| { - opts.optflag(short_name, long_name, desc) - }) - } - OptionKind::FlagMulti => { - assert_eq!(hint, ""); - Box::new(move |opts: &mut getopts::Options| { - opts.optflagmulti(short_name, long_name, desc) - }) - } - }, + kind, + short_name, + long_name, + desc, + value_hint, + is_verbose_help_only: false, } } @@ -1454,16 +1467,15 @@ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE ) }); -/// 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. -pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> { +/// Returns all rustc command line options, including metadata for +/// each option, such as whether the option is stable. +pub fn rustc_optgroups() -> Vec<RustcOptGroup> { use OptionKind::{Flag, FlagMulti, Multi, Opt}; - use OptionStability::Stable; + use OptionStability::{Stable, Unstable}; use self::make_opt as opt; - vec![ + let mut options = vec![ opt(Stable, Flag, "h", "help", "Display this message", ""), opt( Stable, @@ -1550,21 +1562,11 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> { opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"), opt(Stable, Flag, "V", "version", "Print version info and exit", ""), opt(Stable, Flag, "v", "verbose", "Use verbose output", ""), - ] -} - -/// Returns all rustc command line options, including metadata for -/// each option, such as whether the option is part of the stable -/// long-term interface for rustc. -pub fn rustc_optgroups() -> Vec<RustcOptGroup> { - use OptionKind::{Multi, Opt}; - use OptionStability::{Stable, Unstable}; - - use self::make_opt as opt; + ]; - let mut opts = rustc_short_optgroups(); - // FIXME: none of these descriptions are actually used - opts.extend(vec![ + // Options in this list are hidden from `rustc --help` by default, but are + // shown by `rustc --help -v`. + let verbose_only = [ opt( Stable, Multi, @@ -1590,9 +1592,9 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "", "color", "Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output", + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output", "auto|always|never", ), opt( @@ -1612,8 +1614,13 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "FROM=TO", ), opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"), - ]); - opts + ]; + options.extend(verbose_only.into_iter().map(|mut opt| { + opt.is_verbose_help_only = true; + opt + })); + + options } pub fn get_cmd_lint_options( @@ -1721,6 +1728,9 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json for sub_option in option.split(',') { match sub_option { "diagnostic-short" => json_rendered = HumanReadableErrorType::Short, + "diagnostic-unicode" => { + json_rendered = HumanReadableErrorType::Unicode; + } "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, "artifacts" => json_artifact_notifications = true, "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud, @@ -1767,14 +1777,17 @@ pub fn parse_error_format( ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color } } Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short, color), + Some("human-unicode") => { + ErrorOutputType::HumanReadable(HumanReadableErrorType::Unicode, color) + } Some(arg) => { early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable( HumanReadableErrorType::Default, color, )); early_dcx.early_fatal(format!( - "argument for `--error-format` must be `human`, `json` or \ - `short` (instead was `{arg}`)" + "argument for `--error-format` must be `human`, `human-annotate-rs`, \ + `human-unicode`, `json`, `pretty-json` or `short` (instead was `{arg}`)" )) } } @@ -1827,18 +1840,21 @@ pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches fn check_error_format_stability( early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions, - error_format: ErrorOutputType, + format: ErrorOutputType, ) { - if !unstable_opts.unstable_options { - if let ErrorOutputType::Json { pretty: true, .. } = error_format { - early_dcx.early_fatal("`--error-format=pretty-json` is unstable"); - } - if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, _) = - error_format - { - early_dcx.early_fatal("`--error-format=human-annotate-rs` is unstable"); - } - } + if unstable_opts.unstable_options { + return; + } + let format = match format { + ErrorOutputType::Json { pretty: true, .. } => "pretty-json", + ErrorOutputType::HumanReadable(format, _) => match format { + HumanReadableErrorType::AnnotateSnippet => "human-annotate-rs", + HumanReadableErrorType::Unicode => "human-unicode", + _ => return, + }, + _ => return, + }; + early_dcx.early_fatal(format!("`--error-format={format}` is unstable")) } fn parse_output_types( diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 213a94ab880..4be013fd6fd 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -4,37 +4,44 @@ use std::path::{Path, PathBuf}; use std::{env, fs}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; +use rustc_target::spec::Target; use smallvec::{SmallVec, smallvec}; use crate::search_paths::{PathKind, SearchPath}; #[derive(Clone)] -pub struct FileSearch<'a> { - cli_search_paths: &'a [SearchPath], - tlib_path: &'a SearchPath, - kind: PathKind, +pub struct FileSearch { + cli_search_paths: Vec<SearchPath>, + tlib_path: SearchPath, } -impl<'a> FileSearch<'a> { - pub fn cli_search_paths(&self) -> impl Iterator<Item = &'a SearchPath> { - let kind = self.kind; +impl FileSearch { + pub fn cli_search_paths<'b>(&'b self, kind: PathKind) -> impl Iterator<Item = &'b SearchPath> { self.cli_search_paths.iter().filter(move |sp| sp.kind.matches(kind)) } - pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> { - let kind = self.kind; + pub fn search_paths<'b>(&'b self, kind: PathKind) -> impl Iterator<Item = &'b SearchPath> { self.cli_search_paths .iter() .filter(move |sp| sp.kind.matches(kind)) - .chain(std::iter::once(self.tlib_path)) + .chain(std::iter::once(&self.tlib_path)) } - pub fn new( - cli_search_paths: &'a [SearchPath], - tlib_path: &'a SearchPath, - kind: PathKind, - ) -> FileSearch<'a> { - FileSearch { cli_search_paths, tlib_path, kind } + pub fn new(cli_search_paths: &[SearchPath], tlib_path: &SearchPath, target: &Target) -> Self { + let this = FileSearch { + cli_search_paths: cli_search_paths.to_owned(), + tlib_path: tlib_path.clone(), + }; + this.refine(&["lib", &target.staticlib_prefix, &target.dll_prefix]) + } + // Produce a new file search from this search that has a smaller set of candidates. + fn refine(mut self, allowed_prefixes: &[&str]) -> FileSearch { + self.cli_search_paths + .iter_mut() + .for_each(|search_paths| search_paths.files.retain(allowed_prefixes)); + self.tlib_path.files.retain(allowed_prefixes); + + self } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 087ba0522eb..f485e8cace5 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -130,7 +130,7 @@ top_level_options!( pub struct Options { /// The crate config requested for the session, which may be combined /// with additional crate configurations during the compile process. - #[rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field")] + #[rustc_lint_opt_deny_field_access("use `TyCtxt::crate_types` instead of this field")] crate_types: Vec<CrateType> [TRACKED], optimize: OptLevel [TRACKED], /// Include the `debug_assertions` flag in dependency tracking, since it diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index c148b09c718..78473fccd2d 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -1,4 +1,5 @@ use std::path::{Path, PathBuf}; +use std::sync::Arc; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_target::spec::TargetTuple; @@ -10,9 +11,44 @@ use crate::filesearch::make_target_lib_path; pub struct SearchPath { pub kind: PathKind, pub dir: PathBuf, - pub files: Vec<SearchPathFile>, + pub files: FilesIndex, } +/// [FilesIndex] contains paths that can be efficiently looked up with (prefix, suffix) pairs. +#[derive(Clone, Debug)] +pub struct FilesIndex(Vec<(Arc<str>, SearchPathFile)>); + +impl FilesIndex { + /// Look up [SearchPathFile] by (prefix, suffix) pair. + pub fn query<'this, 'prefix, 'suffix>( + &'this self, + prefix: &'prefix str, + suffix: &'suffix str, + ) -> Option<impl Iterator<Item = (String, &'this SearchPathFile)> + use<'this, 'prefix, 'suffix>> + { + let start = self.0.partition_point(|(k, _)| **k < *prefix); + if start == self.0.len() { + return None; + } + let end = self.0[start..].partition_point(|(k, _)| k.starts_with(prefix)); + let prefixed_items = &self.0[start..][..end]; + + let ret = prefixed_items.into_iter().filter_map(move |(k, v)| { + k.ends_with(suffix).then(|| { + ( + String::from( + &v.file_name_str[prefix.len()..v.file_name_str.len() - suffix.len()], + ), + v, + ) + }) + }); + Some(ret) + } + pub fn retain(&mut self, prefixes: &[&str]) { + self.0.retain(|(k, _)| prefixes.iter().any(|prefix| k.starts_with(prefix))); + } +} /// The obvious implementation of `SearchPath::files` is a `Vec<PathBuf>`. But /// it is searched repeatedly by `find_library_crate`, and the searches involve /// checking the prefix and suffix of the filename of each `PathBuf`. This is @@ -26,8 +62,8 @@ pub struct SearchPath { /// UTF-8, and so a non-UTF-8 filename couldn't be one we're looking for.) #[derive(Clone, Debug)] pub struct SearchPathFile { - pub path: PathBuf, - pub file_name_str: String, + pub path: Arc<Path>, + pub file_name_str: Arc<str>, } #[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable, HashStable_Generic)] @@ -98,20 +134,25 @@ impl SearchPath { pub fn new(kind: PathKind, dir: PathBuf) -> Self { // Get the files within the directory. - let files = match std::fs::read_dir(&dir) { + let mut files = match std::fs::read_dir(&dir) { Ok(files) => files .filter_map(|e| { e.ok().and_then(|e| { - e.file_name().to_str().map(|s| SearchPathFile { - path: e.path(), - file_name_str: s.to_string(), + e.file_name().to_str().map(|s| { + let file_name_str: Arc<str> = s.into(); + (Arc::clone(&file_name_str), SearchPathFile { + path: e.path().into(), + file_name_str, + }) }) }) }) .collect::<Vec<_>>(), - Err(..) => vec![], - }; + Err(..) => Default::default(), + }; + files.sort_by(|(lhs, _), (rhs, _)| lhs.cmp(rhs)); + let files = FilesIndex(files); SearchPath { kind, dir, files } } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9d9434a7776..29fabdd1deb 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -16,7 +16,9 @@ use rustc_data_structures::sync::{ }; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::codes::*; -use rustc_errors::emitter::{DynEmitter, HumanEmitter, HumanReadableErrorType, stderr_destination}; +use rustc_errors::emitter::{ + DynEmitter, HumanEmitter, HumanReadableErrorType, OutputTheme, stderr_destination, +}; use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{ @@ -42,8 +44,9 @@ use crate::config::{ InstrumentCoverage, OptLevel, OutFileName, OutputType, RemapPathScopeComponents, SwitchWithOptPath, }; +use crate::filesearch::FileSearch; use crate::parse::{ParseSess, add_feature_diagnostics}; -use crate::search_paths::{PathKind, SearchPath}; +use crate::search_paths::SearchPath; use crate::{errors, filesearch, lint}; struct OptimizationFuel { @@ -216,6 +219,9 @@ pub struct Session { /// This is mainly useful for other tools that reads that debuginfo to figure out /// how to call the compiler with the same arguments. pub expanded_args: Vec<String>, + + target_filesearch: FileSearch, + host_filesearch: FileSearch, } #[derive(PartialEq, Eq, PartialOrd, Ord)] @@ -441,11 +447,11 @@ impl Session { format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.as_u64()) } - pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new(&self.opts.search_paths, &self.target_tlib_path, kind) + pub fn target_filesearch(&self) -> &filesearch::FileSearch { + &self.target_filesearch } - pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new(&self.opts.search_paths, &self.host_tlib_path, kind) + pub fn host_filesearch(&self) -> &filesearch::FileSearch { + &self.host_filesearch } /// Returns a list of directories where target-specific tool binaries are located. Some fallback @@ -966,6 +972,11 @@ fn default_emitter( .macro_backtrace(macro_backtrace) .track_diagnostics(track_diagnostics) .terminal_url(terminal_url) + .theme(if let HumanReadableErrorType::Unicode = kind { + OutputTheme::Unicode + } else { + OutputTheme::Ascii + }) .ignored_directories_in_source_blocks( sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(), ); @@ -1104,7 +1115,9 @@ pub fn build_session( }); let asm_arch = if target.allow_asm { InlineAsmArch::from_str(&target.arch).ok() } else { None }; - + let target_filesearch = + filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target); + let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host); let sess = Session { target, host, @@ -1131,6 +1144,8 @@ pub fn build_session( cfg_version, using_internal_features, expanded_args, + target_filesearch, + host_filesearch, }; validate_commandline_args_with_session_available(&sess); @@ -1470,6 +1485,11 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> { let short = kind.short(); Box::new( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) + .theme(if let HumanReadableErrorType::Unicode = kind { + OutputTheme::Unicode + } else { + OutputTheme::Ascii + }) .short_message(short), ) } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index c57d516fcae..a9ca5dbe509 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1300,7 +1300,6 @@ pub fn register_expn_id( let expn_id = ExpnId { krate, local_id }; HygieneData::with(|hygiene_data| { let _old_data = hygiene_data.foreign_expn_data.insert(expn_id, data); - debug_assert!(_old_data.is_none() || cfg!(parallel_compiler)); let _old_hash = hygiene_data.foreign_expn_hashes.insert(expn_id, hash); debug_assert!(_old_hash.is_none() || _old_hash == Some(hash)); let _old_id = hygiene_data.expn_hash_to_expn_id.insert(hash, expn_id); @@ -1423,18 +1422,7 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext ctxt_data = old.clone(); } - let dummy = std::mem::replace( - &mut hygiene_data.syntax_context_data[ctxt.as_u32() as usize], - ctxt_data, - ); - if cfg!(not(parallel_compiler)) { - // Make sure nothing weird happened while `decode_data` was running. - // We used `kw::Empty` for the dummy value and we expect nothing to be - // modifying the dummy entry. - // This does not hold for the parallel compiler as another thread may - // have inserted the fully decoded data. - assert_eq!(dummy.dollar_crate_name, kw::Empty); - } + hygiene_data.syntax_context_data[ctxt.as_u32() as usize] = ctxt_data; }); // Mark the context as completed diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 5b1be5bca05..8966cfc9171 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -521,14 +521,6 @@ impl SpanData { } } -// The interner is pointed to by a thread local value which is only set on the main thread -// with parallelization is disabled. So we don't allow `Span` to transfer between threads -// to avoid panics and other errors, even though it would be memory safe to do so. -#[cfg(not(parallel_compiler))] -impl !Send for Span {} -#[cfg(not(parallel_compiler))] -impl !Sync for Span {} - impl PartialOrd for Span { fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { PartialOrd::partial_cmp(&self.data(), &rhs.data()) @@ -567,12 +559,6 @@ impl Span { !self.is_dummy() && sm.is_span_accessible(self) } - /// Returns `true` if this span comes from any kind of macro, desugaring or inlining. - #[inline] - pub fn from_expansion(self) -> bool { - !self.ctxt().is_root() - } - /// Returns `true` if `span` originates in a derive-macro's expansion. pub fn in_derive_expansion(self) -> bool { matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _)) @@ -2223,6 +2209,10 @@ pub fn char_width(ch: char) -> usize { } } +pub fn str_width(s: &str) -> usize { + s.chars().map(char_width).sum() +} + /// Normalizes the source code and records the normalizations. fn normalize_src(src: &mut String) -> Vec<NormalizedPos> { let mut normalized_pos = vec![]; diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index f36bb38623e..e74a5c2d8fe 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -534,7 +534,7 @@ impl SourceMap { /// Extracts the source surrounding the given `Span` using the `extract_source` function. The /// extract function takes three arguments: a string slice containing the source, an index in /// the slice for the beginning of the span and an index in the slice for the end of the span. - fn span_to_source<F, T>(&self, sp: Span, extract_source: F) -> Result<T, SpanSnippetError> + pub fn span_to_source<F, T>(&self, sp: Span, extract_source: F) -> Result<T, SpanSnippetError> where F: Fn(&str, usize, usize) -> Result<T, SpanSnippetError>, { diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index db8170fa6ae..9d6c7d2a42a 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -303,6 +303,13 @@ impl Span { } } + /// Returns `true` if this span comes from any kind of macro, desugaring or inlining. + #[inline] + pub fn from_expansion(self) -> bool { + // If the span is fully inferred then ctxt > MAX_CTXT + self.inline_ctxt().map_or(true, |ctxt| !ctxt.is_root()) + } + /// Returns `true` if this is a dummy span with any hygienic context. #[inline] pub fn is_dummy(self) -> bool { @@ -370,9 +377,10 @@ impl Span { pub fn eq_ctxt(self, other: Span) -> bool { match (self.inline_ctxt(), other.inline_ctxt()) { (Ok(ctxt1), Ok(ctxt2)) => ctxt1 == ctxt2, - (Ok(ctxt), Err(index)) | (Err(index), Ok(ctxt)) => { - with_span_interner(|interner| ctxt == interner.spans[index].ctxt) - } + // If `inline_ctxt` returns `Ok` the context is <= MAX_CTXT. + // If it returns `Err` the span is fully interned and the context is > MAX_CTXT. + // As these do not overlap an `Ok` and `Err` result cannot have an equal context. + (Ok(_), Err(_)) | (Err(_), Ok(_)) => false, (Err(index1), Err(index2)) => with_span_interner(|interner| { interner.spans[index1].ctxt == interner.spans[index2].ctxt }), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c04c793ba46..42c6221dc57 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1568,6 +1568,8 @@ symbols! { readonly, realloc, reason, + receiver, + receiver_target, recursion_limit, reexport_test_harness_main, ref_pat_eat_one_layer_2024, @@ -1761,6 +1763,7 @@ symbols! { saturating_add, saturating_div, saturating_sub, + search_unbox, select_unpredictable, self_in_typedefs, self_struct_ctor, diff --git a/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs index c0bcc79b5b9..1515d8fe00a 100644 --- a/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs @@ -1,5 +1,5 @@ use crate::abi::Endian; -use crate::spec::{Target, TargetOptions, base}; +use crate::spec::{LinkSelfContainedDefault, Target, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_gnu::opts(); @@ -17,6 +17,13 @@ pub(crate) fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16".into(), arch: "m68k".into(), - options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, + options: TargetOptions { + endian: Endian::Big, + mcount: "_mcount".into(), + + // LLD currently does not have support for M68k + link_self_contained: LinkSelfContainedDefault::False, + ..base + }, } } diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index c4f9c742650..88536926b11 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -92,6 +92,11 @@ impl Stability { // // Stabilizing a target feature requires t-lang approval. +// If feature A "implies" feature B, then: +// - when A gets enabled (via `-Ctarget-feature` or `#[target_feature]`), we also enable B +// - when B gets disabled (via `-Ctarget-feature`), we also disable A +// +// Both of these are also applied transitively. type ImpliedFeatures = &'static [&'static str]; const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ @@ -463,13 +468,14 @@ const WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("bulk-memory", Stable, &[]), ("exception-handling", Unstable(sym::wasm_target_feature), &[]), ("extended-const", Stable, &[]), - ("multivalue", Unstable(sym::wasm_target_feature), &[]), + ("multivalue", Stable, &[]), ("mutable-globals", Stable, &[]), ("nontrapping-fptoint", Stable, &[]), - ("reference-types", Unstable(sym::wasm_target_feature), &[]), + ("reference-types", Stable, &[]), ("relaxed-simd", Stable, &["simd128"]), ("sign-ext", Stable, &[]), ("simd128", Stable, &[]), + ("tail-call", Stable, &[]), ("wide-arithmetic", Unstable(sym::wasm_target_feature), &[]), // tidy-alphabetical-end ]; @@ -580,9 +586,20 @@ pub fn all_rust_features() -> impl Iterator<Item = (&'static str, Stability)> { // certain size to have their "proper" ABI on each architecture. // Note that they must be kept sorted by vector size. const X86_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = - &[(128, "sse"), (256, "avx"), (512, "avx512f")]; + &[(128, "sse"), (256, "avx"), (512, "avx512f")]; // FIXME: might need changes for AVX10. const AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")]; +// We might want to add "helium" too. +const ARM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")]; + +const POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "altivec")]; +const WASM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "simd128")]; +const S390X_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vector")]; +const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = + &[/*(64, "zvl64b"), */ (128, "v")]; +// Always warn on SPARC, as the necessary target features cannot be enabled in Rust at the moment. +const SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(128, "vis")*/]; + impl super::spec::Target { pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { @@ -607,8 +624,15 @@ impl super::spec::Target { pub fn features_for_correct_vector_abi(&self) -> Option<&'static [(u64, &'static str)]> { match &*self.arch { "x86" | "x86_64" => Some(X86_FEATURES_FOR_CORRECT_VECTOR_ABI), - "aarch64" => Some(AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI), - // FIXME: add support for non-tier1 architectures + "aarch64" | "arm64ec" => Some(AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI), + "arm" => Some(ARM_FEATURES_FOR_CORRECT_VECTOR_ABI), + "powerpc" | "powerpc64" => Some(POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI), + "loongarch64" => Some(&[]), // on-stack ABI, so we complain about all by-val vectors + "riscv32" | "riscv64" => Some(RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI), + "wasm32" | "wasm64" => Some(WASM_FEATURES_FOR_CORRECT_VECTOR_ABI), + "s390x" => Some(S390X_FEATURES_FOR_CORRECT_VECTOR_ABI), + "sparc" | "sparc64" => Some(SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI), + // FIXME: add support for non-tier2 architectures _ => None, } } diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 3ddd23924b5..1ab89ecde7a 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -282,6 +282,8 @@ trait_selection_precise_capturing_new = add a `use<...>` bound to explicitly cap trait_selection_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate +trait_selection_precise_capturing_overcaptures = use the precise capturing `use<...>` syntax to make the captures explicit + trait_selection_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here... trait_selection_prlf_defined_without_sub = the lifetime defined here... trait_selection_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information) @@ -455,7 +457,9 @@ 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_warn_removing_apit_params = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable +trait_selection_warn_removing_apit_params_for_overcapture = you could use a `use<...>` bound to explicitly specify captures, but argument-position `impl Trait`s are not nameable + +trait_selection_warn_removing_apit_params_for_undercapture = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable trait_selection_where_copy_predicates = copy the `where` clause predicates from the trait diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 455e3ec751e..afac6fc6004 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,13 +1,14 @@ use std::path::PathBuf; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{Visitor, walk_ty}; use rustc_hir::{FnRetTy, GenericParamKind}; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -1792,6 +1793,138 @@ impl Subdiagnostic for AddPreciseCapturingAndParams { self.suggs, Applicability::MaybeIncorrect, ); - diag.span_note(self.apit_spans, fluent::trait_selection_warn_removing_apit_params); + diag.span_note( + self.apit_spans, + fluent::trait_selection_warn_removing_apit_params_for_undercapture, + ); + } +} + +/// Given a set of captured `DefId` for an RPIT (opaque_def_id) and a given +/// function (fn_def_id), try to suggest adding `+ use<...>` to capture just +/// the specified parameters. If one of those parameters is an APIT, then try +/// to suggest turning it into a regular type parameter. +pub fn impl_trait_overcapture_suggestion<'tcx>( + tcx: TyCtxt<'tcx>, + opaque_def_id: LocalDefId, + fn_def_id: LocalDefId, + captured_args: FxIndexSet<DefId>, +) -> Option<AddPreciseCapturingForOvercapture> { + let generics = tcx.generics_of(fn_def_id); + + let mut captured_lifetimes = FxIndexSet::default(); + let mut captured_non_lifetimes = FxIndexSet::default(); + let mut synthetics = vec![]; + + for arg in captured_args { + if tcx.def_kind(arg) == DefKind::LifetimeParam { + captured_lifetimes.insert(tcx.item_name(arg)); + } else { + let idx = generics.param_def_id_to_index(tcx, arg).expect("expected arg in scope"); + let param = generics.param_at(idx as usize, tcx); + if param.kind.is_synthetic() { + synthetics.push((tcx.def_span(arg), param.name)); + } else { + captured_non_lifetimes.insert(tcx.item_name(arg)); + } + } + } + + let mut next_fresh_param = || { + ["T", "U", "V", "W", "X", "Y", "A", "B", "C"] + .into_iter() + .map(Symbol::intern) + .chain((0..).map(|i| Symbol::intern(&format!("T{i}")))) + .find(|s| captured_non_lifetimes.insert(*s)) + .unwrap() + }; + + let mut suggs = vec![]; + let mut apit_spans = vec![]; + + if !synthetics.is_empty() { + let mut new_params = String::new(); + for (i, (span, name)) in synthetics.into_iter().enumerate() { + apit_spans.push(span); + + let fresh_param = next_fresh_param(); + + // Suggest renaming. + suggs.push((span, fresh_param.to_string())); + + // Super jank. Turn `impl Trait` into `T: Trait`. + // + // This currently involves stripping the `impl` from the name of + // the parameter, since APITs are always named after how they are + // rendered in the AST. This sucks! But to recreate the bound list + // from the APIT itself would be miserable, so we're stuck with + // this for now! + if i > 0 { + new_params += ", "; + } + let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start(); + new_params += fresh_param.as_str(); + new_params += ": "; + new_params += name_as_bounds; + } + + let Some(generics) = tcx.hir().get_generics(fn_def_id) else { + // This shouldn't happen, but don't ICE. + return None; + }; + + // Add generics or concatenate to the end of the list. + suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() { + (params_span, format!(", {new_params}")) + } else { + (generics.span, format!("<{new_params}>")) + }); + } + + let concatenated_bounds = captured_lifetimes + .into_iter() + .chain(captured_non_lifetimes) + .map(|sym| sym.to_string()) + .collect::<Vec<_>>() + .join(", "); + + suggs.push(( + tcx.def_span(opaque_def_id).shrink_to_hi(), + format!(" + use<{concatenated_bounds}>"), + )); + + Some(AddPreciseCapturingForOvercapture { suggs, apit_spans }) +} + +pub struct AddPreciseCapturingForOvercapture { + pub suggs: Vec<(Span, String)>, + pub apit_spans: Vec<Span>, +} + +impl Subdiagnostic for AddPreciseCapturingForOvercapture { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + let applicability = if self.apit_spans.is_empty() { + Applicability::MachineApplicable + } else { + // If there are APIT that are converted to regular parameters, + // then this may make the API turbofishable in ways that were + // not intended. + Applicability::MaybeIncorrect + }; + diag.multipart_suggestion_verbose( + fluent::trait_selection_precise_capturing_overcaptures, + self.suggs, + applicability, + ); + if !self.apit_spans.is_empty() { + diag.span_note( + self.apit_spans, + fluent::trait_selection_warn_removing_apit_params_for_overcapture, + ); + } } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index c53689b211d..2cc787c8bc5 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -15,7 +15,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_type_ir::TypingMode; use rustc_type_ir::solve::{Certainty, NoSolution}; -use crate::traits::specialization_graph; +use crate::traits::{EvaluateConstErr, specialization_graph}; #[repr(transparent)] pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>); @@ -77,20 +77,19 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution) } - fn try_const_eval_resolve( + fn evaluate_const( &self, param_env: ty::ParamEnv<'tcx>, - unevaluated: ty::UnevaluatedConst<'tcx>, + uv: ty::UnevaluatedConst<'tcx>, ) -> Option<ty::Const<'tcx>> { - use rustc_middle::mir::interpret::ErrorHandled; - match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) { - Ok(Ok(val)) => Some(ty::Const::new_value( - self.tcx, - val, - self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args), - )), - Ok(Err(_)) | Err(ErrorHandled::TooGeneric(_)) => None, - Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())), + let ct = ty::Const::new_unevaluated(self.tcx, uv); + + match crate::traits::try_evaluate_const(&self.0, ct, param_env) { + Ok(ct) => Some(ct), + Err(EvaluateConstErr::EvaluationFailure(e)) => Some(ty::Const::new_error(self.tcx, e)), + Err( + EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers, + ) => None, } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 52ba5621d31..103f7c76a86 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -7,7 +7,6 @@ use std::iter; 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::{Region, RegionVid}; use tracing::debug; use ty::TypingMode; @@ -761,23 +760,20 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: ty::Const<'tcx>| { if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { - match selcx.infcx.const_eval_resolve( + let ct = super::try_evaluate_const( + selcx.infcx, + c, obligation.param_env, - unevaluated, - obligation.cause.span, - ) { - Ok(Ok(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))), - Ok(Err(_)) => { - let tcx = self.tcx; - let reported = - tcx.dcx().emit_err(UnableToConstructConstantValue { - span: tcx.def_span(unevaluated.def), - unevaluated, - }); - Err(ErrorHandled::Reported(reported.into(), tcx.def_span(unevaluated.def))) - } - Err(err) => Err(err), + ); + + if let Err(EvaluateConstErr::InvalidConstParamTy(_)) = ct { + self.tcx.dcx().emit_err(UnableToConstructConstantValue { + span: self.tcx.def_span(unevaluated.def), + unevaluated, + }); } + + ct } else { Ok(c) } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 1be3c964454..15eb5d74cbf 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -12,13 +12,13 @@ use rustc_hir::def::DefKind; use rustc_infer::infer::InferCtxt; use rustc_middle::bug; -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::{DUMMY_SP, Span}; use tracing::{debug, instrument}; +use super::EvaluateConstErr; use crate::traits::ObligationCtxt; /// Check if a given constant can be evaluated. @@ -68,16 +68,18 @@ pub fn is_const_evaluatable<'tcx>( // here. 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, span); - match concrete { - Err(ErrorHandled::TooGeneric(_)) => { + ty::ConstKind::Unevaluated(_) => { + match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) { + Err(EvaluateConstErr::HasGenericsOrInfers) => { Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( span, "Missing value for constant, but no error reported?", ))) } - Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())), + Err( + EvaluateConstErr::EvaluationFailure(e) + | EvaluateConstErr::InvalidConstParamTy(e), + ) => Err(NotConstEvaluatable::Error(e.into())), Ok(_) => Ok(()), } } @@ -92,16 +94,7 @@ pub fn is_const_evaluatable<'tcx>( _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), }; - // FIXME: We should only try to evaluate a given constant here if it is fully concrete - // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. - // - // We previously did not check this, so we only emit a future compat warning if - // const evaluation succeeds and the given constant is still polymorphic for now - // and hopefully soon change this to an error. - // - // See #74595 for more details about this. - let concrete = infcx.const_eval_resolve(param_env, uv, span); - match concrete { + match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) { // If we're evaluating a generic foreign constant, under a nightly compiler while // the current crate does not enable `feature(generic_const_exprs)`, abort // compilation with a useful error. @@ -130,7 +123,7 @@ pub fn is_const_evaluatable<'tcx>( .emit() } - Err(ErrorHandled::TooGeneric(_)) => { + Err(EvaluateConstErr::HasGenericsOrInfers) => { let err = if uv.has_non_region_infer() { NotConstEvaluatable::MentionsInfer } else if uv.has_non_region_param() { @@ -145,7 +138,9 @@ pub fn is_const_evaluatable<'tcx>( Err(err) } - Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())), + Err( + EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e), + ) => Err(NotConstEvaluatable::Error(e.into())), Ok(_) => Ok(()), } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 21141f6e18f..2ec5f0d2249 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -10,7 +10,6 @@ use rustc_infer::traits::{ TraitEngine, }; use rustc_middle::bug; -use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt, TypingMode}; @@ -26,6 +25,7 @@ use super::{ }; use crate::error_reporting::InferCtxtErrorExt; use crate::infer::{InferCtxt, TyOrConstInferVar}; +use crate::traits::EvaluateConstErr; use crate::traits::normalize::normalize_with_depth_to; use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _}; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -664,23 +664,25 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let mut evaluate = |c: Const<'tcx>| { if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { - match self.selcx.infcx.try_const_eval_resolve( + match super::try_evaluate_const( + self.selcx.infcx, + c, obligation.param_env, - unevaluated, - obligation.cause.span, ) { Ok(val) => Ok(val), - Err(e) => { - match e { - ErrorHandled::TooGeneric(..) => { - stalled_on.extend(unevaluated.args.iter().filter_map( - TyOrConstInferVar::maybe_from_generic_arg, - )); - } - _ => {} - } - Err(e) + e @ Err(EvaluateConstErr::HasGenericsOrInfers) => { + stalled_on.extend( + unevaluated + .args + .iter() + .filter_map(TyOrConstInferVar::maybe_from_generic_arg), + ); + e } + e @ Err( + EvaluateConstErr::EvaluationFailure(_) + | EvaluateConstErr::InvalidConstParamTy(_), + ) => e, } } else { Ok(c) @@ -707,14 +709,20 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } } - (Err(ErrorHandled::Reported(reported, _)), _) - | (_, Err(ErrorHandled::Reported(reported, _))) => ProcessResult::Error( - FulfillmentErrorCode::Select(SelectionError::NotConstEvaluatable( - NotConstEvaluatable::Error(reported.into()), - )), - ), - (Err(ErrorHandled::TooGeneric(_)), _) - | (_, Err(ErrorHandled::TooGeneric(_))) => { + (Err(EvaluateConstErr::InvalidConstParamTy(e)), _) + | (_, Err(EvaluateConstErr::InvalidConstParamTy(e))) => { + ProcessResult::Error(FulfillmentErrorCode::Select( + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(e)), + )) + } + (Err(EvaluateConstErr::EvaluationFailure(e)), _) + | (_, Err(EvaluateConstErr::EvaluationFailure(e))) => { + ProcessResult::Error(FulfillmentErrorCode::Select( + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(e)), + )) + } + (Err(EvaluateConstErr::HasGenericsOrInfers), _) + | (_, Err(EvaluateConstErr::HasGenericsOrInfers)) => { if c1.has_non_region_infer() || c2.has_non_region_infer() { ProcessResult::Unchanged } else { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 436c0fabd29..fe90066b4e7 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -27,6 +27,7 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::DefKind; pub use rustc_infer::traits::*; use rustc_middle::query::Providers; use rustc_middle::span_bug; @@ -34,11 +35,11 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, TypingMode, - Upcast, + self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypingMode, Upcast, }; -use rustc_span::Span; use rustc_span::def_id::DefId; +use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; pub use self::coherence::{ @@ -366,11 +367,23 @@ pub fn normalize_param_env_or_error<'tcx>( if c.has_escaping_bound_vars() { return ty::Const::new_misc_error(self.0); } + // While it is pretty sus to be evaluating things with an empty param env, it // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. - c.normalize_internal(self.0, ty::ParamEnv::empty()) + if let ty::ConstKind::Unevaluated(uv) = c.kind() + && self.0.def_kind(uv.def) == DefKind::AnonConst + { + let infcx = self.0.infer_ctxt().build(TypingMode::non_body_analysis()); + let c = evaluate_const(&infcx, c, ty::ParamEnv::empty()); + // We should never wind up with any `infcx` local state when normalizing anon consts + // under min const generics. + assert!(!c.has_infer() && !c.has_placeholders()); + return c; + } + + c } } @@ -474,6 +487,210 @@ pub fn normalize_param_env_or_error<'tcx>( ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal()) } +#[derive(Debug)] +pub enum EvaluateConstErr { + /// The constant being evaluated was either a generic parameter or inference variable, *or*, + /// some unevaluated constant with either generic parameters or inference variables in its + /// generic arguments. + HasGenericsOrInfers, + /// The type this constant evalauted to is not valid for use in const generics. This should + /// always result in an error when checking the constant is correctly typed for the parameter + /// it is an argument to, so a bug is delayed when encountering this. + InvalidConstParamTy(ErrorGuaranteed), + /// CTFE failed to evaluate the constant in some unrecoverable way (e.g. encountered a `panic!`). + /// This is also used when the constant was already tainted by error. + EvaluationFailure(ErrorGuaranteed), +} + +// FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine +// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// normalization scheme +/// Evaluates a type system constant returning a `ConstKind::Error` in cases where CTFE failed and +/// returning the passed in constant if it was not fully concrete (i.e. depended on generic parameters +/// or inference variables) +/// +/// You should not call this function unless you are implementing normalization itself. Prefer to use +/// `normalize_erasing_regions` or the `normalize` functions on `ObligationCtxt`/`FnCtxt`/`InferCtxt`. +pub fn evaluate_const<'tcx>( + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> ty::Const<'tcx> { + match try_evaluate_const(infcx, ct, param_env) { + Ok(ct) => ct, + Err(EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e)) => { + ty::Const::new_error(infcx.tcx, e) + } + Err(EvaluateConstErr::HasGenericsOrInfers) => ct, + } +} + +// FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine +// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// normalization scheme +/// Evaluates a type system constant making sure to not allow constants that depend on generic parameters +/// or inference variables to succeed in evaluating. +/// +/// You should not call this function unless you are implementing normalization itself. Prefer to use +/// `normalize_erasing_regions` or the `normalize` functions on `ObligationCtxt`/`FnCtxt`/`InferCtxt`. +#[instrument(level = "debug", skip(infcx), ret)] +pub fn try_evaluate_const<'tcx>( + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Result<ty::Const<'tcx>, EvaluateConstErr> { + let tcx = infcx.tcx; + let ct = infcx.resolve_vars_if_possible(ct); + debug!(?ct); + + match ct.kind() { + ty::ConstKind::Value(..) => Ok(ct), + ty::ConstKind::Error(e) => Err(EvaluateConstErr::EvaluationFailure(e)), + ty::ConstKind::Param(_) + | ty::ConstKind::Infer(_) + | ty::ConstKind::Bound(_, _) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), + ty::ConstKind::Unevaluated(uv) => { + // Postpone evaluation of constants that depend on generic parameters or inference variables. + let (args, param_env) = if tcx.features().generic_const_exprs() + && uv.has_non_region_infer() + { + // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause + // inference variables and generic parameters to show up in `ty::Const` even though the anon const + // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. + match tcx.thir_abstract_const(uv.def) { + Ok(Some(ct)) => { + let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args)); + if let Err(e) = ct.error_reported() { + return Err(EvaluateConstErr::EvaluationFailure(e)); + } else if ct.has_non_region_infer() || ct.has_non_region_param() { + // If the anon const *does* actually use generic parameters or inference variables from + // the generic arguments provided for it, then we should *not* attempt to evaluate it. + return Err(EvaluateConstErr::HasGenericsOrInfers); + } else { + (replace_param_and_infer_args_with_placeholder(tcx, uv.args), param_env) + } + } + Err(_) | Ok(None) => { + let args = GenericArgs::identity_for_item(tcx, uv.def); + let param_env = tcx.param_env(uv.def); + (args, param_env) + } + } + } else if tcx.def_kind(uv.def) == DefKind::AnonConst && uv.has_non_region_infer() { + // FIXME: remove this when `const_evaluatable_unchecked` is a hard error. + // + // Diagnostics will sometimes replace the identity args of anon consts in + // array repeat expr counts with inference variables so we have to handle this + // even though it is not something we should ever actually encounter. + // + // Array repeat expr counts are allowed to syntactically use generic parameters + // but must not actually depend on them in order to evalaute succesfully. This means + // that it is actually fine to evalaute them in their own environment rather than with + // the actually provided generic arguments. + tcx.dcx().delayed_bug( + "Encountered anon const with inference variable args but no error reported", + ); + + let args = GenericArgs::identity_for_item(tcx, uv.def); + let param_env = tcx.param_env(uv.def); + (args, param_env) + } else { + // FIXME: This codepath is reachable under `associated_const_equality` and in the + // future will be reachable by `min_generic_const_args`. We should handle inference + // variables and generic parameters properly instead of doing nothing. + (uv.args, param_env) + }; + let uv = ty::UnevaluatedConst::new(uv.def, args); + + // It's not *technically* correct to be revealing opaque types here as we could still be + // before borrowchecking. However, CTFE itself uses `Reveal::All` unconditionally even during + // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). As a result we + // always use a revealed env when resolving the instance to evaluate. + // + // FIXME: `const_eval_resolve_for_typeck` should probably just set the env to `Reveal::All` + // instead of having this logic here + let env = tcx.erase_regions(param_env).with_reveal_all_normalized(tcx); + let erased_uv = tcx.erase_regions(uv); + + use rustc_middle::mir::interpret::ErrorHandled; + match tcx.const_eval_resolve_for_typeck(env, erased_uv, DUMMY_SP) { + Ok(Ok(val)) => Ok(ty::Const::new_value( + tcx, + val, + tcx.type_of(uv.def).instantiate(tcx, uv.args), + )), + Ok(Err(_)) => { + let e = tcx.dcx().delayed_bug( + "Type system constant with non valtree'able type evaluated but no error emitted", + ); + Err(EvaluateConstErr::InvalidConstParamTy(e)) + } + Err(ErrorHandled::Reported(info, _)) => { + Err(EvaluateConstErr::EvaluationFailure(info.into())) + } + Err(ErrorHandled::TooGeneric(_)) => Err(EvaluateConstErr::HasGenericsOrInfers), + } + } + } +} + +/// Replaces args that reference param or infer variables with suitable +/// placeholders. This function is meant to remove these param and infer +/// args when they're not actually needed to evaluate a constant. +fn replace_param_and_infer_args_with_placeholder<'tcx>( + tcx: TyCtxt<'tcx>, + args: GenericArgsRef<'tcx>, +) -> GenericArgsRef<'tcx> { + struct ReplaceParamAndInferWithPlaceholder<'tcx> { + tcx: TyCtxt<'tcx>, + idx: u32, + } + + impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceParamAndInferWithPlaceholder<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Infer(_) = t.kind() { + let idx = { + let idx = self.idx; + self.idx += 1; + idx + }; + Ty::new_placeholder(self.tcx, ty::PlaceholderType { + universe: ty::UniverseIndex::ROOT, + bound: ty::BoundTy { + var: ty::BoundVar::from_u32(idx), + kind: ty::BoundTyKind::Anon, + }, + }) + } else { + t.super_fold_with(self) + } + } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Infer(_) = c.kind() { + ty::Const::new_placeholder(self.tcx, ty::PlaceholderConst { + universe: ty::UniverseIndex::ROOT, + bound: ty::BoundVar::from_u32({ + let idx = self.idx; + self.idx += 1; + idx + }), + }) + } else { + c.super_fold_with(self) + } + } + } + + args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 }) +} + /// Normalizes the predicates and checks whether they hold in an empty environment. If this /// returns true, then either normalize encountered an error or one of the predicates did not /// hold. Used when creating vtables to check for unsatisfiable methods. This should not be diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 954dfe93387..5c38d162712 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -418,8 +418,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx self.selcx.infcx, &mut self.universes, constant, - |constant| constant.normalize_internal(tcx, self.param_env), + |constant| super::evaluate_const(self.selcx.infcx, constant, self.param_env), ) + .super_fold_with(self) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index a8d701a750d..5f89894fb82 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -342,7 +342,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> { self.infcx, &mut self.universes, constant, - |constant| constant.normalize_internal(self.infcx.tcx, self.param_env), + |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), ); debug!(?constant, ?self.param_env); constant.try_super_fold_with(self) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 041cfc21267..712856e6a8f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -403,7 +403,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut assume = predicate.trait_ref.args.const_at(2); // FIXME(min_generic_const_exprs): We should shallowly normalize this. if self.tcx().features().generic_const_exprs() { - assume = assume.normalize_internal(self.tcx(), obligation.param_env); + assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env) } let Some(assume) = rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, assume) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index cffb62ab559..5b4e895189b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -22,7 +22,6 @@ use rustc_infer::infer::relate::TypeRelation; use rustc_infer::traits::{PredicateObligations, TraitObligation}; use rustc_middle::bug; use rustc_middle::dep_graph::{DepNodeIndex, dep_kinds}; -use rustc_middle::mir::interpret::ErrorHandled; pub use rustc_middle::traits::select::*; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::TypeErrorToStringExt; @@ -50,7 +49,7 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt as _; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt}; -use crate::traits::{ProjectionCacheKey, Unimplemented, effects}; +use crate::traits::{EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects}; mod _match; mod candidate_assembly; @@ -931,11 +930,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { - match self.infcx.try_const_eval_resolve( + if let ty::ConstKind::Unevaluated(_) = c.kind() { + match crate::traits::try_evaluate_const( + self.infcx, + c, obligation.param_env, - unevaluated, - obligation.cause.span, ) { Ok(val) => Ok(val), Err(e) => Err(e), @@ -961,10 +960,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(_) => Ok(EvaluatedToErr), } } - (Err(ErrorHandled::Reported(..)), _) - | (_, Err(ErrorHandled::Reported(..))) => Ok(EvaluatedToErr), - (Err(ErrorHandled::TooGeneric(..)), _) - | (_, Err(ErrorHandled::TooGeneric(..))) => { + (Err(EvaluateConstErr::InvalidConstParamTy(..)), _) + | (_, Err(EvaluateConstErr::InvalidConstParamTy(..))) => Ok(EvaluatedToErr), + (Err(EvaluateConstErr::EvaluationFailure(..)), _) + | (_, Err(EvaluateConstErr::EvaluationFailure(..))) => Ok(EvaluatedToErr), + (Err(EvaluateConstErr::HasGenericsOrInfers), _) + | (_, Err(EvaluateConstErr::HasGenericsOrInfers)) => { if c1.has_non_region_infer() || c2.has_non_region_infer() { Ok(EvaluatedToAmbig) } else { diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index 23b5f62b5ca..f8e8f2176c1 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -83,7 +83,7 @@ impl<'tcx> At<'_, 'tcx> { Ok(self.infcx.resolve_vars_if_possible(new_infer_ct)) } else if self.infcx.tcx.features().generic_const_exprs() { - Ok(ct.normalize_internal(self.infcx.tcx, self.param_env)) + Ok(super::evaluate_const(&self.infcx, ct, self.param_env)) } else { Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx)) } |
