diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index d0bd364425a54..8e7b6f083acaa 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -1,9 +1,9 @@ //! The entry point of the NLL borrow checker. +use std::io; use std::path::PathBuf; use std::rc::Rc; use std::str::FromStr; -use std::{env, io}; use polonius_engine::{Algorithm, Output}; use rustc_index::IndexSlice; @@ -162,9 +162,8 @@ pub(crate) fn compute_regions<'a, 'tcx>( } if polonius_output { - let algorithm = - env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid")); - let algorithm = Algorithm::from_str(&algorithm).unwrap(); + let algorithm = infcx.tcx.env_var("POLONIUS_ALGORITHM").unwrap_or("Hybrid"); + let algorithm = Algorithm::from_str(algorithm).unwrap(); debug!("compute_regions: using polonius algorithm {:?}", algorithm); let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis"); Some(Box::new(Output::compute(polonius_facts, algorithm, false))) diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index ffbe54d620612..3a64c924cc224 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -564,6 +564,8 @@ where } } +impl_stable_traits_for_trivial_type!(::std::ffi::OsStr); + impl_stable_traits_for_trivial_type!(::std::path::Path); impl_stable_traits_for_trivial_type!(::std::path::PathBuf); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8be7ba7455e1e..2440f0639c8a6 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1,5 +1,5 @@ use std::any::Any; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; use std::sync::{Arc, LazyLock, OnceLock}; @@ -361,6 +361,31 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { ) } +fn env_var_os<'tcx>(tcx: TyCtxt<'tcx>, key: &'tcx OsStr) -> Option<&'tcx OsStr> { + let value = env::var_os(key); + + let value_tcx = value.as_ref().map(|value| { + let encoded_bytes = tcx.arena.alloc_slice(value.as_encoded_bytes()); + debug_assert_eq!(value.as_encoded_bytes(), encoded_bytes); + // SAFETY: The bytes came from `as_encoded_bytes`, and we assume that + // `alloc_slice` is implemented correctly, and passes the same bytes + // back (debug asserted above). + unsafe { OsStr::from_encoded_bytes_unchecked(encoded_bytes) } + }); + + // Also add the variable to Cargo's dependency tracking + // + // NOTE: This only works for passes run before `write_dep_info`. See that + // for extension points for configuring environment variables to be + // properly change-tracked. + tcx.sess.psess.env_depinfo.borrow_mut().insert(( + Symbol::intern(&key.to_string_lossy()), + value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(&value)), + )); + + value_tcx +} + // Returns all the paths that correspond to generated files. fn generated_output_paths( tcx: TyCtxt<'_>, @@ -725,6 +750,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { |tcx, _| tcx.arena.alloc_from_iter(tcx.resolutions(()).stripped_cfg_items.steal()); providers.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).1; providers.early_lint_checks = early_lint_checks; + providers.env_var_os = env_var_os; limits::provide(providers); proc_macro_decls::provide(providers); rustc_const_eval::provide(providers); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 333786f0ca38f..83d80938b4e30 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -18,7 +18,7 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; -use rustc_span::{Symbol, sym}; +use rustc_span::{SessionGlobals, Symbol, sym}; use rustc_target::spec::Target; use tracing::info; @@ -188,26 +188,11 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, // On deadlock, creates a new thread and forwards information in thread // locals to it. The new thread runs the deadlock handler. - // Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a - // `TyCtxt` TLS reference here. - let query_map = current_gcx2.access(|gcx| { - tls::enter_context(&tls::ImplicitCtxt::new(gcx), || { - tls::with(|tcx| { - match QueryCtxt::new(tcx).collect_active_jobs() { - Ok(query_map) => query_map, - Err(_) => { - // There was an unexpected error collecting all active jobs, which we need - // to find cycles to break. - // We want to avoid panicking in the deadlock handler, so we abort instead. - eprintln!("internal compiler error: failed to get query map in deadlock handler, aborting process"); - process::abort(); - } - } - }) - }) - }); - let query_map = FromDyn::from(query_map); + let current_gcx2 = current_gcx2.clone(); let registry = rayon_core::Registry::current(); + let session_globals = rustc_span::with_session_globals(|session_globals| { + session_globals as *const SessionGlobals as usize + }); thread::Builder::new() .name("rustc query cycle handler".to_string()) .spawn(move || { @@ -217,7 +202,24 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, // otherwise the compiler could just hang, process::abort(); }); - break_query_cycles(query_map.into_inner(), ®istry); + + // Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a + // `TyCtxt` TLS reference here. + current_gcx2.access(|gcx| { + tls::enter_context(&tls::ImplicitCtxt::new(gcx), || { + tls::with(|tcx| { + // Accessing session globals is sound as they outlive `GlobalCtxt`. + // They are needed to hash query keys containing spans or symbols. + let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { + // Ensure there was no errors collecting all active jobs. + // We need the complete map to ensure we find a cycle to break. + QueryCtxt::new(tcx).collect_active_jobs().ok().expect("failed to collect active queries in deadlock handler") + }); + break_query_cycles(query_map, ®istry); + }) + }) + }); + on_panic.disable(); }) .unwrap(); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index d51865810b9a8..0a3eb434d3f11 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -799,6 +799,9 @@ lint_tykind_kind = usage of `ty::TyKind::` lint_type_ir_inherent_usage = do not use `rustc_type_ir::inherent` unless you're inside of the trait solver .note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler +lint_type_ir_trait_usage = do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver + .note = the method or struct you're looking for is likely defined somewhere else downstream in the compiler + lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing .label = argument has type `{$arg_ty}` .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index b359ee790a50f..1d4be24ea9fa0 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -1,24 +1,21 @@ //! Some lints that are only useful in the compiler or crates that use compiler internals, such as //! Clippy. -use rustc_ast as ast; +use rustc_hir::HirId; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; -use rustc_hir::{ - AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, - PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Ty, TyKind, -}; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::{Span, sym}; use tracing::debug; +use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonGlobImportTypeIrInherent, QueryInstability, QueryUntracked, SpanUseEqCtxtDiag, SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrInherentUsage, - UntranslatableDiag, + TypeIrTraitUsage, UntranslatableDiag, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -37,9 +34,12 @@ declare_tool_lint! { declare_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]); impl LateLintPass<'_> for DefaultHashTypes { - fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) { + fn check_path(&mut self, cx: &LateContext<'_>, path: &hir::Path<'_>, hir_id: HirId) { let Res::Def(rustc_hir::def::DefKind::Struct, def_id) = path.res else { return }; - if matches!(cx.tcx.hir_node(hir_id), Node::Item(Item { kind: ItemKind::Use(..), .. })) { + if matches!( + cx.tcx.hir_node(hir_id), + hir::Node::Item(hir::Item { kind: hir::ItemKind::Use(..), .. }) + ) { // Don't lint imports, only actual usages. return; } @@ -60,10 +60,10 @@ impl LateLintPass<'_> for DefaultHashTypes { /// get the `DefId` and `GenericArgsRef` of the function. fn typeck_results_of_method_fn<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + expr: &hir::Expr<'_>, ) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> { match expr.kind { - ExprKind::MethodCall(segment, ..) + hir::ExprKind::MethodCall(segment, ..) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => { Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id))) @@ -102,7 +102,7 @@ declare_tool_lint! { declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]); impl LateLintPass<'_> for QueryStability { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return }; if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args) { @@ -164,21 +164,25 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx, AmbigArg>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { match &ty.kind { - TyKind::Path(QPath::Resolved(_, path)) => { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { let span = match cx.tcx.parent_hir_node(ty.hir_id) { - Node::PatExpr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - | Node::Pat(Pat { - kind: PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..), + hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(qpath), + .. + }) + | hir::Node::Pat(hir::Pat { + kind: + hir::PatKind::TupleStruct(qpath, ..) | hir::PatKind::Struct(qpath, ..), .. }) - | Node::Expr( - Expr { kind: ExprKind::Path(qpath), .. } - | &Expr { kind: ExprKind::Struct(qpath, ..), .. }, + | hir::Node::Expr( + hir::Expr { kind: hir::ExprKind::Path(qpath), .. } + | &hir::Expr { kind: hir::ExprKind::Struct(qpath, ..), .. }, ) => { - if let QPath::TypeRelative(qpath_ty, ..) = qpath + if let hir::QPath::TypeRelative(qpath_ty, ..) = qpath && qpath_ty.hir_id == ty.hir_id { Some(path.span) @@ -223,7 +227,7 @@ fn lint_ty_kind_usage(cx: &LateContext<'_>, res: &Res) -> bool { } } -fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option { +fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &hir::Path<'_>) -> Option { match &path.res { Res::Def(_, def_id) => { if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(*def_id) { @@ -244,13 +248,17 @@ fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option { None } -fn gen_args(segment: &PathSegment<'_>) -> String { +fn gen_args(segment: &hir::PathSegment<'_>) -> String { if let Some(args) = &segment.args { let lifetimes = args .args .iter() .filter_map(|arg| { - if let GenericArg::Lifetime(lt) = arg { Some(lt.ident.to_string()) } else { None } + if let hir::GenericArg::Lifetime(lt) = arg { + Some(lt.ident.to_string()) + } else { + None + } }) .collect::>(); @@ -272,7 +280,7 @@ declare_tool_lint! { } declare_tool_lint! { - /// The `usage_of_type_ir_inherent` lint detects usage `rustc_type_ir::inherent`. + /// The `usage_of_type_ir_inherent` lint detects usage of `rustc_type_ir::inherent`. /// /// This module should only be used within the trait solver. pub rustc::USAGE_OF_TYPE_IR_INHERENT, @@ -281,10 +289,43 @@ declare_tool_lint! { report_in_external_macro: true } -declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT]); +declare_tool_lint! { + /// The `usage_of_type_ir_traits` lint detects usage of `rustc_type_ir::Interner`, + /// or `rustc_infer::InferCtxtLike`. + /// + /// Methods of this trait should only be used within the type system abstraction layer, + /// and in the generic next trait solver implementation. Look for an analogously named + /// method on `TyCtxt` or `InferCtxt` (respectively). + pub rustc::USAGE_OF_TYPE_IR_TRAITS, + Allow, + "usage `rustc_type_ir`-specific abstraction traits outside of trait system", + report_in_external_macro: true +} + +declare_lint_pass!(TypeIr => [NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_INHERENT, USAGE_OF_TYPE_IR_TRAITS]); impl<'tcx> LateLintPass<'tcx> for TypeIr { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let res_def_id = match expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => path.res.opt_def_id(), + hir::ExprKind::Path(hir::QPath::TypeRelative(..)) | hir::ExprKind::MethodCall(..) => { + cx.typeck_results().type_dependent_def_id(expr.hir_id) + } + _ => return, + }; + let Some(res_def_id) = res_def_id else { + return; + }; + if let Some(assoc_item) = cx.tcx.opt_associated_item(res_def_id) + && let Some(trait_def_id) = assoc_item.trait_container(cx.tcx) + && (cx.tcx.is_diagnostic_item(sym::type_ir_interner, trait_def_id) + | cx.tcx.is_diagnostic_item(sym::type_ir_infer_ctxt_like, trait_def_id)) + { + cx.emit_span_lint(USAGE_OF_TYPE_IR_TRAITS, expr.span, TypeIrTraitUsage); + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return }; let is_mod_inherent = |def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id); @@ -394,15 +435,15 @@ declare_tool_lint! { declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]); impl LateLintPass<'_> for Diagnostics { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let collect_args_tys_and_spans = |args: &[Expr<'_>], reserve_one_extra: bool| { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| { let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra)); result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span))); result }; // Only check function calls and method calls. let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind { - ExprKind::Call(callee, args) => { + hir::ExprKind::Call(callee, args) => { match cx.typeck_results().node_type(callee.hir_id).kind() { &ty::FnDef(def_id, fn_gen_args) => { (callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false)) @@ -410,7 +451,7 @@ impl LateLintPass<'_> for Diagnostics { _ => return, // occurs for fns passed as args } } - ExprKind::MethodCall(_segment, _recv, args, _span) => { + hir::ExprKind::MethodCall(_segment, _recv, args, _span) => { let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr) else { return; @@ -514,8 +555,8 @@ impl Diagnostics { let mut is_inside_appropriate_impl = false; for (_hir_id, parent) in cx.tcx.hir_parent_iter(current_id) { debug!(?parent); - if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent - && let Impl { of_trait: Some(of_trait), .. } = impl_ + if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) = parent + && let hir::Impl { of_trait: Some(of_trait), .. } = impl_ && let Some(def_id) = of_trait.trait_def_id() && let Some(name) = cx.tcx.get_diagnostic_name(def_id) && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic) @@ -543,8 +584,8 @@ declare_tool_lint! { declare_lint_pass!(BadOptAccess => [BAD_OPT_ACCESS]); impl LateLintPass<'_> for BadOptAccess { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let ExprKind::Field(base, target) = expr.kind else { return }; + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + let hir::ExprKind::Field(base, target) = expr.kind else { return }; let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return }; // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be // avoided. @@ -581,9 +622,12 @@ declare_tool_lint! { declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]); impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if let ExprKind::Binary(BinOp { node: BinOpKind::Eq | BinOpKind::Ne, .. }, lhs, rhs) = - expr.kind + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { + if let hir::ExprKind::Binary( + hir::BinOp { node: hir::BinOpKind::Eq | hir::BinOpKind::Ne, .. }, + lhs, + rhs, + ) = expr.kind { if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) { cx.emit_span_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag); @@ -592,9 +636,9 @@ impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt { } } -fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { match &expr.kind { - ExprKind::MethodCall(..) => cx + hir::ExprKind::MethodCall(..) => cx .typeck_results() .type_dependent_def_id(expr.hir_id) .is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)), @@ -617,11 +661,11 @@ declare_lint_pass!(SymbolInternStringLiteral => [SYMBOL_INTERN_STRING_LITERAL]); impl<'tcx> LateLintPass<'tcx> for SymbolInternStringLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if let ExprKind::Call(path, [arg]) = expr.kind - && let ExprKind::Path(ref qpath) = path.kind + if let hir::ExprKind::Call(path, [arg]) = expr.kind + && let hir::ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::SymbolIntern, def_id) - && let ExprKind::Lit(kind) = arg.kind + && let hir::ExprKind::Lit(kind) = arg.kind && let rustc_ast::LitKind::Str(_, _) = kind.node { cx.emit_span_lint( diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c38a754001811..cd474f1b7db2c 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -645,6 +645,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT), LintId::of(USAGE_OF_TYPE_IR_INHERENT), + LintId::of(USAGE_OF_TYPE_IR_TRAITS), LintId::of(BAD_OPT_ACCESS), LintId::of(SPAN_USE_EQ_CTXT), ], diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0058630957291..036d68d13fa40 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -943,6 +943,11 @@ pub(crate) struct TyQualified { #[note] pub(crate) struct TypeIrInherentUsage; +#[derive(LintDiagnostic)] +#[diag(lint_type_ir_trait_usage)] +#[note] +pub(crate) struct TypeIrTraitUsage; + #[derive(LintDiagnostic)] #[diag(lint_non_glob_import_type_ir_inherent)] pub(crate) struct NonGlobImportTypeIrInherent { diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index f8e0a94f9ec2d..9ed11d9cc82ff 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -104,8 +104,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // determining if we are in a doctest context can't currently be determined // by the code itself (there are no specific attributes), but fortunately rustdoc // sets a perma-unstable env var for libtest so we just reuse that for now - let is_at_toplevel_doctest = - || self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok(); + let is_at_toplevel_doctest = || { + self.body_depth == 2 + && cx.tcx.env_var_os("UNSTABLE_RUSTDOC_TEST_PATH".as_ref()).is_some() + }; match item.kind { ItemKind::Impl(impl_) => { diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 7bbaa0496d53d..6c6b9a5510c69 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -1,3 +1,4 @@ +use std::ffi::OsStr; use std::intrinsics::transmute_unchecked; use std::mem::MaybeUninit; @@ -67,6 +68,10 @@ impl EraseType for &'_ [T] { type Result = [u8; size_of::<&'static [()]>()]; } +impl EraseType for &'_ OsStr { + type Result = [u8; size_of::<&'static OsStr>()]; +} + impl EraseType for &'_ ty::List { type Result = [u8; size_of::<&'static ty::List<()>>()]; } @@ -174,6 +179,10 @@ impl EraseType for Option<&'_ [T]> { type Result = [u8; size_of::>()]; } +impl EraseType for Option<&'_ OsStr> { + type Result = [u8; size_of::>()]; +} + impl EraseType for Option> { type Result = [u8; size_of::>>()]; } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 98314b5abfda1..c382bcd726ffa 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -1,5 +1,7 @@ //! Defines the set of legal keys that can be used in queries. +use std::ffi::OsStr; + use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId}; use rustc_hir::hir_id::{HirId, OwnerId}; use rustc_query_system::dep_graph::DepNodeIndex; @@ -498,6 +500,14 @@ impl Key for Option { } } +impl<'tcx> Key for &'tcx OsStr { + type Cache = DefaultCache; + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 527c18addbe12..d7ed703f4ae30 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -6,6 +6,7 @@ #![allow(unused_parens)] +use std::ffi::OsStr; use std::mem; use std::path::PathBuf; use std::sync::Arc; @@ -30,7 +31,9 @@ use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::{QueryCache, QueryMode, QueryState, try_get_cached}; +use rustc_query_system::query::{ + QueryCache, QueryMode, QueryStackDeferred, QueryState, try_get_cached, +}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{ @@ -119,6 +122,21 @@ rustc_queries! { desc { "perform lints prior to AST lowering" } } + /// Tracked access to environment variables. + /// + /// Useful for the implementation of `std::env!`, `proc-macro`s change + /// detection and other changes in the compiler's behaviour that is easier + /// to control with an environment variable than a flag. + /// + /// NOTE: This currently does not work with dependency info in the + /// analysis, codegen and linking passes, place extra code at the top of + /// `rustc_interface::passes::write_dep_info` to make that work. + query env_var_os(key: &'tcx OsStr) -> Option<&'tcx OsStr> { + // Environment variables are global state + eval_always + desc { "get the value of an environment variable" } + } + query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt { no_hash desc { "getting the resolver outputs" } diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 4834444ed1d7a..a099f77041709 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -488,7 +488,7 @@ macro_rules! define_callbacks { #[derive(Default)] pub struct QueryStates<'tcx> { $( - pub $name: QueryState<$($K)*>, + pub $name: QueryState<$($K)*, QueryStackDeferred<'tcx>>, )* } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 834d1f2a4a8de..08d4c1f9cf2f3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -7,6 +7,8 @@ pub mod tls; use std::assert_matches::{assert_matches, debug_assert_matches}; use std::borrow::Borrow; use std::cmp::Ordering; +use std::env::VarError; +use std::ffi::OsStr; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::{Bound, Deref}; @@ -1883,6 +1885,15 @@ impl<'tcx> TyCtxt<'tcx> { } None } + + /// Helper to get a tracked environment variable via. [`TyCtxt::env_var_os`] and converting to + /// UTF-8 like [`std::env::var`]. + pub fn env_var>(self, key: &'tcx K) -> Result<&'tcx str, VarError> { + match self.env_var_os(key.as_ref()) { + Some(value) => value.to_str().ok_or_else(|| VarError::NotUnicode(value.to_os_string())), + None => Err(VarError::NotPresent), + } + } } impl<'tcx> TyCtxtAt<'tcx> { diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 9450ce7ec444e..39fcc686c555a 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -88,7 +88,7 @@ impl<'tcx> Value> for Representability { if info.query.dep_kind == dep_kinds::representability && let Some(field_id) = info.query.def_id && let Some(field_id) = field_id.as_local() - && let Some(DefKind::Field) = info.query.def_kind + && let Some(DefKind::Field) = info.query.info.def_kind { let parent_id = tcx.parent(field_id.to_def_id()); let item_id = match tcx.def_kind(parent_id) { @@ -216,7 +216,7 @@ impl<'tcx, T> Value> for Result> continue; }; let frame_span = - frame.query.default_span(cycle[(i + 1) % cycle.len()].span); + frame.query.info.default_span(cycle[(i + 1) % cycle.len()].span); if frame_span.is_dummy() { continue; } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 67fca1d7c2947..2a1b20ba48b87 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -225,13 +225,13 @@ 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, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Interner, 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_session::config::{DebugInfo, EntryFnType}; use rustc_span::source_map::{Spanned, dummy_spanned, respan}; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, trace}; @@ -967,7 +967,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> { // `#[rustc_force_inline]` items should never be codegened. This should be caught by // the MIR validator. - tcx.delay_bug("attempt to codegen `#[rustc_force_inline]` item"); + tcx.dcx().delayed_bug("attempt to codegen `#[rustc_force_inline]` item"); } if def_id.is_local() { @@ -1235,6 +1235,11 @@ fn collect_items_of_instance<'tcx>( }; if mode == CollectionMode::UsedItems { + if tcx.sess.opts.debuginfo == DebugInfo::Full { + for var_debug_info in &body.var_debug_info { + collector.visit_var_debug_info(var_debug_info); + } + } for (bb, data) in traversal::mono_reachable(body, tcx, instance) { collector.visit_basic_block_data(bb, data) } diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index f6963a790675f..f575fe03019ed 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -6,6 +6,7 @@ // tidy-alphabetical-start #![allow(rustc::usage_of_type_ir_inherent)] +#![cfg_attr(not(bootstrap), allow(rustc::usage_of_type_ir_traits))] // tidy-alphabetical-end pub mod canonicalizer; diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index a83c388c747b6..30a9e718d2365 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -26,8 +26,8 @@ use rustc_middle::ty::TyCtxt; use rustc_query_system::dep_graph::SerializedDepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ - CycleError, HashResult, QueryCache, QueryConfig, QueryMap, QueryMode, QueryState, - get_query_incr, get_query_non_incr, + CycleError, HashResult, QueryCache, QueryConfig, QueryMap, QueryMode, QueryStackDeferred, + QueryState, get_query_incr, get_query_non_incr, }; use rustc_query_system::{HandleCycleError, Value}; use rustc_span::{ErrorGuaranteed, Span}; @@ -84,7 +84,10 @@ where } #[inline(always)] - fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState + fn query_state<'a>( + self, + qcx: QueryCtxt<'tcx>, + ) -> &'a QueryState> where QueryCtxt<'tcx>: 'a, { @@ -93,7 +96,7 @@ where unsafe { &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>) .byte_add(self.dynamic.query_state) - .cast::>() + .cast::>>() } } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 55281cd5ac704..3238c7a0912c5 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -5,6 +5,7 @@ use std::num::NonZero; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::unord::UnordMap; use rustc_hashes::Hash64; use rustc_index::Idx; @@ -24,8 +25,8 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext}; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ - QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffect, QueryStackFrame, - force_query, + QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffect, + QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra, force_query, }; use rustc_query_system::{QueryOverflow, QueryOverflowNote}; use rustc_serialize::{Decodable, Encodable}; @@ -65,7 +66,9 @@ impl<'tcx> HasDepContext for QueryCtxt<'tcx> { } } -impl QueryContext for QueryCtxt<'_> { +impl<'tcx> QueryContext for QueryCtxt<'tcx> { + type QueryInfo = QueryStackDeferred<'tcx>; + #[inline] fn next_job_id(self) -> QueryJobId { QueryJobId( @@ -82,7 +85,9 @@ impl QueryContext for QueryCtxt<'_> { /// Returns a query map representing active query jobs. /// It returns an incomplete map as an error if it fails /// to take locks. - fn collect_active_jobs(self) -> Result { + fn collect_active_jobs( + self, + ) -> Result>, QueryMap>> { let mut jobs = QueryMap::default(); let mut complete = true; @@ -95,6 +100,13 @@ impl QueryContext for QueryCtxt<'_> { if complete { Ok(jobs) } else { Err(jobs) } } + fn lift_query_info( + self, + info: &QueryStackDeferred<'tcx>, + ) -> rustc_query_system::query::QueryStackFrameExtra { + info.extract() + } + // Interactions with on_disk_cache fn load_side_effect( self, @@ -159,7 +171,10 @@ impl QueryContext for QueryCtxt<'_> { self.sess.dcx().emit_fatal(QueryOverflow { span: info.job.span, - note: QueryOverflowNote { desc: info.query.description, depth }, + note: QueryOverflowNote { + desc: self.lift_query_info(&info.query.info).description, + depth, + }, suggested_limit, crate_name: self.crate_name(LOCAL_CRATE), }); @@ -296,16 +311,17 @@ macro_rules! should_ever_cache_on_disk { }; } -pub(crate) fn create_query_frame< - 'tcx, - K: Copy + Key + for<'a> HashStable>, ->( - tcx: TyCtxt<'tcx>, - do_describe: fn(TyCtxt<'tcx>, K) -> String, - key: K, - kind: DepKind, - name: &'static str, -) -> QueryStackFrame { +fn create_query_frame_extra<'tcx, K: Key + Copy + 'tcx>( + (tcx, key, kind, name, do_describe): ( + TyCtxt<'tcx>, + K, + DepKind, + &'static str, + fn(TyCtxt<'tcx>, K) -> String, + ), +) -> QueryStackFrameExtra { + let def_id = key.key_as_def_id(); + // If reduced queries are requested, we may be printing a query stack due // to a panic. Avoid using `default_span` and `def_kind` in that case. let reduce_queries = with_reduced_queries(); @@ -324,13 +340,28 @@ pub(crate) fn create_query_frame< } else { Some(key.default_span(tcx)) }; - let def_id = key.key_as_def_id(); + let def_kind = if kind == dep_graph::dep_kinds::def_kind || reduce_queries { // Try to avoid infinite recursion. None } else { def_id.and_then(|def_id| def_id.as_local()).map(|def_id| tcx.def_kind(def_id)) }; + QueryStackFrameExtra::new(description, span, def_kind) +} + +pub(crate) fn create_query_frame< + 'tcx, + K: Copy + DynSend + DynSync + Key + for<'a> HashStable> + 'tcx, +>( + tcx: TyCtxt<'tcx>, + do_describe: fn(TyCtxt<'tcx>, K) -> String, + key: K, + kind: DepKind, + name: &'static str, +) -> QueryStackFrame> { + let def_id = key.key_as_def_id(); + let hash = || { tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); @@ -341,7 +372,10 @@ pub(crate) fn create_query_frame< }; let def_id_for_ty_in_cycle = key.def_id_for_ty_in_cycle(); - QueryStackFrame::new(description, span, def_id, def_kind, kind, def_id_for_ty_in_cycle, hash) + let info = + QueryStackDeferred::new((tcx, key, kind, name, do_describe), create_query_frame_extra); + + QueryStackFrame::new(info, kind, hash, def_id, def_id_for_ty_in_cycle) } pub(crate) fn encode_query_results<'a, 'tcx, Q>( @@ -688,7 +722,10 @@ macro_rules! define_queries { } } - pub(crate) fn try_collect_active_jobs<'tcx>(tcx: TyCtxt<'tcx>, qmap: &mut QueryMap) -> Option<()> { + pub(crate) fn try_collect_active_jobs<'tcx>( + tcx: TyCtxt<'tcx>, + qmap: &mut QueryMap>, + ) -> Option<()> { let make_query = |tcx, key| { let kind = rustc_middle::dep_graph::dep_kinds::$name; let name = stringify!($name); @@ -768,7 +805,9 @@ macro_rules! define_queries { // These arrays are used for iteration and can't be indexed by `DepKind`. - const TRY_COLLECT_ACTIVE_JOBS: &[for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap) -> Option<()>] = + const TRY_COLLECT_ACTIVE_JOBS: &[ + for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap>) -> Option<()> + ] = &[$(query_impl::$name::try_collect_active_jobs),*]; const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index 371b896400a58..e508eadb73b0b 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -6,6 +6,7 @@ use std::hash::Hash; use rustc_data_structures::fingerprint::Fingerprint; use rustc_span::ErrorGuaranteed; +use super::QueryStackFrameExtra; use crate::dep_graph::{DepKind, DepNode, DepNodeParams, SerializedDepNodeIndex}; use crate::error::HandleCycleError; use crate::ich::StableHashingContext; @@ -27,7 +28,7 @@ pub trait QueryConfig: Copy { fn format_value(self) -> fn(&Self::Value) -> String; // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState + fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState where Qcx: 'a; @@ -57,7 +58,7 @@ pub trait QueryConfig: Copy { fn value_from_cycle_error( self, tcx: Qcx::DepContext, - cycle_error: &CycleError, + cycle_error: &CycleError, guar: ErrorGuaranteed, ) -> Self::Value; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 402c783147298..de35cd79ea275 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use std::hash::Hash; use std::io::Write; use std::iter; @@ -12,6 +13,7 @@ use rustc_hir::def::DefKind; use rustc_session::Session; use rustc_span::{DUMMY_SP, Span}; +use super::QueryStackFrameExtra; use crate::dep_graph::DepContext; use crate::error::CycleStack; use crate::query::plumbing::CycleError; @@ -19,45 +21,54 @@ use crate::query::{QueryContext, QueryStackFrame}; /// Represents a span and a query key. #[derive(Clone, Debug)] -pub struct QueryInfo { +pub struct QueryInfo { /// The span corresponding to the reason for which this query was required. pub span: Span, - pub query: QueryStackFrame, + pub query: QueryStackFrame, } -pub type QueryMap = FxHashMap; +impl QueryInfo { + pub(crate) fn lift>( + &self, + qcx: Qcx, + ) -> QueryInfo { + QueryInfo { span: self.span, query: self.query.lift(qcx) } + } +} + +pub type QueryMap = FxHashMap>; /// A value uniquely identifying an active query job. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct QueryJobId(pub NonZero); impl QueryJobId { - fn query(self, map: &QueryMap) -> QueryStackFrame { + fn query(self, map: &QueryMap) -> QueryStackFrame { map.get(&self).unwrap().query.clone() } - fn span(self, map: &QueryMap) -> Span { + fn span(self, map: &QueryMap) -> Span { map.get(&self).unwrap().job.span } - fn parent(self, map: &QueryMap) -> Option { + fn parent(self, map: &QueryMap) -> Option { map.get(&self).unwrap().job.parent } - fn latch(self, map: &QueryMap) -> Option<&QueryLatch> { + fn latch(self, map: &QueryMap) -> Option<&QueryLatch> { map.get(&self).unwrap().job.latch.as_ref() } } #[derive(Clone, Debug)] -pub struct QueryJobInfo { - pub query: QueryStackFrame, - pub job: QueryJob, +pub struct QueryJobInfo { + pub query: QueryStackFrame, + pub job: QueryJob, } /// Represents an active query job. -#[derive(Clone, Debug)] -pub struct QueryJob { +#[derive(Debug)] +pub struct QueryJob { pub id: QueryJobId, /// The span corresponding to the reason for which this query was required. @@ -67,17 +78,23 @@ pub struct QueryJob { pub parent: Option, /// The latch that is used to wait on this job. - latch: Option, + latch: Option>, } -impl QueryJob { +impl Clone for QueryJob { + fn clone(&self) -> Self { + Self { id: self.id, span: self.span, parent: self.parent, latch: self.latch.clone() } + } +} + +impl QueryJob { /// Creates a new query job. #[inline] pub fn new(id: QueryJobId, span: Span, parent: Option) -> Self { QueryJob { id, span, parent, latch: None } } - pub(super) fn latch(&mut self) -> QueryLatch { + pub(super) fn latch(&mut self) -> QueryLatch { if self.latch.is_none() { self.latch = Some(QueryLatch::new()); } @@ -97,12 +114,12 @@ impl QueryJob { } impl QueryJobId { - pub(super) fn find_cycle_in_stack( + pub(super) fn find_cycle_in_stack( &self, - query_map: QueryMap, + query_map: QueryMap, current_job: &Option, span: Span, - ) -> CycleError { + ) -> CycleError { // Find the waitee amongst `current_job` parents let mut cycle = Vec::new(); let mut current_job = Option::clone(current_job); @@ -136,7 +153,7 @@ impl QueryJobId { #[cold] #[inline(never)] - pub fn find_dep_kind_root(&self, query_map: QueryMap) -> (QueryJobInfo, usize) { + pub fn find_dep_kind_root(&self, query_map: QueryMap) -> (QueryJobInfo, usize) { let mut depth = 1; let info = query_map.get(&self).unwrap(); let dep_kind = info.query.dep_kind; @@ -156,25 +173,31 @@ impl QueryJobId { } #[derive(Debug)] -struct QueryWaiter { +struct QueryWaiter { query: Option, condvar: Condvar, span: Span, - cycle: Mutex>, + cycle: Mutex>>, } #[derive(Debug)] -struct QueryLatchInfo { +struct QueryLatchInfo { complete: bool, - waiters: Vec>, + waiters: Vec>>, } -#[derive(Clone, Debug)] -pub(super) struct QueryLatch { - info: Arc>, +#[derive(Debug)] +pub(super) struct QueryLatch { + info: Arc>>, } -impl QueryLatch { +impl Clone for QueryLatch { + fn clone(&self) -> Self { + Self { info: Arc::clone(&self.info) } + } +} + +impl QueryLatch { fn new() -> Self { QueryLatch { info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), @@ -182,7 +205,11 @@ impl QueryLatch { } /// Awaits for the query job to complete. - pub(super) fn wait_on(&self, query: Option, span: Span) -> Result<(), CycleError> { + pub(super) fn wait_on( + &self, + query: Option, + span: Span, + ) -> Result<(), CycleError> { let waiter = Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() }); self.wait_on_inner(&waiter); @@ -197,7 +224,7 @@ impl QueryLatch { } /// Awaits the caller on this latch by blocking the current thread. - fn wait_on_inner(&self, waiter: &Arc) { + fn wait_on_inner(&self, waiter: &Arc>) { let mut info = self.info.lock(); if !info.complete { // We push the waiter on to the `waiters` list. It can be accessed inside @@ -232,7 +259,7 @@ impl QueryLatch { /// Removes a single waiter from the list of waiters. /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Arc { + fn extract_waiter(&self, waiter: usize) -> Arc> { let mut info = self.info.lock(); debug_assert!(!info.complete); // Remove the waiter from the list of waiters @@ -252,7 +279,11 @@ 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. -fn visit_waiters(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option> +fn visit_waiters( + query_map: &QueryMap, + query: QueryJobId, + mut visit: F, +) -> Option> where F: FnMut(Span, QueryJobId) -> Option>, { @@ -282,8 +313,8 @@ 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. -fn cycle_check( - query_map: &QueryMap, +fn cycle_check( + query_map: &QueryMap, query: QueryJobId, span: Span, stack: &mut Vec<(Span, QueryJobId)>, @@ -322,8 +353,8 @@ 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. -fn connected_to_root( - query_map: &QueryMap, +fn connected_to_root( + query_map: &QueryMap, query: QueryJobId, visited: &mut FxHashSet, ) -> bool { @@ -344,7 +375,7 @@ fn connected_to_root( } // Deterministically pick an query from a list -fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T +fn pick_query<'a, I: Clone, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T where F: Fn(&T) -> (Span, QueryJobId), { @@ -369,10 +400,10 @@ where /// the function return true. /// If a cycle was not found, the starting query is removed from `jobs` and /// the function returns false. -fn remove_cycle( - query_map: &QueryMap, +fn remove_cycle( + query_map: &QueryMap, jobs: &mut Vec, - wakelist: &mut Vec>, + wakelist: &mut Vec>>, ) -> bool { let mut visited = FxHashSet::default(); let mut stack = Vec::new(); @@ -473,7 +504,10 @@ 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. -pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) { +pub fn break_query_cycles( + query_map: QueryMap, + registry: &rayon_core::Registry, +) { let mut wakelist = Vec::new(); let mut jobs: Vec = query_map.keys().cloned().collect(); @@ -520,7 +554,7 @@ pub fn report_cycle<'a>( ) -> Diag<'a> { assert!(!stack.is_empty()); - let span = stack[0].query.default_span(stack[1 % stack.len()].span); + let span = stack[0].query.info.default_span(stack[1 % stack.len()].span); let mut cycle_stack = Vec::new(); @@ -529,31 +563,31 @@ pub fn report_cycle<'a>( for i in 1..stack.len() { let query = &stack[i].query; - let span = query.default_span(stack[(i + 1) % stack.len()].span); - cycle_stack.push(CycleStack { span, desc: query.description.to_owned() }); + let span = query.info.default_span(stack[(i + 1) % stack.len()].span); + cycle_stack.push(CycleStack { span, desc: query.info.description.to_owned() }); } let mut cycle_usage = None; if let Some((span, ref query)) = *usage { cycle_usage = Some(crate::error::CycleUsage { - span: query.default_span(span), - usage: query.description.to_string(), + span: query.info.default_span(span), + usage: query.info.description.to_string(), }); } - let alias = if stack.iter().all(|entry| matches!(entry.query.def_kind, Some(DefKind::TyAlias))) - { - Some(crate::error::Alias::Ty) - } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) { - Some(crate::error::Alias::Trait) - } else { - None - }; + let alias = + if stack.iter().all(|entry| matches!(entry.query.info.def_kind, Some(DefKind::TyAlias))) { + Some(crate::error::Alias::Ty) + } else if stack.iter().all(|entry| entry.query.info.def_kind == Some(DefKind::TraitAlias)) { + Some(crate::error::Alias::Trait) + } else { + None + }; let cycle_diag = crate::error::Cycle { span, cycle_stack, - stack_bottom: stack[0].query.description.to_owned(), + stack_bottom: stack[0].query.info.description.to_owned(), alias, cycle_usage, stack_count, @@ -589,6 +623,7 @@ pub fn print_query_stack( let Some(query_info) = query_map.get(&query) else { break; }; + let query_extra = qcx.lift_query_info(&query_info.query.info); if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. // FIXME: needs translation @@ -596,7 +631,7 @@ pub fn print_query_stack( #[allow(rustc::untranslatable_diagnostic)] dcx.struct_failure_note(format!( "#{} [{:?}] {}", - count_printed, query_info.query.dep_kind, query_info.query.description + count_printed, query_info.query.dep_kind, query_extra.description )) .with_span(query_info.job.span) .emit(); @@ -609,7 +644,7 @@ pub fn print_query_stack( "#{} [{}] {}", count_total, qcx.dep_context().dep_kind_info(query_info.query.dep_kind).name, - query_info.query.description + query_extra.description ); } diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 0d0c66aa978ef..ef21af7dafba8 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -1,4 +1,9 @@ mod plumbing; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::mem::transmute; +use std::sync::Arc; + pub use self::plumbing::*; mod job; @@ -11,6 +16,7 @@ mod caches; pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache}; mod config; +use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::DiagInner; use rustc_hashes::Hash64; use rustc_hir::def::DefKind; @@ -25,31 +31,59 @@ use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIn /// /// This is mostly used in case of cycles for error reporting. #[derive(Clone, Debug)] -pub struct QueryStackFrame { - pub description: String, - span: Option, - pub def_id: Option, - pub def_kind: Option, - /// A def-id that is extracted from a `Ty` in a query key - pub def_id_for_ty_in_cycle: Option, +pub struct QueryStackFrame { + /// This field initially stores a `QueryStackDeferred` during collection, + /// but can later be changed to `QueryStackFrameExtra` containing concrete information + /// by calling `lift`. This is done so that collecting query does not need to invoke + /// queries, instead `lift` will call queries in a more appropriate location. + pub info: I, + pub dep_kind: DepKind, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. hash: Hash64, + pub def_id: Option, + /// A def-id that is extracted from a `Ty` in a query key + pub def_id_for_ty_in_cycle: Option, } -impl QueryStackFrame { +impl QueryStackFrame { #[inline] pub fn new( - description: String, - span: Option, - def_id: Option, - def_kind: Option, + info: I, dep_kind: DepKind, - def_id_for_ty_in_cycle: Option, hash: impl FnOnce() -> Hash64, + def_id: Option, + def_id_for_ty_in_cycle: Option, ) -> Self { - Self { description, span, def_id, def_kind, def_id_for_ty_in_cycle, dep_kind, hash: hash() } + Self { info, def_id, dep_kind, hash: hash(), def_id_for_ty_in_cycle } + } + + fn lift>( + &self, + qcx: Qcx, + ) -> QueryStackFrame { + QueryStackFrame { + info: qcx.lift_query_info(&self.info), + dep_kind: self.dep_kind, + hash: self.hash, + def_id: self.def_id, + def_id_for_ty_in_cycle: self.def_id_for_ty_in_cycle, + } + } +} + +#[derive(Clone, Debug)] +pub struct QueryStackFrameExtra { + pub description: String, + span: Option, + pub def_kind: Option, +} + +impl QueryStackFrameExtra { + #[inline] + pub fn new(description: String, span: Option, def_kind: Option) -> Self { + Self { description, span, def_kind } } // FIXME(eddyb) Get more valid `Span`s on queries. @@ -62,7 +96,41 @@ impl QueryStackFrame { } } -/// Track a 'side effects' for a particular query. +/// Track a 'side effect' for a particular query. +/// This is used to hold a closure which can create `QueryStackFrameExtra`. +#[derive(Clone)] +pub struct QueryStackDeferred<'tcx> { + _dummy: PhantomData<&'tcx ()>, + + // `extract` may contain references to 'tcx, but we can't tell drop checking that it won't + // access it in the destructor. + extract: Arc QueryStackFrameExtra + DynSync + DynSend>, +} + +impl<'tcx> QueryStackDeferred<'tcx> { + pub fn new( + context: C, + extract: fn(C) -> QueryStackFrameExtra, + ) -> Self { + let extract: Arc QueryStackFrameExtra + DynSync + DynSend + 'tcx> = + Arc::new(move || extract(context)); + // SAFETY: The `extract` closure does not access 'tcx in its destructor as the only + // captured variable is `context` which is Copy and cannot have a destructor. + Self { _dummy: PhantomData, extract: unsafe { transmute(extract) } } + } + + pub fn extract(&self) -> QueryStackFrameExtra { + (self.extract)() + } +} + +impl<'tcx> Debug for QueryStackDeferred<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("QueryStackDeferred") + } +} + +/// Tracks 'side effects' for a particular query. /// This struct is saved to disk along with the query result, /// and loaded from disk if we mark the query as green. /// This allows us to 'replay' changes to global state @@ -81,12 +149,16 @@ pub enum QuerySideEffect { } pub trait QueryContext: HasDepContext { + type QueryInfo: Clone; + fn next_job_id(self) -> QueryJobId; /// Get the query information from the TLS context. fn current_query_job(self) -> Option; - fn collect_active_jobs(self) -> Result; + fn collect_active_jobs(self) -> Result, QueryMap>; + + fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra; /// Load a side effect associated to the node in the previous session. fn load_side_effect( diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 3a9d80280c26e..6ea8e3b920084 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -16,7 +16,7 @@ use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; use tracing::instrument; -use super::QueryConfig; +use super::{QueryConfig, QueryStackFrameExtra}; use crate::HandleCycleError; use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams}; use crate::ich::StableHashingContext; @@ -29,23 +29,23 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { move |x| x.0 == *k } -pub struct QueryState { - active: Sharded>, +pub struct QueryState { + active: Sharded)>>, } /// Indicates the state of a query for a given key in a query map. -enum QueryResult { +enum QueryResult { /// An already executing query. The query job can be used to await for its completion. - Started(QueryJob), + Started(QueryJob), /// The query panicked. Queries trying to wait on this will raise a fatal error which will /// silently panic. Poisoned, } -impl QueryResult { +impl QueryResult { /// Unwraps the query job expecting that it has started. - fn expect_job(self) -> QueryJob { + fn expect_job(self) -> QueryJob { match self { Self::Started(job) => job, Self::Poisoned => { @@ -55,7 +55,7 @@ impl QueryResult { } } -impl QueryState +impl QueryState where K: Eq + Hash + Copy + Debug, { @@ -66,8 +66,8 @@ where pub fn try_collect_active_jobs( &self, qcx: Qcx, - make_query: fn(Qcx, K) -> QueryStackFrame, - jobs: &mut QueryMap, + make_query: fn(Qcx, K) -> QueryStackFrame, + jobs: &mut QueryMap, ) -> Option<()> { let mut active = Vec::new(); @@ -76,7 +76,7 @@ where for shard in self.active.try_lock_shards() { for (k, v) in shard?.iter() { if let QueryResult::Started(ref job) = *v { - active.push((*k, job.clone())); + active.push((*k, (*job).clone())); } } } @@ -92,19 +92,19 @@ where } } -impl Default for QueryState { - fn default() -> QueryState { +impl Default for QueryState { + fn default() -> QueryState { QueryState { active: Default::default() } } } /// A type representing the responsibility to execute the job in the `job` field. /// This will poison the relevant query if dropped. -struct JobOwner<'tcx, K> +struct JobOwner<'tcx, K, I> where K: Eq + Hash + Copy, { - state: &'tcx QueryState, + state: &'tcx QueryState, key: K, } @@ -146,7 +146,7 @@ where } Stash => { let guar = if let Some(root) = cycle_error.cycle.first() - && let Some(span) = root.query.span + && let Some(span) = root.query.info.span { error.stash(span, StashKey::Cycle).unwrap() } else { @@ -157,7 +157,7 @@ where } } -impl<'tcx, K> JobOwner<'tcx, K> +impl<'tcx, K, I> JobOwner<'tcx, K, I> where K: Eq + Hash + Copy, { @@ -194,7 +194,7 @@ where } } -impl<'tcx, K> Drop for JobOwner<'tcx, K> +impl<'tcx, K, I> Drop for JobOwner<'tcx, K, I> where K: Eq + Hash + Copy, { @@ -222,10 +222,19 @@ where } #[derive(Clone, Debug)] -pub struct CycleError { +pub struct CycleError { /// The query and related span that uses the cycle. - pub usage: Option<(Span, QueryStackFrame)>, - pub cycle: Vec, + pub usage: Option<(Span, QueryStackFrame)>, + pub cycle: Vec>, +} + +impl CycleError { + fn lift>(&self, qcx: Qcx) -> CycleError { + CycleError { + usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift(qcx))), + cycle: self.cycle.iter().map(|info| info.lift(qcx)).collect(), + } + } } /// Checks whether there is already a value for this key in the in-memory @@ -262,10 +271,10 @@ where { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let query_map = qcx.collect_active_jobs().expect("failed to collect active queries"); + let query_map = qcx.collect_active_jobs().ok().expect("failed to collect active queries"); let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); - (mk_cycle(query, qcx, error), None) + (mk_cycle(query, qcx, error.lift(qcx)), None) } #[inline(always)] @@ -274,7 +283,7 @@ fn wait_for_query( qcx: Qcx, span: Span, key: Q::Key, - latch: QueryLatch, + latch: QueryLatch, current: Option, ) -> (Q::Value, Option) where @@ -314,7 +323,7 @@ where (v, Some(index)) } - Err(cycle) => (mk_cycle(query, qcx, cycle), None), + Err(cycle) => (mk_cycle(query, qcx, cycle.lift(qcx)), None), } } @@ -392,7 +401,7 @@ where fn execute_job( query: Q, qcx: Qcx, - state: &QueryState, + state: &QueryState, key: Q::Key, key_hash: u64, id: QueryJobId, diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index fcb638a117e31..6f48a75d61742 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -19,18 +19,15 @@ pub(crate) fn collect_definitions( fragment: &AstFragment, expansion: LocalExpnId, ) { - let InvocationParent { parent_def, impl_trait_context, in_attr } = - resolver.invocation_parents[&expansion]; - let mut visitor = DefCollector { resolver, parent_def, expansion, impl_trait_context, in_attr }; + let invocation_parent = resolver.invocation_parents[&expansion]; + let mut visitor = DefCollector { resolver, expansion, invocation_parent }; fragment.visit_with(&mut visitor); } /// Creates `DefId`s for nodes in the AST. struct DefCollector<'a, 'ra, 'tcx> { resolver: &'a mut Resolver<'ra, 'tcx>, - parent_def: LocalDefId, - impl_trait_context: ImplTraitContext, - in_attr: bool, + invocation_parent: InvocationParent, expansion: LocalExpnId, } @@ -42,7 +39,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { def_kind: DefKind, span: Span, ) -> LocalDefId { - let parent_def = self.parent_def; + let parent_def = self.invocation_parent.parent_def; debug!( "create_def(node_id={:?}, def_kind={:?}, parent_def={:?})", node_id, def_kind, parent_def @@ -60,9 +57,9 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } fn with_parent(&mut self, parent_def: LocalDefId, f: F) { - let orig_parent_def = mem::replace(&mut self.parent_def, parent_def); + let orig_parent_def = mem::replace(&mut self.invocation_parent.parent_def, parent_def); f(self); - self.parent_def = orig_parent_def; + self.invocation_parent.parent_def = orig_parent_def; } fn with_impl_trait( @@ -70,9 +67,10 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { impl_trait_context: ImplTraitContext, f: F, ) { - let orig_itc = mem::replace(&mut self.impl_trait_context, impl_trait_context); + let orig_itc = + mem::replace(&mut self.invocation_parent.impl_trait_context, impl_trait_context); f(self); - self.impl_trait_context = orig_itc; + self.invocation_parent.impl_trait_context = orig_itc; } fn collect_field(&mut self, field: &'a FieldDef, index: Option) { @@ -96,14 +94,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { fn visit_macro_invoc(&mut self, id: NodeId) { let id = id.placeholder_to_expn_id(); - let old_parent = self.resolver.invocation_parents.insert( - id, - InvocationParent { - parent_def: self.parent_def, - impl_trait_context: self.impl_trait_context, - in_attr: self.in_attr, - }, - ); + let old_parent = self.resolver.invocation_parents.insert(id, self.invocation_parent); assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation"); } } @@ -367,7 +358,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { self.with_parent(def, |this| visit::walk_anon_const(this, constant)); return; } - _ => self.parent_def, + _ => self.invocation_parent.parent_def, }; self.with_parent(parent_def, |this| visit::walk_expr(this, expr)) @@ -382,13 +373,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { // output or built artifacts, so replace them here... // Perhaps we should instead format APITs more robustly. let name = Symbol::intern(&pprust::ty_to_string(ty).replace('\n', " ")); - let kind = match self.impl_trait_context { + let kind = match self.invocation_parent.impl_trait_context { ImplTraitContext::Universal => DefKind::TyParam, ImplTraitContext::Existential => DefKind::OpaqueTy, ImplTraitContext::InBinding => return visit::walk_ty(self, ty), }; let id = self.create_def(*id, Some(name), kind, ty.span); - match self.impl_trait_context { + match self.invocation_parent.impl_trait_context { // Do not nest APIT, as we desugar them as `impl_trait: bounds`, // so the `impl_trait` node is not a parent to `bounds`. ImplTraitContext::Universal => visit::walk_ty(self, ty), @@ -459,9 +450,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_attribute(&mut self, attr: &'a Attribute) -> Self::Result { - let orig_in_attr = mem::replace(&mut self.in_attr, true); + let orig_in_attr = mem::replace(&mut self.invocation_parent.in_attr, true); visit::walk_attribute(self, attr); - self.in_attr = orig_in_attr; + self.invocation_parent.in_attr = orig_in_attr; } fn visit_inline_asm(&mut self, asm: &'a InlineAsm) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6bc644e9e1177..533e216ddb291 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3329,34 +3329,44 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }, |this| { this.with_lifetime_rib( - LifetimeRibKind::StaticIfNoLifetimeInScope { - lint_id: item.id, - // In impls, it's not a hard error yet due to backcompat. - emit_lint: true, + // Until these are a hard error, we need to create them within the correct binder, + // Otherwise the lifetimes of this assoc const think they are lifetimes of the trait. + LifetimeRibKind::AnonymousCreateParameter { + binder: item.id, + report_in_path: true, }, |this| { - // If this is a trait impl, ensure the const - // exists in trait - this.check_trait_item( - item.id, - item.ident, - &item.kind, - ValueNS, - item.span, - seen_trait_items, - |i, s, c| ConstNotMemberOfTrait(i, s, c), - ); + this.with_lifetime_rib( + LifetimeRibKind::StaticIfNoLifetimeInScope { + lint_id: item.id, + // In impls, it's not a hard error yet due to backcompat. + emit_lint: true, + }, + |this| { + // If this is a trait impl, ensure the const + // exists in trait + this.check_trait_item( + item.id, + item.ident, + &item.kind, + ValueNS, + item.span, + seen_trait_items, + |i, s, c| ConstNotMemberOfTrait(i, s, c), + ); - this.visit_generics(generics); - this.visit_ty(ty); - if let Some(expr) = expr { - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - this.resolve_const_body(expr, None); - } + this.visit_generics(generics); + this.visit_ty(ty); + if let Some(expr) = expr { + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_body(expr, None); + } + }, + ) }, ); }, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3e4742439655a..555764c26a633 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2118,7 +2118,9 @@ symbols! { type_changing_struct_update, type_const, type_id, + type_ir_infer_ctxt_like, type_ir_inherent, + type_ir_interner, type_length_limit, type_macros, type_name, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index fc352499146eb..4cfd8149b1e95 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -16,7 +16,7 @@ use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; -use rustc_type_ir::{Interner, elaborate}; +use rustc_type_ir::elaborate; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; @@ -802,7 +802,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::UnsafeBinder(_) => { // Only consider auto impls of unsafe traits when there are // no unsafe fields. - if self.tcx().trait_is_unsafe(def_id) && self_ty.has_unsafe_fields() { + if self.tcx().trait_def(def_id).safety.is_unsafe() + && self_ty.has_unsafe_fields() + { return; } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 26c4951069626..e512e8fc838f1 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -102,6 +102,7 @@ impl TypingMode { } } +#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_infer_ctxt_like")] pub trait InferCtxtLike: Sized { type Interner: Interner; fn cx(&self) -> Self::Interner; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e765cb66d00a3..8f86270d7dce7 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -15,6 +15,7 @@ use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesDat use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; use crate::{self as ty, search_graph}; +#[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] pub trait Interner: Sized + Copy diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 6291218950917..4e2baca27854f 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -6,6 +6,7 @@ feature(associated_type_defaults, never_type, rustc_attrs, negative_impls) )] #![cfg_attr(feature = "nightly", allow(internal_features))] +#![cfg_attr(not(bootstrap), allow(rustc::usage_of_type_ir_traits))] // tidy-alphabetical-end extern crate self as rustc_type_ir; diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs index 99b5ad9cb9fe5..98f471ad54b2e 100644 --- a/library/std/src/thread/spawnhook.rs +++ b/library/std/src/thread/spawnhook.rs @@ -113,18 +113,23 @@ where pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks { // Get a snapshot of the spawn hooks. // (Increments the refcount to the first node.) - let hooks = SPAWN_HOOKS.with(|hooks| { + if let Ok(hooks) = SPAWN_HOOKS.try_with(|hooks| { let snapshot = hooks.take(); hooks.set(snapshot.clone()); snapshot - }); - // Iterate over the hooks, run them, and collect the results in a vector. - let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref()) - .map(|hook| (hook.hook)(thread)) - .collect(); - // Pass on the snapshot of the hooks and the results to the new thread, - // which will then run SpawnHookResults::run(). - ChildSpawnHooks { hooks, to_run } + }) { + // Iterate over the hooks, run them, and collect the results in a vector. + let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref()) + .map(|hook| (hook.hook)(thread)) + .collect(); + // Pass on the snapshot of the hooks and the results to the new thread, + // which will then run SpawnHookResults::run(). + ChildSpawnHooks { hooks, to_run } + } else { + // TLS has been destroyed. Skip running the hooks. + // See https://github.com/rust-lang/rust/issues/138696 + ChildSpawnHooks::default() + } } /// The results of running the spawn hooks. diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index b3bd44990e408..1183fd59739a5 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -14,6 +14,22 @@ - [Deny-by-default Lints](lints/listing/deny-by-default.md) - [JSON Output](json.md) - [Tests](tests/index.md) +- [Targets](targets/index.md) + - [Built-in Targets](targets/built-in.md) + - [Custom Targets](targets/custom.md) + - [Known Issues](targets/known-issues.md) +- [Profile-guided Optimization](profile-guided-optimization.md) +- [Instrumentation-based Code Coverage](instrument-coverage.md) +- [Linker-plugin-based LTO](linker-plugin-lto.md) +- [Checking Conditional Configurations](check-cfg.md) + - [Cargo Specifics](check-cfg/cargo-specifics.md) +- [Exploit Mitigations](exploit-mitigations.md) +- [Symbol Mangling](symbol-mangling/index.md) + - [v0 Symbol Format](symbol-mangling/v0.md) +- [Contributing to `rustc`](contributing.md) + +-------- + - [Platform Support](platform-support.md) - [Target Tier Policy](target-tier-policy.md) - [Template for Target-specific Documentation](platform-support/TEMPLATE.md) @@ -114,16 +130,3 @@ - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) - [\*-nuttx-\*](platform-support/nuttx.md) -- [Targets](targets/index.md) - - [Built-in Targets](targets/built-in.md) - - [Custom Targets](targets/custom.md) - - [Known Issues](targets/known-issues.md) -- [Profile-guided Optimization](profile-guided-optimization.md) -- [Instrumentation-based Code Coverage](instrument-coverage.md) -- [Linker-plugin-based LTO](linker-plugin-lto.md) -- [Checking Conditional Configurations](check-cfg.md) - - [Cargo Specifics](check-cfg/cargo-specifics.md) -- [Exploit Mitigations](exploit-mitigations.md) -- [Symbol Mangling](symbol-mangling/index.md) - - [v0 Symbol Format](symbol-mangling/v0.md) -- [Contributing to `rustc`](contributing.md) diff --git a/tests/incremental/env/env_macro.rs b/tests/incremental/env/env_macro.rs new file mode 100644 index 0000000000000..0c026328874dd --- /dev/null +++ b/tests/incremental/env/env_macro.rs @@ -0,0 +1,18 @@ +// Check that changes to environment variables are propagated to `env!`. +// +// This test is intentionally written to not use any `#[cfg(rpass*)]`, to +// _really_ test that we re-compile if the environment variable changes. + +//@ revisions: cfail1 rpass2 rpass3 cfail4 +//@ [cfail1]unset-rustc-env:EXAMPLE_ENV +//@ [rpass2]rustc-env:EXAMPLE_ENV=one +//@ [rpass2]exec-env:EXAMPLE_ENV=one +//@ [rpass3]rustc-env:EXAMPLE_ENV=two +//@ [rpass3]exec-env:EXAMPLE_ENV=two +//@ [cfail4]unset-rustc-env:EXAMPLE_ENV + +fn main() { + assert_eq!(env!("EXAMPLE_ENV"), std::env::var("EXAMPLE_ENV").unwrap()); + //[cfail1]~^ ERROR environment variable `EXAMPLE_ENV` not defined at compile time + //[cfail4]~^^ ERROR environment variable `EXAMPLE_ENV` not defined at compile time +} diff --git a/tests/incremental/env/option_env_macro.rs b/tests/incremental/env/option_env_macro.rs new file mode 100644 index 0000000000000..44c3bfd69e050 --- /dev/null +++ b/tests/incremental/env/option_env_macro.rs @@ -0,0 +1,18 @@ +// Check that changes to environment variables are propagated to `option_env!`. +// +// This test is intentionally written to not use any `#[cfg(rpass*)]`, to +// _really_ test that we re-compile if the environment variable changes. + +//@ revisions: rpass1 rpass2 rpass3 rpass4 +//@ [rpass1]unset-rustc-env:EXAMPLE_ENV +//@ [rpass1]unset-exec-env:EXAMPLE_ENV +//@ [rpass2]rustc-env:EXAMPLE_ENV=one +//@ [rpass2]exec-env:EXAMPLE_ENV=one +//@ [rpass3]rustc-env:EXAMPLE_ENV=two +//@ [rpass3]exec-env:EXAMPLE_ENV=two +//@ [rpass4]unset-rustc-env:EXAMPLE_ENV +//@ [rpass4]unset-exec-env:EXAMPLE_ENV + +fn main() { + assert_eq!(option_env!("EXAMPLE_ENV"), std::env::var("EXAMPLE_ENV").ok().as_deref()); +} diff --git a/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs new file mode 100644 index 0000000000000..3fdd65d6c87f1 --- /dev/null +++ b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Z unstable-options +//@ ignore-stage1 + +#![feature(rustc_private)] +#![deny(rustc::usage_of_type_ir_traits)] + +extern crate rustc_type_ir; + +use rustc_type_ir::Interner; + +fn foo(cx: I, did: I::DefId) { + let _ = cx.trait_is_unsafe(did); + //~^ ERROR do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver +} + +fn main() {} diff --git a/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.stderr b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.stderr new file mode 100644 index 0000000000000..df29a4945582b --- /dev/null +++ b/tests/ui-fulldeps/internal-lints/import-of-type-ir-traits.stderr @@ -0,0 +1,15 @@ +error: do not use `rustc_type_ir::Interner` or `rustc_type_ir::InferCtxtLike` unless you're inside of the trait solver + --> $DIR/import-of-type-ir-traits.rs:12:13 + | +LL | let _ = cx.trait_is_unsafe(did); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the method or struct you're looking for is likely defined somewhere else downstream in the compiler +note: the lint level is defined here + --> $DIR/import-of-type-ir-traits.rs:5:9 + | +LL | #![deny(rustc::usage_of_type_ir_traits)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/assoc-const-elided-lifetime.stderr b/tests/ui/consts/assoc-const-elided-lifetime.stderr index 0c3e455eb2ded..9582152683578 100644 --- a/tests/ui/consts/assoc-const-elided-lifetime.stderr +++ b/tests/ui/consts/assoc-const-elided-lifetime.stderr @@ -35,8 +35,6 @@ note: cannot automatically infer `'static` because of other lifetimes in scope | LL | impl<'a> Foo<'a> { | ^^ -LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; - | ^^ help: use the `'static` lifetime | LL | const BAR: &'static () = &(); diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs index 95d59f9b894e4..ccf63f86fcf3a 100644 --- a/tests/ui/consts/static-default-lifetime/elided-lifetime.rs +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.rs @@ -16,7 +16,7 @@ impl Bar for Foo<'_> { const STATIC: &str = ""; //~^ ERROR `&` without an explicit lifetime name cannot be used here //~| WARN this was previously accepted by the compiler but is being phased out - //~| ERROR const not compatible with trait + //~| ERROR lifetime parameters or bounds on const `STATIC` do not match the trait declaration } fn main() {} diff --git a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr index ec01225c6bfa1..33873f5c5a502 100644 --- a/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr +++ b/tests/ui/consts/static-default-lifetime/elided-lifetime.stderr @@ -39,21 +39,15 @@ help: use the `'static` lifetime LL | const STATIC: &'static str = ""; | +++++++ -error[E0308]: const not compatible with trait - --> $DIR/elided-lifetime.rs:16:5 +error[E0195]: lifetime parameters or bounds on const `STATIC` do not match the trait declaration + --> $DIR/elided-lifetime.rs:16:17 | +LL | const STATIC: &str; + | - lifetimes in impl do not match this const in trait +... LL | const STATIC: &str = ""; - | ^^^^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected reference `&'static _` - found reference `&_` -note: the anonymous lifetime as defined here... - --> $DIR/elided-lifetime.rs:16:19 - | -LL | const STATIC: &str = ""; - | ^ - = note: ...does not necessarily outlive the static lifetime + | ^ lifetimes do not match const in trait error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0195`. diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs index 025fda4df5811..b50bf01453dcb 100644 --- a/tests/ui/consts/static-default-lifetime/static-trait-impl.rs +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.rs @@ -9,7 +9,7 @@ impl Bar<'_> for A { const STATIC: &str = ""; //~^ ERROR `&` without an explicit lifetime name cannot be used here //~| WARN this was previously accepted by the compiler but is being phased out - //~| ERROR const not compatible with trait + //~| ERROR lifetime parameters or bounds on const `STATIC` do not match the trait declaration } struct B; diff --git a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr index b8e2f412b492c..116f28e84847d 100644 --- a/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr +++ b/tests/ui/consts/static-default-lifetime/static-trait-impl.stderr @@ -21,25 +21,15 @@ help: use the `'static` lifetime LL | const STATIC: &'static str = ""; | +++++++ -error[E0308]: const not compatible with trait - --> $DIR/static-trait-impl.rs:9:5 +error[E0195]: lifetime parameters or bounds on const `STATIC` do not match the trait declaration + --> $DIR/static-trait-impl.rs:9:17 | +LL | const STATIC: &'a str; + | - lifetimes in impl do not match this const in trait +... LL | const STATIC: &str = ""; - | ^^^^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected reference `&_` - found reference `&_` -note: the anonymous lifetime as defined here... - --> $DIR/static-trait-impl.rs:9:19 - | -LL | const STATIC: &str = ""; - | ^ -note: ...does not necessarily outlive the anonymous lifetime as defined here - --> $DIR/static-trait-impl.rs:8:10 - | -LL | impl Bar<'_> for A { - | ^^ + | ^ lifetimes do not match const in trait error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0195`. diff --git a/tests/ui/mir/var_debug_ref.rs b/tests/ui/mir/var_debug_ref.rs new file mode 100644 index 0000000000000..1dcf38b5bb9f7 --- /dev/null +++ b/tests/ui/mir/var_debug_ref.rs @@ -0,0 +1,24 @@ +// Regression test for #138942, where a function was incorrectly internalized, despite the fact +// that it was referenced by a var debug info from another code generation unit. +// +//@ build-pass +//@ revisions: limited full +//@ compile-flags: -Ccodegen-units=4 +//@[limited] compile-flags: -Cdebuginfo=limited +//@[full] compile-flags: -Cdebuginfo=full +trait Fun { + const FUN: &'static fn(); +} +impl Fun for () { + const FUN: &'static fn() = &(detail::f as fn()); +} +mod detail { + // Place `f` in a distinct module to generate a separate code generation unit. + #[inline(never)] + pub(super) fn f() {} +} +fn main() { + // SingleUseConsts represents "x" using VarDebugInfoContents::Const. + // It is the only reference to `f` remaining. + let x = <() as ::Fun>::FUN; +} diff --git a/tests/ui/thread-local/spawn-hook-atexit.rs b/tests/ui/thread-local/spawn-hook-atexit.rs new file mode 100644 index 0000000000000..cc517e8fa4c66 --- /dev/null +++ b/tests/ui/thread-local/spawn-hook-atexit.rs @@ -0,0 +1,22 @@ +// Regression test for https://github.com/rust-lang/rust/issues/138696 +//@ run-pass + +#![feature(rustc_private)] + +extern crate libc; + +fn main() { + std::thread::spawn(|| { + unsafe { libc::atexit(spawn_in_atexit) }; + }) + .join() + .unwrap(); +} + +extern "C" fn spawn_in_atexit() { + std::thread::spawn(|| { + println!("Thread spawned in atexit"); + }) + .join() + .unwrap(); +} diff --git a/triagebot.toml b/triagebot.toml index ebbcfa4516b99..6dfae8b16a3ec 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -581,12 +581,12 @@ trigger_files = [ ] [notify-zulip."I-prioritize"] -zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +zulip_stream = 245100 # #t-compiler/prioritization/alerts topic = "#{number} {title}" message_on_add = """\ @*WG-prioritization/alerts* issue #{number} has been requested for prioritization. -# [Procedure](https://forge.rust-lang.org/compiler/prioritization/procedure.html#assign-priority-to-unprioritized-issues-with-i-prioritize-label) +# [Procedure](https://forge.rust-lang.org/compiler/prioritization.html) - Priority? - Regression? - Notify people/groups?