diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md index 1d93939e23360..9ccda17a6ef47 100644 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -4,13 +4,28 @@ about: Create a report for a documentation problem. labels: A-docs --- ### Location ### Summary - diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d4b41aad08ca4..a1bf0f94964bb 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1883,29 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } hir::LifetimeName::Param(param, ParamName::Fresh) } - LifetimeRes::Anonymous { binder, elided } => { - let mut l_name = None; - if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() { - if !captured_lifetimes.binders_to_ignore.contains(&binder) { - let p_id = self.next_node_id(); - let p_def_id = self.create_def( - captured_lifetimes.parent_def_id, - p_id, - DefPathData::LifetimeNs(kw::UnderscoreLifetime), - ); - captured_lifetimes - .captures - .insert(p_def_id, (span, p_id, ParamName::Fresh, res)); - l_name = Some(hir::LifetimeName::Param(p_def_id, ParamName::Fresh)); - } - self.captured_lifetimes = Some(captured_lifetimes); - }; - l_name.unwrap_or(if elided { - hir::LifetimeName::Implicit - } else { - hir::LifetimeName::Underscore - }) - } + LifetimeRes::Infer => hir::LifetimeName::Infer, LifetimeRes::Static => hir::LifetimeName::Static, LifetimeRes::Error => hir::LifetimeName::Error, res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span), diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 4cf1ac4d7abc0..0662d4d882f6a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -589,8 +589,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { hir::LifetimeName::Param(_, hir::ParamName::Fresh) | hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Implicit - | hir::LifetimeName::Underscore => { + | hir::LifetimeName::Infer => { // In this case, the user left off the lifetime; so // they wrote something like: // diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 29464cf8c4e4f..e099445117225 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -226,7 +226,7 @@ impl Qualif for CustomEq { // because that component may be part of an enum variant (e.g., // `Option::::Some`), in which case some values of this type may be // structural-match (`Option::None`). - traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some() + traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some() } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index f5b059793cf4b..53ae913f94f12 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -123,8 +123,7 @@ impl Callbacks for TimePassesCallbacks { fn config(&mut self, config: &mut interface::Config) { // If a --prints=... option has been given, we don't print the "total" // time because it will mess up the --prints output. See #64339. - self.time_passes = config.opts.prints.is_empty() - && (config.opts.unstable_opts.time_passes || config.opts.unstable_opts.time); + self.time_passes = config.opts.prints.is_empty() && config.opts.time_passes(); config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath; } } @@ -249,7 +248,7 @@ fn run_compiler( if sopts.describe_lints { let mut lint_store = rustc_lint::new_lint_store( sopts.unstable_opts.no_interleave_lints, - compiler.session().unstable_options(), + compiler.session().enable_internal_lints(), ); let registered_lints = if let Some(register_lints) = compiler.register_lints() { diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index f95e33cd16a7d..b17eb9c2d260e 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -256,3 +256,9 @@ passes-unused-duplicate = unused attribute passes-unused-multiple = multiple `{$name}` attributes .suggestion = remove this attribute .note = attribute also specified here + +passes-rustc-lint-opt-ty = `#[rustc_lint_opt_ty]` should be applied to a struct + .label = not a struct + +passes-rustc-lint-opt-deny-field-access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field + .label = not a field diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7b540e67aab3d..0e73d8fd7f600 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -619,6 +619,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints // to assist in changes to diagnostic APIs. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint on fields + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Const related: diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index c0d5d2bc46d0d..be5b7eccbafb2 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -738,13 +738,8 @@ pub enum LifetimeRes { binder: NodeId, }, /// This variant is used for anonymous lifetimes that we did not resolve during - /// late resolution. Shifting the work to the HIR lifetime resolver. - Anonymous { - /// Id of the introducing place. See `Param`. - binder: NodeId, - /// Whether this lifetime was spelled or elided. - elided: bool, - }, + /// late resolution. Those lifetimes will be inferred by typechecking. + Infer, /// Explicit `'static` lifetime. Static, /// Resolution failure. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 18ffc227fed86..f71400898e60b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -90,9 +90,6 @@ pub enum LifetimeName { /// User-given names or fresh (synthetic) names. Param(LocalDefId, ParamName), - /// User wrote nothing (e.g., the lifetime in `&u32`). - Implicit, - /// Implicit lifetime in a context like `dyn Foo`. This is /// distinguished from implicit lifetimes elsewhere because the /// lifetime that they default to must appear elsewhere within the @@ -110,8 +107,9 @@ pub enum LifetimeName { /// that was already reported. Error, - /// User wrote specifies `'_`. - Underscore, + /// User wrote an anonymous lifetime, either `'_` or nothing. + /// The semantics of this lifetime should be inferred by typechecking code. + Infer, /// User wrote `'static`. Static, @@ -120,10 +118,8 @@ pub enum LifetimeName { impl LifetimeName { pub fn ident(&self) -> Ident { match *self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Error => Ident::empty(), - LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime), + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(), + LifetimeName::Infer => Ident::with_dummy_span(kw::UnderscoreLifetime), LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime), LifetimeName::Param(_, param_name) => param_name.ident(), } @@ -132,8 +128,7 @@ impl LifetimeName { pub fn is_anonymous(&self) -> bool { match *self { LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Underscore + | LifetimeName::Infer | LifetimeName::Param(_, ParamName::Fresh) | LifetimeName::Error => true, LifetimeName::Static | LifetimeName::Param(..) => false, @@ -142,9 +137,7 @@ impl LifetimeName { pub fn is_elided(&self) -> bool { match self { - LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Implicit - | LifetimeName::Underscore => true, + LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true, // It might seem surprising that `Fresh` counts as // *not* elided -- but this is because, as far as the code diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index d00b65da7e6a5..640974115b926 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -496,9 +496,8 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime | LifetimeName::Param(_, ParamName::Error) | LifetimeName::Static | LifetimeName::Error - | LifetimeName::Implicit | LifetimeName::ImplicitObjectLifetimeDefault - | LifetimeName::Underscore => {} + | LifetimeName::Infer => {} } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index e5ae835e81349..c1b201da69121 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -100,23 +100,6 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { // the lifetime of the TyRptr let hir_id = lifetime.hir_id; match (self.tcx.named_region(hir_id), self.bound_region) { - // Find the index of the anonymous region that was part of the - // error. We will then search the function parameters for a bound - // region at the right depth with the same index - ( - Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), - ty::BrAnon(br_index), - ) => { - debug!( - "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}", - debruijn_index, anon_index, br_index - ); - if debruijn_index == self.current_index && anon_index == br_index { - self.found_type = Some(arg); - return; // we can stop visiting now - } - } - // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index @@ -151,8 +134,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { rl::Region::Static | rl::Region::Free(_, _) | rl::Region::EarlyBound(_, _) - | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _, _), + | rl::Region::LateBound(_, _, _), ) | None, _, @@ -206,16 +188,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { // the lifetime of the TyPath! - ( - Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)), - ty::BrAnon(br_index), - ) => { - if debruijn_index == self.current_index && anon_index == br_index { - self.found_it = true; - return; - } - } - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound id={:?} def_id={:?}", id, def_id); if id == def_id { @@ -239,7 +211,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { rl::Region::Static | rl::Region::EarlyBound(_, _) | rl::Region::LateBound(_, _, _) - | rl::Region::LateBoundAnon(_, _, _) | rl::Region::Free(_, _), ) | None, diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 6c7ddb4531ef8..94f81b6607798 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -329,6 +329,8 @@ pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R }) } +// JUSTIFICATION: before session exists, only config +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { tracing::trace!("run_compiler"); util::run_in_thread_pool_with_globals( diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 334a595a88ae6..8f0835917861a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -210,7 +210,7 @@ pub fn register_plugins<'a>( let mut lint_store = rustc_lint::new_lint_store( sess.opts.unstable_opts.no_interleave_lints, - sess.unstable_options(), + sess.enable_internal_lints(), ); register_lints(sess, &mut lint_store); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0a0eb99cd9266..21d9eaccf67e7 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] use crate::interface::parse_cfgspecs; use rustc_data_structures::fx::FxHashSet; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 97856ecf22c66..4c64e679b9571 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -559,6 +559,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec( cx: &LateContext<'tcx>, expr: &Expr<'_>, ) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> { - // FIXME(rustdoc): Lints which use this function use typecheck results which can cause - // `rustdoc` to error if there are resolution failures. - // - // As internal lints are currently always run if there are `unstable_options`, they are added - // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query. - // Crate lints run outside of a query so rustdoc currently doesn't disable them. - // - // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only - // run internal lints if the user is explicitly opting in or figure out a different way to - // avoid running lints for rustdoc. - if cx.tcx.sess.opts.actually_rustdoc { - return None; - } - match expr.kind { ExprKind::MethodCall(segment, _, _) if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => @@ -446,3 +432,38 @@ impl LateLintPass<'_> for Diagnostics { } } } + +declare_tool_lint! { + pub rustc::BAD_OPT_ACCESS, + Deny, + "prevent using options by field access when there is a wrapper function", + report_in_external_macro: true +} + +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 }; + 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. + if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) { + return; + } + + for field in adt_def.all_fields() { + if field.name == target.name && + let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) && + let Some(items) = attr.meta_item_list() && + let Some(item) = items.first() && + let Some(literal) = item.literal() && + let ast::LitKind::Str(val, _) = literal.kind + { + cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| { + lint.build(val.as_str()).emit(); } + ); + } + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 8726d36498bed..7b0702dad75de 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -509,8 +509,14 @@ fn register_internals(store: &mut LintStore) { store.register_late_pass(|| Box::new(TyTyKind)); store.register_lints(&Diagnostics::get_lints()); store.register_late_pass(|| Box::new(Diagnostics)); + store.register_lints(&BadOptAccess::get_lints()); + store.register_late_pass(|| Box::new(BadOptAccess)); store.register_lints(&PassByValue::get_lints()); store.register_late_pass(|| Box::new(PassByValue)); + // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and + // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and + // these lints will trigger all of the time - change this once migration to diagnostic structs + // and translation is completed store.register_group( false, "rustc::internal", @@ -523,6 +529,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), LintId::of(USAGE_OF_QUALIFIED_TY), LintId::of(EXISTING_DOC_KEYWORD), + LintId::of(BAD_OPT_ACCESS), ], ); } diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs index c71ba7b175313..9b2f445670532 100644 --- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs +++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs @@ -12,7 +12,6 @@ pub enum Region { Static, EarlyBound(/* index */ u32, /* lifetime decl */ DefId), LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId), - LateBoundAnon(ty::DebruijnIndex, /* late-bound index */ u32, /* anon index */ u32), Free(DefId, /* lifetime decl */ DefId), } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index a4e7a12bba323..f8792edc017b2 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -203,7 +203,7 @@ impl<'tcx> Const<'tcx> { pub fn to_valtree(self) -> ty::ValTree<'tcx> { match self.kind() { ty::ConstKind::Value(valtree) => valtree, - _ => bug!("expected ConstKind::Value"), + _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), } } diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index c7653bdbe84a2..93707bb18ceec 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -80,31 +80,25 @@ impl<'tcx> ValTree<'tcx> { } /// Get the values inside the ValTree as a slice of bytes. This only works for - /// constants with types &str and &[u8]. + /// constants with types &str, &[u8], or [u8; _]. pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> { match ty.kind() { ty::Ref(_, inner_ty, _) => match inner_ty.kind() { - ty::Str => { - let leafs = self - .unwrap_branch() - .into_iter() - .map(|v| v.unwrap_leaf().try_to_u8().unwrap()); - - return Some(tcx.arena.alloc_from_iter(leafs)); - } - ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => { - let leafs = self - .unwrap_branch() - .into_iter() - .map(|v| v.unwrap_leaf().try_to_u8().unwrap()); - - return Some(tcx.arena.alloc_from_iter(leafs)); - } - _ => {} + // `&str` can be interpreted as raw bytes + ty::Str => {} + // `&[u8]` can be interpreted as raw bytes + ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {} + // other `&_` can't be interpreted as raw bytes + _ => return None, }, - _ => {} + // `[u8; N]` can be interpreted as raw bytes + ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {} + // Otherwise, type cannot be interpreted as raw bytes + _ => return None, } - None + Some(tcx.arena.alloc_from_iter( + self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()), + )) } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 819513884cedc..7f2e81a71a93d 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1452,7 +1452,7 @@ pub trait PrettyPrinter<'tcx>: } }, (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), *t).unwrap_or_else(|| { + let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", t) }); p!("*"); 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 e32e0b11ba497..d6dd0f017941a 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 @@ -120,37 +120,35 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map( - |non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind { - traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt), - traits::NonStructuralMatchTyKind::Dynamic => { - "trait objects cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Opaque => { - "opaque types cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Closure => { - "closures cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Generator => { - "generators cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Float => { - "floating-point numbers cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Param => { - bug!("use of a constant whose type is a parameter inside a pattern") - } - traits::NonStructuralMatchTyKind::Projection => { - bug!("use of a constant whose type is a projection inside a pattern") - } - traits::NonStructuralMatchTyKind::Foreign => { - bug!("use of a value of a foreign type inside a pattern") - } - }) - }, - ) + traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { + with_no_trimmed_paths!(match non_sm_ty.kind() { + ty::Adt(adt, _) => self.adt_derive_msg(*adt), + ty::Dynamic(..) => { + "trait objects cannot be used in patterns".to_string() + } + ty::Opaque(..) => { + "opaque types cannot be used in patterns".to_string() + } + ty::Closure(..) => { + "closures cannot be used in patterns".to_string() + } + ty::Generator(..) | ty::GeneratorWitness(..) => { + "generators cannot be used in patterns".to_string() + } + ty::Float(..) => { + "floating-point numbers cannot be used in patterns".to_string() + } + ty::FnPtr(..) => { + "function pointers cannot be used in patterns".to_string() + } + ty::RawPtr(..) => { + "raw pointers cannot be used in patterns".to_string() + } + _ => { + bug!("use of a value of `{non_sm_ty}` inside a pattern") + } + }) + }) } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 47848cfa497f3..2f02d00ec9fb0 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -11,7 +11,7 @@ pub struct LowerSliceLenCalls; impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() > 0 + sess.mir_opt_level() > 0 } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs index 8ea550fa123b6..4919ad40098cb 100644 --- a/compiler/rustc_mir_transform/src/reveal_all.rs +++ b/compiler/rustc_mir_transform/src/reveal_all.rs @@ -9,7 +9,7 @@ pub struct RevealAll; impl<'tcx> MirPass<'tcx> for RevealAll { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.opts.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) + sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f3ccbbb56792d..fde12b9eee6b9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -121,6 +121,10 @@ impl CheckAttrVisitor<'_> { sym::rustc_lint_diagnostics => { self.check_rustc_lint_diagnostics(&attr, span, target) } + sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target), + sym::rustc_lint_opt_deny_field_access => { + self.check_rustc_lint_opt_deny_field_access(&attr, span, target) + } sym::rustc_clean | sym::rustc_dirty | sym::rustc_if_this_changed @@ -1382,6 +1386,35 @@ impl CheckAttrVisitor<'_> { self.check_applied_to_fn_or_method(attr, span, target) } + /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. + fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool { + match target { + Target::Struct => true, + _ => { + self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); + false + } + } + } + + /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. + fn check_rustc_lint_opt_deny_field_access( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Field => true, + _ => { + self.tcx + .sess + .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); + false + } + } + } + /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 0d4317f6b8881..5feb0e2956b74 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -625,3 +625,21 @@ pub struct UnusedMultiple { pub other: Span, pub name: Symbol, } + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_ty)] +pub struct RustcLintOptTy { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::rustc_lint_opt_deny_field_access)] +pub struct RustcLintOptDenyFieldAccess { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 9b5fd4ea6d133..ed65100ae7751 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -262,9 +262,6 @@ enum LifetimeRibKind { /// error on default object bounds (e.g., `Box`). AnonymousReportError, - /// Pass responsibility to `resolve_lifetime` code for all cases. - AnonymousPassThrough(NodeId), - /// Replace all anonymous lifetimes by provided lifetime. Elided(LifetimeRes), @@ -698,14 +695,25 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }, |this| { this.visit_generic_params(&bare_fn.generic_params, false); - this.resolve_fn_signature( - ty.id, - None, - false, - // We don't need to deal with patterns in parameters, because - // they are not possible for foreign or bodiless functions. - bare_fn.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), - &bare_fn.decl.output, + this.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder: ty.id, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( + ty.id, + false, + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + bare_fn + .decl + .inputs + .iter() + .map(|Param { ty, .. }| (None, &**ty)), + &bare_fn.decl.output, + ) + }, ); }, ) @@ -785,12 +793,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { | FnKind::Fn(_, _, sig, _, generics, None) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); - self.resolve_fn_signature( - fn_id, - None, - sig.decl.has_self(), - sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), - &sig.decl.output, + self.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( + fn_id, + sig.decl.has_self(), + sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &sig.decl.output, + ) + }, ); return; } @@ -815,15 +830,22 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let declaration = &sig.decl; let async_node_id = sig.header.asyncness.opt_return_id(); - this.resolve_fn_signature( - fn_id, - async_node_id, - declaration.has_self(), - declaration - .inputs - .iter() - .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), - &declaration.output, + this.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: async_node_id.is_some(), + }, + |this| { + this.resolve_fn_signature( + fn_id, + declaration.has_self(), + declaration + .inputs + .iter() + .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), + &declaration.output, + ) + }, ); // Construct the list of in-scope lifetime parameters for async lowering. @@ -868,7 +890,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| this.visit_block(body), ); @@ -896,7 +918,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { this.with_lifetime_rib( match binder { ClosureBinder::NotPresent => { - LifetimeRibKind::AnonymousPassThrough(fn_id) + LifetimeRibKind::Elided(LifetimeRes::Infer) } ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError, }, @@ -908,7 +930,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let previous_state = replace(&mut this.in_func_body, true); // Resolve the function body, potentially inside the body of an async closure this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(fn_id), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| this.visit_expr(body), ); @@ -1038,12 +1060,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { kind: LifetimeBinderKind::PolyTrait, .. } => { - self.resolve_fn_signature( - binder, - None, - false, - p_args.inputs.iter().map(|ty| (None, &**ty)), - &p_args.output, + self.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( + binder, + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, + ) + }, ); break; } @@ -1053,8 +1082,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { visit::walk_generic_args(self, path_span, args); break; } - LifetimeRibKind::AnonymousPassThrough(..) - | LifetimeRibKind::AnonymousCreateParameter { .. } + LifetimeRibKind::AnonymousCreateParameter { .. } | LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Elided(_) | LifetimeRibKind::ElisionFailure @@ -1415,8 +1443,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | LifetimeRibKind::AnonymousReportError | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many), // An anonymous lifetime is legal here, go ahead. - LifetimeRibKind::AnonymousPassThrough(_) - | LifetimeRibKind::AnonymousCreateParameter { .. } => { + LifetimeRibKind::AnonymousCreateParameter { .. } => { Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt }) } // Only report if eliding the lifetime would have the same @@ -1527,14 +1554,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } - LifetimeRibKind::AnonymousPassThrough(node_id) => { - self.record_lifetime_res( - lifetime.id, - LifetimeRes::Anonymous { binder: node_id, elided }, - elision_candidate, - ); - return; - } LifetimeRibKind::Elided(res) => { self.record_lifetime_res(lifetime.id, res, elision_candidate); return; @@ -1632,6 +1651,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | PathSource::Struct | PathSource::TupleStruct(..) => false, }; + if !missing && !segment.has_generic_args { + continue; + } let elided_lifetime_span = if segment.has_generic_args { // If there are brackets, but not generic arguments, then use the opening bracket @@ -1653,37 +1675,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if !missing { // Do not create a parameter for patterns and expressions. - for rib in self.lifetime_ribs.iter().rev() { - match rib.kind { - LifetimeRibKind::AnonymousPassThrough(binder) => { - let res = LifetimeRes::Anonymous { binder, elided: true }; - for id in node_ids { - self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); - } - break; - } - // `LifetimeRes::Error`, which would usually be used in the case of - // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead, - // we simply resolve to an implicit lifetime, which will be checked later, at - // which point a suitable error will be emitted. - LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => { - // FIXME(cjgillot) This resolution is wrong, but this does not matter - // since these cases are erroneous anyway. Lifetime resolution should - // emit a "missing lifetime specifier" diagnostic. - let res = - LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true }; - for id in node_ids { - self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named); - } - break; - } - LifetimeRibKind::AnonymousCreateParameter { .. } - | LifetimeRibKind::Elided(_) - | LifetimeRibKind::ElisionFailure - | LifetimeRibKind::Generics { .. } - | LifetimeRibKind::ConstGeneric - | LifetimeRibKind::AnonConst => {} - } + for id in node_ids { + self.record_lifetime_res( + id, + LifetimeRes::Infer, + LifetimeElisionCandidate::Named, + ); } continue; } @@ -1750,19 +1747,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } break; } - // `PassThrough` is the normal case. - LifetimeRibKind::AnonymousPassThrough(binder) => { - let res = LifetimeRes::Anonymous { binder, elided: true }; - let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); - for id in node_ids { - self.record_lifetime_res( - id, - res, - replace(&mut candidate, LifetimeElisionCandidate::Ignore), - ); - } - break; - } LifetimeRibKind::Elided(res) => { let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { @@ -1837,15 +1821,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ) } match res { - LifetimeRes::Param { .. } - | LifetimeRes::Fresh { .. } - | LifetimeRes::Anonymous { .. } - | LifetimeRes::Static => { + LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => { if let Some(ref mut candidates) = self.lifetime_elision_candidates { candidates.insert(res, candidate); } } - LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} + LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {} } } @@ -1864,18 +1845,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn resolve_fn_signature( &mut self, fn_id: NodeId, - async_node_id: Option, has_self: bool, inputs: impl Iterator, &'ast Ty)> + Clone, output_ty: &'ast FnRetTy, ) { // Add each argument to the rib. - let parameter_rib = LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: async_node_id.is_some(), - }; - let elision_lifetime = - self.with_lifetime_rib(parameter_rib, |this| this.resolve_fn_params(has_self, inputs)); + let elision_lifetime = self.resolve_fn_params(has_self, inputs); debug!(?elision_lifetime); let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures); @@ -2268,26 +2243,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { this.visit_ty(ty); }); - this.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(item.id), - |this| { - if let Some(expr) = expr { - let constant_item_kind = match item.kind { - ItemKind::Const(..) => ConstantItemKind::Const, - ItemKind::Static(..) => ConstantItemKind::Static, - _ => unreachable!(), - }; - // We already forbid generic params because of the above item rib, - // so it doesn't matter whether this is a trivial constant. - this.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - Some((item.ident, constant_item_kind)), - |this| this.visit_expr(expr), - ); - } - }, - ); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + if let Some(expr) = expr { + let constant_item_kind = match item.kind { + ItemKind::Const(..) => ConstantItemKind::Const, + ItemKind::Static(..) => ConstantItemKind::Static, + _ => unreachable!(), + }; + // We already forbid generic params because of the above item rib, + // so it doesn't matter whether this is a trivial constant. + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + Some((item.ident, constant_item_kind)), + |this| this.visit_expr(expr), + ); + } + }); }); } @@ -2544,7 +2516,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(item.id), + LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { this.with_constant_rib( IsRepeatExpr::No, @@ -2717,17 +2689,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - self.with_lifetime_rib( - LifetimeRibKind::AnonymousPassThrough(item.id), - |this| { - this.with_constant_rib( - IsRepeatExpr::No, - HasGenericParams::Yes, - None, - |this| this.visit_expr(expr), - ) - }, - ); + self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| this.visit_expr(expr), + ) + }); } } AssocItemKind::Fn(box Fn { generics, .. }) => { diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index a7fd7c427c759..94460e33d8b01 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1,4 +1,3 @@ -// ignore-tidy-filelength //! Resolution of early vs late bound lifetimes. //! //! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this @@ -21,10 +20,9 @@ use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_span::def_id::DefId; -use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::borrow::Cow; -use std::cell::Cell; use std::fmt; use std::mem::take; @@ -33,8 +31,6 @@ trait RegionExt { fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region); - fn late_anon(named_late_bound_vars: u32, index: &Cell) -> Region; - fn id(&self) -> Option; fn shifted(self, amount: u32) -> Region; @@ -65,16 +61,9 @@ impl RegionExt for Region { (def_id, Region::LateBound(depth, idx, def_id.to_def_id())) } - fn late_anon(named_late_bound_vars: u32, index: &Cell) -> Region { - let i = index.get(); - index.set(i + 1); - let depth = ty::INNERMOST; - Region::LateBoundAnon(depth, named_late_bound_vars + i, i) - } - fn id(&self) -> Option { match *self { - Region::Static | Region::LateBoundAnon(..) => None, + Region::Static => None, Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => { Some(id) @@ -87,9 +76,6 @@ impl RegionExt for Region { Region::LateBound(debruijn, idx, id) => { Region::LateBound(debruijn.shifted_in(amount), idx, id) } - Region::LateBoundAnon(debruijn, index, anon_index) => { - Region::LateBoundAnon(debruijn.shifted_in(amount), index, anon_index) - } _ => self, } } @@ -99,9 +85,6 @@ impl RegionExt for Region { Region::LateBound(debruijn, index, id) => { Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id) } - Region::LateBoundAnon(debruijn, index, anon_index) => { - Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index, anon_index) - } _ => self, } } @@ -193,10 +176,6 @@ enum Scope<'a> { s: ScopeRef<'a>, - /// In some cases not allowing late bounds allows us to avoid ICEs. - /// This is almost ways set to true. - allow_late_bound: bool, - /// If this binder comes from a where clause, specify how it was created. /// This is used to diagnose inaccessible lifetimes in APIT: /// ```ignore (illustrative) @@ -215,9 +194,8 @@ enum Scope<'a> { }, /// A scope which either determines unspecified lifetimes or errors - /// on them (e.g., due to ambiguity). For more details, see `Elide`. + /// on them (e.g., due to ambiguity). Elision { - elide: Elide, s: ScopeRef<'a>, }, @@ -273,7 +251,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { opaque_type_parent, scope_type, hir_id, - allow_late_bound, where_bound_origin, s: _, } => f @@ -283,16 +260,13 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("opaque_type_parent", opaque_type_parent) .field("scope_type", scope_type) .field("hir_id", hir_id) - .field("allow_late_bound", allow_late_bound) .field("where_bound_origin", where_bound_origin) .field("s", &"..") .finish(), Scope::Body { id, s: _ } => { f.debug_struct("Body").field("id", id).field("s", &"..").finish() } - Scope::Elision { elide, s: _ } => { - f.debug_struct("Elision").field("elide", elide).field("s", &"..").finish() - } + Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(), Scope::ObjectLifetimeDefault { lifetime, s: _ } => f .debug_struct("ObjectLifetimeDefault") .field("lifetime", lifetime) @@ -309,21 +283,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { } } -#[derive(Clone, Debug)] -enum Elide { - /// Use a fresh anonymous late-bound lifetime each time, by - /// incrementing the counter to generate sequential indices. All - /// anonymous lifetimes must start *after* named bound vars. - FreshLateAnon(u32, Cell), - /// Always use this one lifetime. - Exact(Region), - /// Less or more than one lifetime were found, error on unspecified. - Error, - /// Forbid lifetime elision inside of a larger scope where it would be - /// permitted. For example, in let position impl trait. - Forbid, -} - type ScopeRef<'a> = &'a Scope<'a>; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; @@ -486,9 +445,6 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty:: let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local())); ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name)) } - Region::LateBoundAnon(_, _, anon_idx) => { - ty::BoundVariableKind::Region(ty::BrAnon(*anon_idx)) - } _ => bug!("{:?} is not a late region", region), } } @@ -623,7 +579,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; @@ -664,8 +619,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { // No lifetime parameters, but implied 'static. - let scope = Scope::Elision { elide: Elide::Exact(Region::Static), s: ROOT_SCOPE }; - self.with(scope, |this| intravisit::walk_item(this, item)); + self.with(Scope::Elision { s: self.scope }, |this| { + intravisit::walk_item(this, item) + }); } hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { // Opaque types are visited when we visit the @@ -741,7 +697,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { opaque_type_parent: true, scope_type: BinderScopeType::Normal, s: ROOT_SCOPE, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -794,7 +749,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -819,13 +773,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // `Box`. self.resolve_object_lifetime_default(lifetime) } - LifetimeName::Implicit | LifetimeName::Underscore => { + LifetimeName::Infer => { // If the user writes `'_`, we use the *ordinary* elision // rules. So the `'_` in e.g., `Box` will be // resolved the same as the `'_` in `&'_ Foo`. // // cc #48468 - self.resolve_elided_lifetimes(&[lifetime]) } LifetimeName::Param(..) | LifetimeName::Static => { // If the user wrote an explicit name, use that. @@ -860,7 +813,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // position impl Trait let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { - let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope }; + let scope = Scope::Elision { s: this.scope }; this.with(scope, |this| { intravisit::walk_item(this, opaque_ty); }) @@ -936,7 +889,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let mut index = self.next_early_index_for_opaque_type(); debug!(?index); - let mut elision = None; let mut lifetimes = FxIndexMap::default(); let mut non_lifetime_count = 0; debug!(?generics.params); @@ -945,15 +897,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { GenericParamKind::Lifetime { .. } => { let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, ¶m); lifetimes.insert(def_id, reg); - if let hir::ParamName::Plain(Ident { - name: kw::UnderscoreLifetime, - .. - }) = param.name - { - // Pick the elided lifetime "definition" if one exists - // and use it to make an elision scope. - elision = Some(reg); - } } GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { non_lifetime_count += 1; @@ -963,51 +906,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let next_early_index = index + non_lifetime_count; self.map.late_bound_vars.insert(ty.hir_id, vec![]); - if let Some(elision_region) = elision { - let scope = - Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope }; - self.with(scope, |this| { - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - next_early_index, - s: this.scope, - opaque_type_parent: false, - scope_type: BinderScopeType::Normal, - allow_late_bound: false, - where_bound_origin: None, - }; - this.with(scope, |this| { - this.visit_generics(generics); - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); - }); - } else { - let scope = Scope::Binder { - hir_id: ty.hir_id, - lifetimes, - next_early_index, - s: self.scope, - opaque_type_parent: false, - scope_type: BinderScopeType::Normal, - allow_late_bound: false, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - }) - }); - } + let scope = Scope::Binder { + hir_id: ty.hir_id, + lifetimes, + next_early_index, + s: self.scope, + opaque_type_parent: false, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| { + this.visit_generics(generics); + for bound in bounds { + this.visit_param_bound(bound); + } + }) + }); } _ => intravisit::walk_ty(self, ty), } @@ -1051,7 +967,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: false, where_bound_origin: None, }; self.with(scope, |this| { @@ -1113,7 +1028,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1135,15 +1049,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { #[tracing::instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { match lifetime_ref.name { - hir::LifetimeName::ImplicitObjectLifetimeDefault - | hir::LifetimeName::Implicit - | hir::LifetimeName::Underscore => self.resolve_elided_lifetimes(&[lifetime_ref]), hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), hir::LifetimeName::Param(param_def_id, _) => { self.resolve_lifetime_ref(param_def_id, lifetime_ref) } // If we've already reported an error, just ignore `lifetime_ref`. hir::LifetimeName::Error => {} + // Those will be resolved by typechecking. + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {} } } @@ -1156,12 +1069,21 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { + fn visit_fn( + &mut self, + fk: intravisit::FnKind<'tcx>, + fd: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + _: Span, + _: hir::HirId, + ) { let output = match fd.output { hir::FnRetTy::DefaultReturn(_) => None, hir::FnRetTy::Return(ref ty) => Some(&**ty), }; - self.visit_fn_like_elision(&fd.inputs, output); + self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); + intravisit::walk_fn_kind(self, fk); + self.visit_nested_body(body_id) } fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { @@ -1219,7 +1141,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: Some(origin), }; this.with(scope, |this| { @@ -1292,7 +1213,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index: self.next_early_index(), opaque_type_parent: false, scope_type, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1343,7 +1263,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { next_early_index, opaque_type_parent: false, scope_type, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, |this| { @@ -1597,7 +1516,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { s: self.scope, opaque_type_parent: true, scope_type: BinderScopeType::Normal, - allow_late_bound: true, where_bound_origin: None, }; self.with(scope, walk); @@ -1773,30 +1691,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { ); if generic_args.parenthesized { - self.visit_fn_like_elision(generic_args.inputs(), Some(generic_args.bindings[0].ty())); + self.visit_fn_like_elision( + generic_args.inputs(), + Some(generic_args.bindings[0].ty()), + false, + ); return; } - let mut elide_lifetimes = true; - let lifetimes: Vec<_> = generic_args - .args - .iter() - .filter_map(|arg| match arg { - hir::GenericArg::Lifetime(lt) => { - if !lt.is_elided() { - elide_lifetimes = false; - } - Some(lt) - } - _ => None, - }) - .collect(); - // We short-circuit here if all are elided in order to pluralize - // possible errors - if elide_lifetimes { - self.resolve_elided_lifetimes(&lifetimes); - } else { - lifetimes.iter().for_each(|lt| self.visit_lifetime(lt)); + for arg in generic_args.args { + if let hir::GenericArg::Lifetime(lt) = arg { + self.visit_lifetime(lt); + } } // Figure out if this is a type/trait segment, @@ -2052,380 +1958,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { &mut self, inputs: &'tcx [hir::Ty<'tcx>], output: Option<&'tcx hir::Ty<'tcx>>, + in_closure: bool, ) { - debug!("visit_fn_like_elision: enter"); - let mut scope = &*self.scope; - let hir_id = loop { - match scope { - Scope::Binder { hir_id, allow_late_bound: true, .. } => { - break *hir_id; - } - Scope::ObjectLifetimeDefault { ref s, .. } - | Scope::Elision { ref s, .. } - | Scope::Supertrait { ref s, .. } - | Scope::TraitRefBoundary { ref s, .. } => { - scope = *s; - } - Scope::Root - | Scope::Body { .. } - | Scope::Binder { allow_late_bound: false, .. } => { - // See issues #83907 and #83693. Just bail out from looking inside. - // See the issue #95023 for not allowing late bound - self.tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - "In fn_like_elision without appropriate scope above", - ); - return; - } - } - }; - // While not strictly necessary, we gather anon lifetimes *before* actually - // visiting the argument types. - let mut gather = GatherAnonLifetimes { anon_count: 0 }; - for input in inputs { - gather.visit_ty(input); - } - trace!(?gather.anon_count); - let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default(); - let named_late_bound_vars = late_bound_vars.len() as u32; - late_bound_vars.extend( - (0..gather.anon_count).map(|var| ty::BoundVariableKind::Region(ty::BrAnon(var))), - ); - let arg_scope = Scope::Elision { - elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)), - s: self.scope, - }; - self.with(arg_scope, |this| { + self.with(Scope::Elision { s: self.scope }, |this| { for input in inputs { this.visit_ty(input); } - }); - - let Some(output) = output else { return }; - - debug!("determine output"); - - // Figure out if there's a body we can get argument names from, - // and whether there's a `self` argument (treated specially). - let mut assoc_item_kind = None; - let mut impl_self = None; - let parent = self.tcx.hir().get_parent_node(output.hir_id); - match self.tcx.hir().get(parent) { - // `fn` definitions and methods. - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) => {} - - Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => { - if let hir::ItemKind::Trait(.., ref trait_items) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind - { - assoc_item_kind = - trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind); - } - } - - Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, _), .. }) => { - if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind - { - impl_self = Some(self_ty); - assoc_item_kind = - items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind); - } - } - - // Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds). - Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => {}, - - Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => {}, - - // Everything else (only closures?) doesn't - // actually enjoy elision in return types. - _ => { - self.visit_ty(output); - return; - } - }; - - let has_self = match assoc_item_kind { - Some(hir::AssocItemKind::Fn { has_self }) => has_self, - _ => false, - }; - - // In accordance with the rules for lifetime elision, we can determine - // what region to use for elision in the output type in two ways. - // First (determined here), if `self` is by-reference, then the - // implied output region is the region of the self parameter. - if has_self { - struct SelfVisitor<'a> { - map: &'a NamedRegionMap, - impl_self: Option<&'a hir::TyKind<'a>>, - lifetime: Set1, - } - - impl SelfVisitor<'_> { - // Look for `self: &'a Self` - also desugared from `&'a self`, - // and if that matches, use it for elision and return early. - fn is_self_ty(&self, res: Res) -> bool { - if let Res::SelfTy { .. } = res { - return true; - } - - // Can't always rely on literal (or implied) `Self` due - // to the way elision rules were originally specified. - if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = - self.impl_self - { - match path.res { - // Permit the types that unambiguously always - // result in the same type constructor being used - // (it can't differ between `Self` and `self`). - Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _) - | Res::PrimTy(_) => return res == path.res, - _ => {} - } - } - - false - } - } - - impl<'a> Visitor<'a> for SelfVisitor<'a> { - fn visit_ty(&mut self, ty: &'a hir::Ty<'a>) { - if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind { - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind - { - if self.is_self_ty(path.res) { - if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - self.lifetime.insert(*lifetime); - } - } - } - } - intravisit::walk_ty(self, ty) - } - } - - let mut visitor = SelfVisitor { - map: self.map, - impl_self: impl_self.map(|ty| &ty.kind), - lifetime: Set1::Empty, - }; - visitor.visit_ty(&inputs[0]); - if let Set1::One(lifetime) = visitor.lifetime { - let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope }; - self.with(scope, |this| this.visit_ty(output)); - return; + if !in_closure && let Some(output) = output { + this.visit_ty(output); } - } - - // Second, if there was exactly one lifetime (either a substitution or a - // reference) in the arguments, then any anonymous regions in the output - // have that lifetime. - let mut possible_implied_output_region = None; - let mut lifetime_count = 0; - for input in inputs.iter().skip(has_self as usize) { - let mut gather = GatherLifetimes { - map: self.map, - outer_index: ty::INNERMOST, - have_bound_regions: false, - lifetimes: Default::default(), - }; - gather.visit_ty(input); - - lifetime_count += gather.lifetimes.len(); - - if lifetime_count == 1 && gather.lifetimes.len() == 1 { - // there's a chance that the unique lifetime of this - // iteration will be the appropriate lifetime for output - // parameters, so lets store it. - possible_implied_output_region = gather.lifetimes.iter().cloned().next(); - } - } - - let elide = if lifetime_count == 1 { - Elide::Exact(possible_implied_output_region.unwrap()) - } else { - Elide::Error - }; - - debug!(?elide); - - let scope = Scope::Elision { elide, s: self.scope }; - self.with(scope, |this| this.visit_ty(output)); - - struct GatherLifetimes<'a> { - map: &'a NamedRegionMap, - outer_index: ty::DebruijnIndex, - have_bound_regions: bool, - lifetimes: FxHashSet, - } - - impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> { - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - if let hir::TyKind::BareFn(_) = ty.kind { - self.outer_index.shift_in(1); - } - match ty.kind { - hir::TyKind::TraitObject(bounds, ref lifetime, _) => { - for bound in bounds { - self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); - } - - // Stay on the safe side and don't include the object - // lifetime default (which may not end up being used). - if !lifetime.is_elided() { - self.visit_lifetime(lifetime); - } - } - _ => { - intravisit::walk_ty(self, ty); - } - } - if let hir::TyKind::BareFn(_) = ty.kind { - self.outer_index.shift_out(1); - } - } - - fn visit_generic_param(&mut self, param: &hir::GenericParam<'_>) { - if let hir::GenericParamKind::Lifetime { .. } = param.kind { - // FIXME(eddyb) Do we want this? It only makes a difference - // if this `for<'a>` lifetime parameter is never used. - self.have_bound_regions = true; - } - - intravisit::walk_generic_param(self, param); - } - - fn visit_poly_trait_ref( - &mut self, - trait_ref: &hir::PolyTraitRef<'_>, - modifier: hir::TraitBoundModifier, - ) { - self.outer_index.shift_in(1); - intravisit::walk_poly_trait_ref(self, trait_ref, modifier); - self.outer_index.shift_out(1); - } - - fn visit_param_bound(&mut self, bound: &hir::GenericBound<'_>) { - if let hir::GenericBound::LangItemTrait { .. } = bound { - self.outer_index.shift_in(1); - intravisit::walk_param_bound(self, bound); - self.outer_index.shift_out(1); - } else { - intravisit::walk_param_bound(self, bound); - } - } - - fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { - if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - match lifetime { - Region::LateBound(debruijn, _, _) - | Region::LateBoundAnon(debruijn, _, _) - if debruijn < self.outer_index => - { - self.have_bound_regions = true; - } - _ => { - // FIXME(jackh726): nested trait refs? - self.lifetimes.insert(lifetime.shifted_out_to_binder(self.outer_index)); - } - } - } - } - } - - struct GatherAnonLifetimes { - anon_count: u32, - } - impl<'v> Visitor<'v> for GatherAnonLifetimes { - #[instrument(skip(self), level = "trace")] - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - // If we enter a `BareFn`, then we enter a *new* binding scope - if let hir::TyKind::BareFn(_) = ty.kind { - return; - } - intravisit::walk_ty(self, ty); - } - - fn visit_generic_args( - &mut self, - path_span: Span, - generic_args: &'v hir::GenericArgs<'v>, - ) { - // parenthesized args enter a new elision scope - if generic_args.parenthesized { - return; - } - intravisit::walk_generic_args(self, path_span, generic_args) - } - - #[instrument(skip(self), level = "trace")] - fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { - if lifetime_ref.is_elided() { - self.anon_count += 1; - } - } - } - } - - fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) { - debug!("resolve_elided_lifetimes(lifetime_refs={:?})", lifetime_refs); - - if lifetime_refs.is_empty() { - return; - } - - let mut late_depth = 0; - let mut scope = self.scope; - loop { - match *scope { - // Do not assign any resolution, it will be inferred. - Scope::Body { .. } => return, - - Scope::Root => break, - - Scope::Binder { s, scope_type, .. } => { - match scope_type { - BinderScopeType::Normal => late_depth += 1, - BinderScopeType::Concatenating => {} - } - scope = s; - } - - Scope::Elision { - elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter), - .. - } => { - for lifetime_ref in lifetime_refs { - let lifetime = - Region::late_anon(named_late_bound_vars, counter).shifted(late_depth); - - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - - Scope::Elision { elide: Elide::Exact(l), .. } => { - let lifetime = l.shifted(late_depth); - for lifetime_ref in lifetime_refs { - self.insert_lifetime(lifetime_ref, lifetime); - } - return; - } - - Scope::Elision { elide: Elide::Error, .. } - | Scope::Elision { elide: Elide::Forbid, .. } => break, - - Scope::ObjectLifetimeDefault { s, .. } - | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { - scope = s; - } - } - } - - for lt in lifetime_refs { - self.tcx.sess.delay_span_bug(lt.span, "Missing lifetime specifier"); + }); + if in_closure && let Some(output) = output { + self.visit_ty(output); } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 55307b9cebb70..fe9ef6045415e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -948,6 +948,8 @@ fn default_configuration(sess: &Session) -> CrateConfig { if sess.opts.debug_assertions { ret.insert((sym::debug_assertions, None)); } + // JUSTIFICATION: before wrapper fn is available + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] if sess.opts.crate_types.contains(&CrateType::ProcMacro) { ret.insert((sym::proc_macro, None)); } @@ -2196,6 +2198,8 @@ fn parse_remap_path_prefix( mapping } +// JUSTIFICATION: before wrapper fn is available +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session_options(matches: &getopts::Matches) -> Options { let color = parse_color(matches); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 28e2e0db89a1f..501997679f4bf 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -102,28 +102,6 @@ macro_rules! top_level_options { ); } -impl Options { - pub fn mir_opt_level(&self) -> usize { - self.unstable_opts - .mir_opt_level - .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 }) - } - - pub fn instrument_coverage(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedGenerics - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) - == InstrumentCoverage::ExceptUnusedFunctions - } -} - top_level_options!( /// The top-level command-line options struct. /// @@ -149,9 +127,11 @@ top_level_options!( /// `CodegenOptions`, think about how it influences incremental compilation. If in /// doubt, specify `[TRACKED]`, which is always "correct" but might lead to /// unnecessary re-compilation. + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] pub struct Options { /// The crate config requested for the session, which may be combined /// with additional crate configurations during the compile process. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field"))] crate_types: Vec [TRACKED], optimize: OptLevel [TRACKED], /// Include the `debug_assertions` flag in dependency tracking, since it @@ -198,7 +178,9 @@ top_level_options!( /// what rustc was invoked with, but massaged a bit to agree with /// commands like `--emit llvm-ir` which they're often incompatible with /// if we otherwise use the defaults of rustc. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::codegen_units` instead of this field"))] cli_forced_codegen_units: Option [UNTRACKED], + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] cli_forced_thinlto_off: bool [UNTRACKED], /// Remap source path prefixes in all output (messages, object files, debug, etc.). @@ -249,11 +231,12 @@ macro_rules! options { ),* ,) => ( #[derive(Clone)] - pub struct $struct_name { $(pub $opt: $t),* } + #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)] + pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } impl Default for $struct_name { fn default() -> $struct_name { - $struct_name { $( $( #[$attr] )* $opt: $init),* } + $struct_name { $($opt: $init),* } } } @@ -297,6 +280,22 @@ macro_rules! options { ) } +impl Options { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn time_passes(&self) -> bool { + self.unstable_opts.time_passes || self.unstable_opts.time + } +} + +impl CodegenOptions { + // JUSTIFICATION: defn of the suggested wrapper fn + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + pub fn instrument_coverage(&self) -> InstrumentCoverage { + self.instrument_coverage.unwrap_or(InstrumentCoverage::Off) + } +} + // Sometimes different options need to build a common structure. // That structure can be kept in one of the options' fields, the others become dummy. macro_rules! redirect_field { @@ -1076,6 +1075,7 @@ options! { ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field"))] code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), codegen_units: Option = (None, parse_opt_number, [UNTRACKED], @@ -1095,12 +1095,14 @@ options! { "extra data to put in each output filename"), force_frame_pointers: Option = (None, parse_opt_bool, [TRACKED], "force use of the frame pointers"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field"))] force_unwind_tables: Option = (None, parse_opt_bool, [TRACKED], "force use of unwind tables"), incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), inline_threshold: Option = (None, parse_opt_number, [TRACKED], "set the threshold for inlining a function"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1113,6 +1115,7 @@ options! { "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec = (Vec::new(), parse_list, [UNTRACKED], "extra arguments to append to the linker invocation (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field"))] link_dead_code: Option = (None, parse_opt_bool, [TRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), link_self_contained: Option = (None, parse_opt_bool, [UNTRACKED], @@ -1127,6 +1130,7 @@ options! { "generate build artifacts that are compatible with linker-based LTO"), llvm_args: Vec = (Vec::new(), parse_list, [TRACKED], "a list of arguments to pass to LLVM (space separated)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED], "perform LLVM link-time optimizations"), metadata: Vec = (Vec::new(), parse_list, [TRACKED], @@ -1143,8 +1147,10 @@ options! { "disable LLVM's SLP vectorization pass"), opt_level: String = ("0".to_string(), parse_string, [TRACKED], "optimization level (0-3, s, or z; default: 0)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field"))] overflow_checks: Option = (None, parse_opt_bool, [TRACKED], "use overflow checks for integer arithmetic"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field"))] panic: Option = (None, parse_opt_panic_strategy, [TRACKED], "panic strategy to compile crate with"), passes: Vec = (Vec::new(), parse_list, [TRACKED], @@ -1156,6 +1162,7 @@ options! { "compile the program with profiling instrumentation"), profile_use: Option = (None, parse_opt_pathbuf, [TRACKED], "use the given `.profdata` file for profile-guided optimization"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field"))] relocation_model: Option = (None, parse_relocation_model, [TRACKED], "control generation of position-independent code (PIC) \ (`rustc --print relocation-models` for details)"), @@ -1167,6 +1174,7 @@ options! { "save all temporary output files during compilation (default: no)"), soft_float: bool = (false, parse_bool, [TRACKED], "use soft float ABI (*eabihf targets only) (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field"))] split_debuginfo: Option = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], @@ -1202,11 +1210,13 @@ options! { "encode MIR of all functions into the crate metadata (default: no)"), assume_incomplete_release: bool = (false, parse_bool, [TRACKED], "make cfg(version) treat the current version as incomplete (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field"))] asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), assert_incr_state: Option = (None, parse_opt_string, [UNTRACKED], "assert that the incremental cache is in given state: \ either `loaded` or `not-loaded`."), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field"))] binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ (default: no)"), @@ -1284,6 +1294,7 @@ options! { "emit the bc module with thin LTO info (default: yes)"), export_executable_symbols: bool = (false, parse_bool, [TRACKED], "export symbols from executables, as if they were dynamic libraries"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field"))] fewer_names: Option = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), @@ -1326,6 +1337,7 @@ options! { "control whether `#[inline]` functions are in all CGUs"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))] instrument_coverage: Option = (None, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage \ reports (note, the compiler build config must include `profiler = true`); \ @@ -1334,6 +1346,7 @@ options! { `=except-unused-generics` `=except-unused-functions` `=off` (default)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_mcount` instead of this field"))] instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], @@ -1356,6 +1369,7 @@ options! { merge_functions: Option = (None, parse_merge_functions, [TRACKED], "control the operation of the MergeFunctions LLVM pass, taking \ the same values as the target option of the same name"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::meta_stats` instead of this field"))] meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], @@ -1365,6 +1379,7 @@ options! { "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \ enabled, overriding all other checks. Passes that are not specified are enabled or \ disabled by other flags as usual."), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field"))] mir_opt_level: Option = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), move_size_limit: Option = (None, parse_opt_number, [TRACKED], @@ -1431,6 +1446,7 @@ options! { See #77382 and #74551."), print_fuel: Option = (None, parse_opt_string, [TRACKED], "make rustc print the total optimization fuel used by a crate"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::print_llvm_passes` instead of this field"))] print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "print the LLVM optimization passes being run (default: no)"), print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], @@ -1505,6 +1521,7 @@ options! { "exclude spans when debug-printing compiler state (default: no)"), src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))] stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED], "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"), strict_init_checks: bool = (false, parse_bool, [TRACKED], @@ -1525,6 +1542,7 @@ options! { symbol_mangling_version: Option = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field"))] teach: bool = (false, parse_bool, [TRACKED], "show extended diagnostic help (default: no)"), temps_dir: Option = (None, parse_opt_string, [UNTRACKED], @@ -1540,6 +1558,7 @@ options! { "emit directionality isolation markers in translated diagnostics"), tune_cpu: Option = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))] thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), thir_unsafeck: bool = (false, parse_bool, [TRACKED], @@ -1548,14 +1567,19 @@ options! { /// a sequential compiler for now. This'll likely be adjusted /// in the future. Note that -Zthreads=0 is the way to get /// the num_cpus behavior. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field"))] threads: usize = (1, parse_threads, [UNTRACKED], "use a thread pool with N threads"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time: bool = (false, parse_bool, [UNTRACKED], "measure time of rustc processes (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_llvm_passes` instead of this field"))] time_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each LLVM pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))] time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field"))] tls_model: Option = (None, parse_tls_model, [TRACKED], "choose the TLS model to use (`rustc --print tls-models` for details)"), trace_macros: bool = (false, parse_bool, [UNTRACKED], @@ -1590,14 +1614,17 @@ options! { "enable unsound and buggy MIR optimizations (default: no)"), /// This name is kind of confusing: Most unstable options enable something themselves, while /// this just allows "normal" options to be feature-gated. + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field"))] unstable_options: bool = (false, parse_bool, [UNTRACKED], "adds unstable command line options to rustc interface (default: no)"), use_ctors_section: Option = (None, parse_opt_bool, [TRACKED], "use legacy .ctors section for initializers rather than .init_array"), validate_mir: bool = (false, parse_bool, [UNTRACKED], "validate MIR after each transformation"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verbose` instead of this field"))] verbose: bool = (false, parse_bool, [UNTRACKED], "in general, enable more debug printouts (default: no)"), + #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field"))] verify_llvm_ir: bool = (false, parse_bool, [TRACKED], "verify LLVM IR (default: no)"), virtual_function_elimination: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 854cad79a2040..9669287b3f370 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,7 +1,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; -use crate::config::{self, CrateType, OutputType, SwitchWithOptPath}; +use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; use crate::{filesearch, lint}; @@ -583,50 +583,356 @@ impl Session { pub fn source_map(&self) -> &SourceMap { self.parse_sess.source_map() } + + pub fn time_passes(&self) -> bool { + self.opts.time_passes() + } + + /// Returns `true` if internal lints should be added to the lint store - i.e. if + /// `-Zunstable-options` is provided and this isn't rustdoc (internal lints can trigger errors + /// to be emitted under rustdoc). + pub fn enable_internal_lints(&self) -> bool { + self.unstable_options() && !self.opts.actually_rustdoc + } + + pub fn instrument_coverage(&self) -> bool { + self.opts.cg.instrument_coverage() != InstrumentCoverage::Off + } + + pub fn instrument_coverage_except_unused_generics(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics + } + + pub fn instrument_coverage_except_unused_functions(&self) -> bool { + self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions + } + + /// Gets the features enabled for the current compilation session. + /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents + /// dependency tracking. Use tcx.features() instead. + #[inline] + pub fn features_untracked(&self) -> &rustc_feature::Features { + self.features.get().unwrap() + } + + pub fn init_features(&self, features: rustc_feature::Features) { + match self.features.set(features) { + Ok(()) => {} + Err(_) => panic!("`features` was initialized twice"), + } + } + + pub fn is_sanitizer_cfi_enabled(&self) -> bool { + self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) + } + + /// Check whether this compile session and crate type use static crt. + pub fn crt_static(&self, crate_type: Option) -> bool { + if !self.target.crt_static_respected { + // If the target does not opt in to crt-static support, use its default. + return self.target.crt_static_default; + } + + let requested_features = self.opts.cg.target_feature.split(','); + let found_negative = requested_features.clone().any(|r| r == "-crt-static"); + let found_positive = requested_features.clone().any(|r| r == "+crt-static"); + + // JUSTIFICATION: necessary use of crate_types directly (see FIXME below) + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] + if found_positive || found_negative { + found_positive + } else if crate_type == Some(CrateType::ProcMacro) + || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro) + { + // FIXME: When crate_type is not available, + // we use compiler options to determine the crate_type. + // We can't check `#![crate_type = "proc-macro"]` here. + false + } else { + self.target.crt_static_default + } + } + + pub fn is_wasi_reactor(&self) -> bool { + self.target.options.os == "wasi" + && matches!( + self.opts.unstable_opts.wasi_exec_model, + Some(config::WasiExecModel::Reactor) + ) + } + + pub fn target_can_use_split_dwarf(&self) -> bool { + !self.target.is_like_windows && !self.target.is_like_osx + } + + pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String { + format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64()) + } + + pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + self.opts.target_triple.triple(), + &self.opts.search_paths, + &self.target_tlib_path, + kind, + ) + } + pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { + filesearch::FileSearch::new( + &self.sysroot, + config::host_triple(), + &self.opts.search_paths, + &self.host_tlib_path, + kind, + ) + } + + /// Returns a list of directories where target-specific tool binaries are located. + pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec { + let rustlib_path = rustc_target::target_rustlib_path(&self.sysroot, &config::host_triple()); + let p = PathBuf::from_iter([ + Path::new(&self.sysroot), + Path::new(&rustlib_path), + Path::new("bin"), + ]); + if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } + } + + pub fn init_incr_comp_session( + &self, + session_dir: PathBuf, + lock_file: flock::Lock, + load_dep_graph: bool, + ) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::NotInitialized = *incr_comp_session { + } else { + panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session) + } + + *incr_comp_session = + IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph }; + } + + pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + if let IncrCompSession::Active { .. } = *incr_comp_session { + } else { + panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session); + } + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::Finalized { session_directory: new_directory_path }; + } + + pub fn mark_incr_comp_session_as_invalid(&self) { + let mut incr_comp_session = self.incr_comp_session.borrow_mut(); + + let session_directory = match *incr_comp_session { + IncrCompSession::Active { ref session_directory, .. } => session_directory.clone(), + IncrCompSession::InvalidBecauseOfErrors { .. } => return, + _ => panic!("trying to invalidate `IncrCompSession` `{:?}`", *incr_comp_session), + }; + + // Note: this will also drop the lock file, thus unlocking the directory. + *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory }; + } + + pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { + let incr_comp_session = self.incr_comp_session.borrow(); + cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { + IncrCompSession::NotInitialized => panic!( + "trying to get session directory from `IncrCompSession`: {:?}", + *incr_comp_session, + ), + IncrCompSession::Active { ref session_directory, .. } + | IncrCompSession::Finalized { ref session_directory } + | IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => { + session_directory + } + }) + } + + pub fn incr_comp_session_dir_opt(&self) -> Option> { + self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) + } + + pub fn print_perf_stats(&self) { + eprintln!( + "Total time spent computing symbol hashes: {}", + duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) + ); + eprintln!( + "Total queries canonicalized: {}", + self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) + ); + eprintln!( + "normalize_generic_arg_after_erasing_regions: {}", + self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) + ); + eprintln!( + "normalize_projection_ty: {}", + self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed) + ); + } + + /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. + /// This expends fuel if applicable, and records fuel if applicable. + pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { + let mut ret = true; + if let Some((ref c, _)) = self.opts.unstable_opts.fuel { + if c == crate_name { + assert_eq!(self.threads(), 1); + let mut fuel = self.optimization_fuel.lock(); + ret = fuel.remaining != 0; + if fuel.remaining == 0 && !fuel.out_of_fuel { + if self.diagnostic().can_emit_warnings() { + // We only call `msg` in case we can actually emit warnings. + // Otherwise, this could cause a `delay_good_path_bug` to + // trigger (issue #79546). + self.warn(&format!("optimization-fuel-exhausted: {}", msg())); + } + fuel.out_of_fuel = true; + } else if fuel.remaining > 0 { + fuel.remaining -= 1; + } + } + } + if let Some(ref c) = self.opts.unstable_opts.print_fuel { + if c == crate_name { + assert_eq!(self.threads(), 1); + self.print_fuel.fetch_add(1, SeqCst); + } + } + ret + } + + pub fn rust_2015(&self) -> bool { + self.edition() == Edition::Edition2015 + } + + /// Are we allowed to use features from the Rust 2018 edition? + pub fn rust_2018(&self) -> bool { + self.edition() >= Edition::Edition2018 + } + + /// Are we allowed to use features from the Rust 2021 edition? + pub fn rust_2021(&self) -> bool { + self.edition() >= Edition::Edition2021 + } + + /// Are we allowed to use features from the Rust 2024 edition? + pub fn rust_2024(&self) -> bool { + self.edition() >= Edition::Edition2024 + } + + /// Returns `true` if we cannot skip the PLT for shared library calls. + pub fn needs_plt(&self) -> bool { + // Check if the current target usually needs PLT to be enabled. + // The user can use the command line flag to override it. + let needs_plt = self.target.needs_plt; + + let dbg_opts = &self.opts.unstable_opts; + + let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); + + // Only enable this optimization by default if full relro is also enabled. + // In this case, lazy binding was already unavailable, so nothing is lost. + // This also ensures `-Wl,-z,now` is supported by the linker. + let full_relro = RelroLevel::Full == relro_level; + + // If user didn't explicitly forced us to use / skip the PLT, + // then try to skip it where possible. + dbg_opts.plt.unwrap_or(needs_plt || !full_relro) + } + + /// Checks if LLVM lifetime markers should be emitted. + pub fn emit_lifetime_markers(&self) -> bool { + self.opts.optimize != config::OptLevel::No + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. + || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) + } + + pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| attr.has_name(*kind)) + } + + pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|item| item.has_name(name)) + } + + pub fn find_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> Option<&'a Attribute> { + attrs.iter().find(|attr| attr.has_name(name)) + } + + pub fn filter_by_name<'a>( + &'a self, + attrs: &'a [Attribute], + name: Symbol, + ) -> impl Iterator { + attrs.iter().filter(move |attr| attr.has_name(name)) + } + + pub fn first_attr_value_str_by_name( + &self, + attrs: &[Attribute], + name: Symbol, + ) -> Option { + attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) + } +} + +// JUSTIFICATION: defn of the suggested wrapper fns +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] +impl Session { pub fn verbose(&self) -> bool { self.opts.unstable_opts.verbose } - pub fn time_passes(&self) -> bool { - self.opts.unstable_opts.time_passes || self.opts.unstable_opts.time - } + pub fn instrument_mcount(&self) -> bool { self.opts.unstable_opts.instrument_mcount } + pub fn time_llvm_passes(&self) -> bool { self.opts.unstable_opts.time_llvm_passes } + pub fn meta_stats(&self) -> bool { self.opts.unstable_opts.meta_stats } + pub fn asm_comments(&self) -> bool { self.opts.unstable_opts.asm_comments } + pub fn verify_llvm_ir(&self) -> bool { self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() } + pub fn print_llvm_passes(&self) -> bool { self.opts.unstable_opts.print_llvm_passes } + pub fn binary_dep_depinfo(&self) -> bool { self.opts.unstable_opts.binary_dep_depinfo } - pub fn mir_opt_level(&self) -> usize { - self.opts.mir_opt_level() - } - - /// Gets the features enabled for the current compilation session. - /// DO NOT USE THIS METHOD if there is a TyCtxt available, as it circumvents - /// dependency tracking. Use tcx.features() instead. - #[inline] - pub fn features_untracked(&self) -> &rustc_feature::Features { - self.features.get().unwrap() - } - pub fn init_features(&self, features: rustc_feature::Features) { - match self.features.set(features) { - Ok(()) => {} - Err(_) => panic!("`features` was initialized twice"), - } + pub fn mir_opt_level(&self) -> usize { + self.opts + .unstable_opts + .mir_opt_level + .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 }) } /// Calculates the flavor of LTO to use for this compilation. @@ -702,6 +1008,7 @@ impl Session { pub fn panic_strategy(&self) -> PanicStrategy { self.opts.cg.panic.unwrap_or(self.target.panic_strategy) } + pub fn fewer_names(&self) -> bool { if let Some(fewer_names) = self.opts.unstable_opts.fewer_names { fewer_names @@ -713,43 +1020,17 @@ impl Session { !more_names } } - - pub fn unstable_options(&self) -> bool { - self.opts.unstable_opts.unstable_options - } - pub fn is_nightly_build(&self) -> bool { - self.opts.unstable_features.is_nightly_build() - } - pub fn is_sanitizer_cfi_enabled(&self) -> bool { - self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) - } - pub fn overflow_checks(&self) -> bool { - self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) - } - - /// Check whether this compile session and crate type use static crt. - pub fn crt_static(&self, crate_type: Option) -> bool { - if !self.target.crt_static_respected { - // If the target does not opt in to crt-static support, use its default. - return self.target.crt_static_default; - } - - let requested_features = self.opts.cg.target_feature.split(','); - let found_negative = requested_features.clone().any(|r| r == "-crt-static"); - let found_positive = requested_features.clone().any(|r| r == "+crt-static"); - - if found_positive || found_negative { - found_positive - } else if crate_type == Some(CrateType::ProcMacro) - || crate_type == None && self.opts.crate_types.contains(&CrateType::ProcMacro) - { - // FIXME: When crate_type is not available, - // we use compiler options to determine the crate_type. - // We can't check `#![crate_type = "proc-macro"]` here. - false - } else { - self.target.crt_static_default - } + + pub fn unstable_options(&self) -> bool { + self.opts.unstable_opts.unstable_options + } + + pub fn is_nightly_build(&self) -> bool { + self.opts.unstable_features.is_nightly_build() + } + + pub fn overflow_checks(&self) -> bool { + self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions) } pub fn relocation_model(&self) -> RelocModel { @@ -764,14 +1045,6 @@ impl Session { self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model) } - pub fn is_wasi_reactor(&self) -> bool { - self.target.options.os == "wasi" - && matches!( - self.opts.unstable_opts.wasi_exec_model, - Some(config::WasiExecModel::Reactor) - ) - } - pub fn split_debuginfo(&self) -> SplitDebuginfo { self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) } @@ -784,10 +1057,6 @@ impl Session { } } - pub fn target_can_use_split_dwarf(&self) -> bool { - !self.target.is_like_windows && !self.target.is_like_osx - } - pub fn must_emit_unwind_tables(&self) -> bool { // This is used to control the emission of the `uwtable` attribute on // LLVM functions. @@ -815,151 +1084,6 @@ impl Session { ) } - pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String { - format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64()) - } - - pub fn target_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new( - &self.sysroot, - self.opts.target_triple.triple(), - &self.opts.search_paths, - &self.target_tlib_path, - kind, - ) - } - pub fn host_filesearch(&self, kind: PathKind) -> filesearch::FileSearch<'_> { - filesearch::FileSearch::new( - &self.sysroot, - config::host_triple(), - &self.opts.search_paths, - &self.host_tlib_path, - kind, - ) - } - - /// Returns a list of directories where target-specific tool binaries are located. - pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec { - let rustlib_path = rustc_target::target_rustlib_path(&self.sysroot, &config::host_triple()); - let p = PathBuf::from_iter([ - Path::new(&self.sysroot), - Path::new(&rustlib_path), - Path::new("bin"), - ]); - if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] } - } - - pub fn init_incr_comp_session( - &self, - session_dir: PathBuf, - lock_file: flock::Lock, - load_dep_graph: bool, - ) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::NotInitialized = *incr_comp_session { - } else { - panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session) - } - - *incr_comp_session = - IncrCompSession::Active { session_directory: session_dir, lock_file, load_dep_graph }; - } - - pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::Active { .. } = *incr_comp_session { - } else { - panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session); - } - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::Finalized { session_directory: new_directory_path }; - } - - pub fn mark_incr_comp_session_as_invalid(&self) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - let session_directory = match *incr_comp_session { - IncrCompSession::Active { ref session_directory, .. } => session_directory.clone(), - IncrCompSession::InvalidBecauseOfErrors { .. } => return, - _ => panic!("trying to invalidate `IncrCompSession` `{:?}`", *incr_comp_session), - }; - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::InvalidBecauseOfErrors { session_directory }; - } - - pub fn incr_comp_session_dir(&self) -> cell::Ref<'_, PathBuf> { - let incr_comp_session = self.incr_comp_session.borrow(); - cell::Ref::map(incr_comp_session, |incr_comp_session| match *incr_comp_session { - IncrCompSession::NotInitialized => panic!( - "trying to get session directory from `IncrCompSession`: {:?}", - *incr_comp_session, - ), - IncrCompSession::Active { ref session_directory, .. } - | IncrCompSession::Finalized { ref session_directory } - | IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => { - session_directory - } - }) - } - - pub fn incr_comp_session_dir_opt(&self) -> Option> { - self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) - } - - pub fn print_perf_stats(&self) { - eprintln!( - "Total time spent computing symbol hashes: {}", - duration_to_secs_str(*self.perf_stats.symbol_hash_time.lock()) - ); - eprintln!( - "Total queries canonicalized: {}", - self.perf_stats.queries_canonicalized.load(Ordering::Relaxed) - ); - eprintln!( - "normalize_generic_arg_after_erasing_regions: {}", - self.perf_stats.normalize_generic_arg_after_erasing_regions.load(Ordering::Relaxed) - ); - eprintln!( - "normalize_projection_ty: {}", - self.perf_stats.normalize_projection_ty.load(Ordering::Relaxed) - ); - } - - /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. - /// This expends fuel if applicable, and records fuel if applicable. - pub fn consider_optimizing String>(&self, crate_name: &str, msg: T) -> bool { - let mut ret = true; - if let Some((ref c, _)) = self.opts.unstable_opts.fuel { - if c == crate_name { - assert_eq!(self.threads(), 1); - let mut fuel = self.optimization_fuel.lock(); - ret = fuel.remaining != 0; - if fuel.remaining == 0 && !fuel.out_of_fuel { - if self.diagnostic().can_emit_warnings() { - // We only call `msg` in case we can actually emit warnings. - // Otherwise, this could cause a `delay_good_path_bug` to - // trigger (issue #79546). - self.warn(&format!("optimization-fuel-exhausted: {}", msg())); - } - fuel.out_of_fuel = true; - } else if fuel.remaining > 0 { - fuel.remaining -= 1; - } - } - } - if let Some(ref c) = self.opts.unstable_opts.print_fuel { - if c == crate_name { - assert_eq!(self.threads(), 1); - self.print_fuel.fetch_add(1, SeqCst); - } - } - ret - } - /// Returns the number of query threads that should be used for this /// compilation pub fn threads(&self) -> usize { @@ -1040,109 +1164,17 @@ impl Session { self.opts.unstable_opts.teach && self.diagnostic().must_teach(code) } - pub fn rust_2015(&self) -> bool { - self.opts.edition == Edition::Edition2015 - } - - /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.opts.edition >= Edition::Edition2018 - } - - /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(&self) -> bool { - self.opts.edition >= Edition::Edition2021 - } - - /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(&self) -> bool { - self.opts.edition >= Edition::Edition2024 - } - pub fn edition(&self) -> Edition { self.opts.edition } - /// Returns `true` if we cannot skip the PLT for shared library calls. - pub fn needs_plt(&self) -> bool { - // Check if the current target usually needs PLT to be enabled. - // The user can use the command line flag to override it. - let needs_plt = self.target.needs_plt; - - let dbg_opts = &self.opts.unstable_opts; - - let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); - - // Only enable this optimization by default if full relro is also enabled. - // In this case, lazy binding was already unavailable, so nothing is lost. - // This also ensures `-Wl,-z,now` is supported by the linker. - let full_relro = RelroLevel::Full == relro_level; - - // If user didn't explicitly forced us to use / skip the PLT, - // then try to skip it where possible. - dbg_opts.plt.unwrap_or(needs_plt || !full_relro) - } - - /// Checks if LLVM lifetime markers should be emitted. - pub fn emit_lifetime_markers(&self) -> bool { - self.opts.optimize != config::OptLevel::No - // AddressSanitizer uses lifetimes to detect use after scope bugs. - // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. - // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future. - || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS) - } - pub fn link_dead_code(&self) -> bool { self.opts.cg.link_dead_code.unwrap_or(false) } - - pub fn instrument_coverage(&self) -> bool { - self.opts.instrument_coverage() - } - - pub fn instrument_coverage_except_unused_generics(&self) -> bool { - self.opts.instrument_coverage_except_unused_generics() - } - - pub fn instrument_coverage_except_unused_functions(&self) -> bool { - self.opts.instrument_coverage_except_unused_functions() - } - - pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| attr.has_name(*kind)) - } - - pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool { - attrs.iter().any(|item| item.has_name(name)) - } - - pub fn find_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> Option<&'a Attribute> { - attrs.iter().find(|attr| attr.has_name(name)) - } - - pub fn filter_by_name<'a>( - &'a self, - attrs: &'a [Attribute], - name: Symbol, - ) -> impl Iterator { - attrs.iter().filter(move |attr| attr.has_name(name)) - } - - pub fn first_attr_value_str_by_name( - &self, - attrs: &[Attribute], - name: Symbol, - ) -> Option { - attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str()) - } } +// JUSTIFICATION: part of session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, @@ -1227,6 +1259,8 @@ pub enum DiagnosticOutput { Raw(Box), } +// JUSTIFICATION: literally session construction +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] pub fn build_session( sopts: config::Options, local_crate_source_file: Option, @@ -1348,11 +1382,8 @@ pub fn build_session( CguReuseTracker::new_disabled() }; - let prof = SelfProfilerRef::new( - self_profiler, - sopts.unstable_opts.time_passes || sopts.unstable_opts.time, - sopts.unstable_opts.time_passes, - ); + let prof = + SelfProfilerRef::new(self_profiler, sopts.time_passes(), sopts.unstable_opts.time_passes); let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") { Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate, @@ -1401,8 +1432,12 @@ pub fn build_session( sess } -// If it is useful to have a Session available already for validating a -// commandline argument, you can do so here. +/// Validate command line arguments with a `Session`. +/// +/// If it is useful to have a Session available already for validating a commandline argument, you +/// can do so here. +// JUSTIFICATION: needs to access args to validate them +#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn validate_commandline_args_with_session_available(sess: &Session) { // Since we don't know if code in an rlib will be linked to statically or // dynamically downstream, rustc generates `__imp_` symbols that help linkers diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c75b6772487f9..060e7a7b90aee 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1238,6 +1238,8 @@ symbols! { rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, rustc_lint_diagnostics, + rustc_lint_opt_deny_field_access, + rustc_lint_opt_ty, rustc_lint_query_instability, rustc_macro_transparency, rustc_main, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 3ef51b0c27abd..d0a17f712d3df 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -60,8 +60,9 @@ pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; -pub use self::structural_match::search_for_structural_match_violation; -pub use self::structural_match::{NonStructuralMatchTy, NonStructuralMatchTyKind}; +pub use self::structural_match::{ + search_for_adt_const_param_violation, search_for_structural_match_violation, +}; pub use self::util::{ elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span, elaborate_trait_ref, elaborate_trait_refs, diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 6c0b83fbd0304..c278752e3d9f4 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -6,29 +6,10 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; use std::ops::ControlFlow; -#[derive(Debug)] -pub struct NonStructuralMatchTy<'tcx> { - pub ty: Ty<'tcx>, - pub kind: NonStructuralMatchTyKind<'tcx>, -} - -#[derive(Debug)] -pub enum NonStructuralMatchTyKind<'tcx> { - Adt(AdtDef<'tcx>), - Param, - Dynamic, - Foreign, - Opaque, - Closure, - Generator, - Projection, - Float, -} - /// This method traverses the structure of `ty`, trying to find an /// instance of an ADT (i.e. struct or enum) that doesn't implement /// the structural-match traits, or a generic type parameter @@ -54,15 +35,28 @@ pub enum NonStructuralMatchTyKind<'tcx> { /// For more background on why Rust has this requirement, and issues /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. -/// -/// The floats_allowed flag is used to deny constants in floating point pub fn search_for_structural_match_violation<'tcx>( span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - floats_allowed: bool, -) -> Option> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed }) +) -> Option> { + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false }) + .break_value() +} + +/// This method traverses the structure of `ty`, trying to find any +/// types that are not allowed to be used in a const generic. +/// +/// This is either because the type does not implement `StructuralEq` +/// and `StructuralPartialEq`, or because the type is intentionally +/// not supported in const generics (such as floats and raw pointers, +/// which are allowed in match blocks). +pub fn search_for_adt_const_param_violation<'tcx>( + span: Span, + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option> { + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true }) .break_value() } @@ -125,7 +119,10 @@ struct Search<'tcx> { /// we will not recur on them again. seen: FxHashSet, - floats_allowed: bool, + // Additionally deny things that have been allowed in patterns, + // but are not allowed in adt const params, such as floats and + // fn ptrs. + adt_const_param: bool, } impl<'tcx> Search<'tcx> { @@ -135,7 +132,7 @@ impl<'tcx> Search<'tcx> { } impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { - type BreakTy = NonStructuralMatchTy<'tcx>; + type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { debug!("Search visiting ty: {:?}", ty); @@ -143,51 +140,27 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { let (adt_def, substs) = match *ty.kind() { ty::Adt(adt_def, substs) => (adt_def, substs), ty::Param(_) => { - let kind = NonStructuralMatchTyKind::Param; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Dynamic(..) => { - let kind = NonStructuralMatchTyKind::Dynamic; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Foreign(_) => { - let kind = NonStructuralMatchTyKind::Foreign; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Opaque(..) => { - let kind = NonStructuralMatchTyKind::Opaque; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Projection(..) => { - let kind = NonStructuralMatchTyKind::Projection; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Closure(..) => { - let kind = NonStructuralMatchTyKind::Closure; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } ty::Generator(..) | ty::GeneratorWitness(..) => { - let kind = NonStructuralMatchTyKind::Generator; - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); - } - ty::RawPtr(..) => { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::CONTINUE; + return ControlFlow::Break(ty); } - ty::FnDef(..) | ty::FnPtr(..) => { + ty::FnDef(..) => { // Types of formals and return in `fn(_) -> _` are also irrelevant; // so we do not recur into them via `super_visit_with` return ControlFlow::CONTINUE; @@ -206,14 +179,41 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { return ControlFlow::CONTINUE; } + ty::FnPtr(..) => { + if !self.adt_const_param { + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(ty); + } + } + + ty::RawPtr(..) => { + if !self.adt_const_param { + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(ty); + } + } + ty::Float(_) => { - if self.floats_allowed { + if !self.adt_const_param { return ControlFlow::CONTINUE; } else { - return ControlFlow::Break(NonStructuralMatchTy { - ty, - kind: NonStructuralMatchTyKind::Float, - }); + return ControlFlow::Break(ty); } } @@ -239,8 +239,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { if !self.type_marked_structural(ty) { debug!("Search found ty: {:?}", ty); - let kind = NonStructuralMatchTyKind::Adt(adt_def); - return ControlFlow::Break(NonStructuralMatchTy { ty, kind }); + return ControlFlow::Break(ty); } // structural-match does not care about the diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 58f4f02052f8b..08e8e6f7d0f40 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -221,14 +221,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.mk_region(ty::ReLateBound(debruijn, br)) } - Some(rl::Region::LateBoundAnon(debruijn, index, anon_index)) => { - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(index), - kind: ty::BrAnon(anon_index), - }; - tcx.mk_region(ty::ReLateBound(debruijn, br)) - } - Some(rl::Region::EarlyBound(index, id)) => { let name = lifetime_name(id.expect_local()); tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name })) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index e65955fe86038..bae986de9a23d 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -848,29 +848,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); if tcx.features().adt_const_params { - let err = match ty.peel_refs().kind() { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - }; - - if let Some(unsupported_type) = err { - tcx.sess.span_err( - hir_ty.span, - &format!( - "using {} as const generic parameters is forbidden", - unsupported_type - ), - ); - } - if let Some(non_structural_match_ty) = - traits::search_for_structural_match_violation(param.span, tcx, ty, false) + traits::search_for_adt_const_param_violation(param.span, tcx, ty) { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it // clearer. - match ty.peel_refs().kind() { + match non_structural_match_ty.kind() { ty::Param(_) => { // Const parameters may not have type parameters as their types, // because we cannot be sure that the type parameter derives `PartialEq` @@ -902,6 +886,24 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { .note("floats do not derive `Eq` or `Ord`, which are required for const parameters") .emit(); } + ty::FnPtr(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + .emit(); + } + ty::RawPtr(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "using raw pointers as const generic parameters is forbidden", + ) + .emit(); + } _ => { let mut diag = struct_span_err!( tcx.sess, @@ -909,10 +911,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { E0741, "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ the type of a const parameter", - non_structural_match_ty.ty, + non_structural_match_ty, ); - if ty == non_structural_match_ty.ty { + if ty == non_structural_match_ty { diag.span_label( hir_ty.span, format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 0e78c60ca5b6a..60c0694ca0e45 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1346,16 +1346,8 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option {} - Some( - rl::Region::LateBound(debruijn, _, _) - | rl::Region::LateBoundAnon(debruijn, _, _), - ) if debruijn < self.outer_index => {} - Some( - rl::Region::LateBound(..) - | rl::Region::LateBoundAnon(..) - | rl::Region::Free(..), - ) - | None => { + Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {} + Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => { self.has_late_bound_regions = Some(lt.span); } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index a1889e3d21cb8..76b4a534e5db5 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1628,14 +1628,21 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Chunks<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ChunksMut<'a, T: 'a> { - v: &'a mut [T], + /// # Safety + /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally, + /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot + /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing + /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw + /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap. + v: *mut [T], chunk_size: usize, + _marker: PhantomData<&'a mut T>, } impl<'a, T: 'a> ChunksMut<'a, T> { #[inline] pub(super) fn new(slice: &'a mut [T], size: usize) -> Self { - Self { v: slice, chunk_size: size } + Self { v: slice, chunk_size: size, _marker: PhantomData } } } @@ -1649,10 +1656,11 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { None } else { let sz = cmp::min(self.v.len(), self.chunk_size); - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(sz); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (head, tail) = unsafe { self.v.split_at_mut(sz) }; self.v = tail; - Some(head) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *head }) } } @@ -1684,11 +1692,13 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { Some(sum) => cmp::min(self.v.len(), sum), None => self.v.len(), }; - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(end); - let (_, nth) = head.split_at_mut(start); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (head, tail) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (_, nth) = unsafe { head.split_at_mut(start) }; self.v = tail; - Some(nth) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *nth }) } } @@ -1698,13 +1708,14 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { None } else { let start = (self.v.len() - 1) / self.chunk_size * self.chunk_size; - Some(&mut self.v[start..]) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *self.v.get_unchecked_mut(start..) }) } } unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let start = idx * self.chunk_size; - // SAFETY: see comments for `Chunks::__iterator_get_unchecked`. + // SAFETY: see comments for `Chunks::__iterator_get_unchecked` and `self.v`. // // Also note that the caller also guarantees that we're never called // with the same index again, and that no other methods that will @@ -1726,12 +1737,12 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { } else { let remainder = self.v.len() % self.chunk_size; let sz = if remainder != 0 { remainder } else { self.chunk_size }; - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); + let len = self.v.len(); // SAFETY: Similar to `Chunks::next_back` - let (head, tail) = unsafe { tmp.split_at_mut_unchecked(tmp_len - sz) }; + let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) }; self.v = head; - Some(tail) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *tail }) } } @@ -1747,10 +1758,13 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { Some(res) => cmp::min(self.v.len(), res), None => self.v.len(), }; - let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (head, nth_back) = temp.split_at_mut(start); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (temp, _tail) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (head, nth_back) = unsafe { temp.split_at_mut(start) }; self.v = head; - Some(nth_back) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *nth_back }) } } } @@ -1956,9 +1970,16 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for ChunksExact<'a, T> { #[stable(feature = "chunks_exact", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ChunksExactMut<'a, T: 'a> { - v: &'a mut [T], - rem: &'a mut [T], + /// # Safety + /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally, + /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot + /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing + /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw + /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap. + v: *mut [T], + rem: &'a mut [T], // The iterator never yields from here, so this can be unique chunk_size: usize, + _marker: PhantomData<&'a mut T>, } impl<'a, T> ChunksExactMut<'a, T> { @@ -1968,7 +1989,7 @@ impl<'a, T> ChunksExactMut<'a, T> { let fst_len = slice.len() - rem; // SAFETY: 0 <= fst_len <= slice.len() by construction above let (fst, snd) = unsafe { slice.split_at_mut_unchecked(fst_len) }; - Self { v: fst, rem: snd, chunk_size } + Self { v: fst, rem: snd, chunk_size, _marker: PhantomData } } /// Returns the remainder of the original slice that is not going to be @@ -1990,10 +2011,11 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(self.chunk_size); + // SAFETY: self.chunk_size is inbounds because we compared above against self.v.len() + let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; self.v = tail; - Some(head) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *head }) } } @@ -2015,8 +2037,8 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { self.v = &mut []; None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (_, snd) = tmp.split_at_mut(start); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (_, snd) = unsafe { self.v.split_at_mut(start) }; self.v = snd; self.next() } @@ -2029,7 +2051,7 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let start = idx * self.chunk_size; - // SAFETY: see comments for `ChunksMut::__iterator_get_unchecked`. + // SAFETY: see comments for `Chunks::__iterator_get_unchecked` and `self.v`. unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } } } @@ -2041,11 +2063,11 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); + // SAFETY: This subtraction is inbounds because of the check above + let (head, tail) = unsafe { self.v.split_at_mut(self.v.len() - self.chunk_size) }; self.v = head; - Some(tail) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *tail }) } } @@ -2058,10 +2080,13 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { } else { let start = (len - 1 - n) * self.chunk_size; let end = start + self.chunk_size; - let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (head, nth_back) = temp.split_at_mut(start); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (temp, _tail) = unsafe { mem::replace(&mut self.v, &mut []).split_at_mut(end) }; + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (head, nth_back) = unsafe { temp.split_at_mut(start) }; self.v = head; - Some(nth_back) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *nth_back }) } } } @@ -2645,14 +2670,21 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunks<'a, T> { #[stable(feature = "rchunks", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct RChunksMut<'a, T: 'a> { - v: &'a mut [T], + /// # Safety + /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally, + /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot + /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing + /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw + /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap. + v: *mut [T], chunk_size: usize, + _marker: PhantomData<&'a mut T>, } impl<'a, T: 'a> RChunksMut<'a, T> { #[inline] pub(super) fn new(slice: &'a mut [T], size: usize) -> Self { - Self { v: slice, chunk_size: size } + Self { v: slice, chunk_size: size, _marker: PhantomData } } } @@ -2666,16 +2698,16 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { None } else { let sz = cmp::min(self.v.len(), self.chunk_size); - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); + let len = self.v.len(); // SAFETY: split_at_mut_unchecked just requires the argument be less // than the length. This could only happen if the expression - // `tmp_len - sz` overflows. This could only happen if `sz > - // tmp_len`, which is impossible as we initialize it as the `min` of - // `self.v.len()` (e.g. `tmp_len`) and `self.chunk_size`. - let (head, tail) = unsafe { tmp.split_at_mut_unchecked(tmp_len - sz) }; + // `len - sz` overflows. This could only happen if `sz > + // len`, which is impossible as we initialize it as the `min` of + // `self.v.len()` (e.g. `len`) and `self.chunk_size`. + let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) }; self.v = head; - Some(tail) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *tail }) } } @@ -2709,11 +2741,15 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { Some(sum) => sum, None => 0, }; - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(start); - let (nth, _) = tail.split_at_mut(end - start); + // SAFETY: This type ensures that self.v is a valid pointer with a correct len. + // Therefore the bounds check in split_at_mut guarantess the split point is inbounds. + let (head, tail) = unsafe { self.v.split_at_mut(start) }; + // SAFETY: This type ensures that self.v is a valid pointer with a correct len. + // Therefore the bounds check in split_at_mut guarantess the split point is inbounds. + let (nth, _) = unsafe { tail.split_at_mut(end - start) }; self.v = head; - Some(nth) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *nth }) } } @@ -2724,7 +2760,8 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { } else { let rem = self.v.len() % self.chunk_size; let end = if rem == 0 { self.chunk_size } else { rem }; - Some(&mut self.v[0..end]) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *self.v.get_unchecked_mut(0..end) }) } } @@ -2735,7 +2772,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { Some(start) => start, }; // SAFETY: see comments for `RChunks::__iterator_get_unchecked` and - // `ChunksMut::__iterator_get_unchecked` + // `ChunksMut::__iterator_get_unchecked`, `self.v`. unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } } } @@ -2749,11 +2786,11 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { } else { let remainder = self.v.len() % self.chunk_size; let sz = if remainder != 0 { remainder } else { self.chunk_size }; - let tmp = mem::replace(&mut self.v, &mut []); // SAFETY: Similar to `Chunks::next_back` - let (head, tail) = unsafe { tmp.split_at_mut_unchecked(sz) }; + let (head, tail) = unsafe { self.v.split_at_mut_unchecked(sz) }; self.v = tail; - Some(head) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *head }) } } @@ -2768,10 +2805,13 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { let offset_from_end = (len - 1 - n) * self.chunk_size; let end = self.v.len() - offset_from_end; let start = end.saturating_sub(self.chunk_size); - let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (_, nth_back) = tmp.split_at_mut(start); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (tmp, tail) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (_, nth_back) = unsafe { tmp.split_at_mut(start) }; self.v = tail; - Some(nth_back) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *nth_back }) } } } @@ -2897,8 +2937,7 @@ impl<'a, T> Iterator for RChunksExact<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let end = self.v.len() - idx * self.chunk_size; let start = end - self.chunk_size; - // SAFETY: - // SAFETY: mostmy identical to `Chunks::__iterator_get_unchecked`. + // SAFETY: mostly identical to `Chunks::__iterator_get_unchecked`. unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } } } @@ -2981,7 +3020,13 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksExact<'a, T> { #[stable(feature = "rchunks", since = "1.31.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct RChunksExactMut<'a, T: 'a> { - v: &'a mut [T], + /// # Safety + /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally, + /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot + /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing + /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw + /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap. + v: *mut [T], rem: &'a mut [T], chunk_size: usize, } @@ -3014,11 +3059,12 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); + let len = self.v.len(); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (head, tail) = unsafe { self.v.split_at_mut(len - self.chunk_size) }; self.v = head; - Some(tail) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *tail }) } } @@ -3040,9 +3086,9 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { self.v = &mut []; None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let tmp_len = tmp.len(); - let (fst, _) = tmp.split_at_mut(tmp_len - end); + let len = self.v.len(); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (fst, _) = unsafe { self.v.split_at_mut(len - end) }; self.v = fst; self.next() } @@ -3056,7 +3102,7 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let end = self.v.len() - idx * self.chunk_size; let start = end - self.chunk_size; - // SAFETY: see comments for `RChunksMut::__iterator_get_unchecked`. + // SAFETY: see comments for `RChunksMut::__iterator_get_unchecked` and `self.v`. unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } } } @@ -3068,10 +3114,11 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { if self.v.len() < self.chunk_size { None } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(self.chunk_size); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; self.v = tail; - Some(head) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *head }) } } @@ -3087,10 +3134,13 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { let offset = (len - n) * self.chunk_size; let start = self.v.len() - offset; let end = start + self.chunk_size; - let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); - let (_, nth_back) = tmp.split_at_mut(start); + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (tmp, tail) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (_, nth_back) = unsafe { tmp.split_at_mut(start) }; self.v = tail; - Some(nth_back) + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *nth_back }) } } } diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 5751a91721d1f..6d1516958f39b 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -409,6 +409,50 @@ fn test_chunks_mut_zip() { assert_eq!(v1, [13, 14, 19, 20, 14]); } +#[test] +fn test_chunks_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.chunks_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); +} + +#[test] +fn test_chunks_exact_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.chunks_exact_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); +} + +#[test] +fn test_rchunks_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.rchunks_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [3, 4][..], &[6, 7][..])); +} + +#[test] +fn test_rchunks_exact_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.rchunks_exact_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [3, 4][..], &[6, 7][..])); +} + #[test] fn test_chunks_exact_count() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 025157713498e..aecc9aa879a69 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -167,7 +167,7 @@ h2, h3, h4 { h2, .top-doc .docblock > h3, .top-doc .docblock > h4 { - border-bottom: 1px solid; + border-bottom: 1px solid var(--headings-border-bottom-color); } h3.code-header { font-size: 1.125rem; /* 18px */ @@ -652,7 +652,7 @@ h2.location a { } .top-doc .docblock h2 { font-size: 1.375rem; } -.top-doc .docblock h3 { font-size: 1.25; } +.top-doc .docblock h3 { font-size: 1.25rem; } .top-doc .docblock h4, .top-doc .docblock h5 { font-size: 1.125rem; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 62d9eaa02e6a7..a550eb1c130af 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -42,10 +42,6 @@ input:focus + .slider { background-color: #0a042f !important; } -.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5, .docblock h6 { - border-bottom-color: #DDD; -} - .docblock table td, .docblock table th { border-color: #ddd; } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 7429f2b6ab148..3a96884d45d9f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -788,7 +788,7 @@ fn main_options(options: config::Options) -> MainResult { if sess.opts.describe_lints { let mut lint_store = rustc_lint::new_lint_store( sess.opts.unstable_opts.no_interleave_lints, - sess.unstable_options(), + sess.enable_internal_lints(), ); let registered_lints = if let Some(register_lints) = compiler.register_lints() { register_lints(sess, &mut lint_store); diff --git a/src/test/mir-opt/issue-99325.rs b/src/test/mir-opt/issue-99325.rs new file mode 100644 index 0000000000000..b79946ea8b56c --- /dev/null +++ b/src/test/mir-opt/issue-99325.rs @@ -0,0 +1,12 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +pub fn function_with_bytes() -> &'static [u8] { + BYTES +} + +// EMIT_MIR issue_99325.main.mir_map.0.mir +pub fn main() { + assert_eq!(function_with_bytes::(), &[0x41, 0x41, 0x41, 0x41]); + assert_eq!(function_with_bytes::<{ &[0x41, 0x41, 0x41, 0x41] }>(), b"AAAA"); +} diff --git a/src/test/mir-opt/issue_99325.main.mir_map.0.mir b/src/test/mir-opt/issue_99325.main.mir_map.0.mir new file mode 100644 index 0000000000000..175f244aa5dc9 --- /dev/null +++ b/src/test/mir-opt/issue_99325.main.mir_map.0.mir @@ -0,0 +1,295 @@ +// MIR for `main` 0 mir_map + +| User Type Annotations +| 0: user_ty: Canonical { max_universe: U0, variables: [], value: TypeOf(DefId(0:3 ~ issue_99325[8f58]::function_with_bytes), UserSubsts { substs: [Const { ty: &'static [u8; 4], kind: Value(Branch([Leaf(0x41), Leaf(0x41), Leaf(0x41), Leaf(0x41)])) }], user_self_ty: None }) }, span: $DIR/issue-99325.rs:10:16: 10:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { max_universe: U0, variables: [], value: TypeOf(DefId(0:3 ~ issue_99325[8f58]::function_with_bytes), UserSubsts { substs: [Const { ty: &'static [u8; 4], kind: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:8 ~ issue_99325[8f58]::main::{constant#1}), const_param_did: Some(DefId(0:4 ~ issue_99325[8f58]::function_with_bytes::BYTES)) }, substs: [], promoted: None }) }], user_self_ty: None }) }, span: $DIR/issue-99325.rs:11:16: 11:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-99325.rs:9:15: 9:15 + let _1: (); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _2: (&&[u8], &&[u8; 4]); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _3: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _4: &[u8]; // in scope 0 at $DIR/issue-99325.rs:10:16: 10:48 + let mut _5: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _6: &[u8; 4]; // in scope 0 at $DIR/issue-99325.rs:10:50: 10:75 + let _7: [u8; 4]; // in scope 0 at $DIR/issue-99325.rs:10:51: 10:75 + let _8: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _9: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _10: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _11: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _12: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _13: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _14: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _16: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _17: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _18: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _19: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _20: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _21: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _22: std::option::Option; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _23: (); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _24: (&&[u8], &&[u8; 4]); // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _25: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _26: &[u8]; // in scope 0 at $DIR/issue-99325.rs:11:16: 11:70 + let mut _27: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _28: &[u8; 4]; // in scope 0 at $DIR/issue-99325.rs:11:72: 11:79 + let _29: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _30: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _31: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _32: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _33: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _34: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _35: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _37: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _38: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _39: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _40: &&[u8]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _41: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _42: &&[u8; 4]; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let mut _43: std::option::Option; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 1 { + debug left_val => _8; // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug right_val => _9; // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _15: core::panicking::AssertKind; // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 2 { + debug kind => _15; // in scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + } + scope 3 { + debug left_val => _29; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + debug right_val => _30; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + let _36: core::panicking::AssertKind; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + scope 4 { + debug kind => _36; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + } + + bb0: { + StorageLive(_1); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_3); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_4); // scope 0 at $DIR/issue-99325.rs:10:16: 10:48 + _4 = function_with_bytes::<&*b"AAAA">() -> [return: bb1, unwind: bb19]; // scope 0 at $DIR/issue-99325.rs:10:16: 10:48 + // mir::Constant + // + span: $DIR/issue-99325.rs:10:16: 10:46 + // + user_ty: UserType(0) + // + literal: Const { ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}, val: Value() } + } + + bb1: { + _3 = &_4; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_5); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_6); // scope 0 at $DIR/issue-99325.rs:10:50: 10:75 + StorageLive(_7); // scope 0 at $DIR/issue-99325.rs:10:51: 10:75 + _7 = [const 65_u8, const 65_u8, const 65_u8, const 65_u8]; // scope 0 at $DIR/issue-99325.rs:10:51: 10:75 + _6 = &_7; // scope 0 at $DIR/issue-99325.rs:10:50: 10:75 + _5 = &_6; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _2 = (move _3, move _5); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_5); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_3); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + FakeRead(ForMatchedPlace(None), _2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_8); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _8 = (_2.0: &&[u8]); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_9); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _9 = (_2.1: &&[u8; 4]); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_10); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_11); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_12); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _12 = &(*_8); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_13); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _13 = &(*_9); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _11 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _12, move _13) -> [return: bb2, unwind: bb19]; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's> fn(&'r &[u8], &'s &[u8; 4]) -> bool {<&[u8] as PartialEq<&[u8; 4]>>::eq}, val: Value() } + } + + bb2: { + StorageDead(_13); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_12); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _10 = Not(move _11); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_11); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + switchInt(move _10) -> [false: bb4, otherwise: bb3]; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb3: { + StorageLive(_15); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _15 = core::panicking::AssertKind::Eq; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + FakeRead(ForLet(None), _15); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_16); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_17); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _17 = move _15; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_18); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_19); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _19 = &(*_8); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _18 = &(*_19); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_20); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_21); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _21 = &(*_9); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _20 = &(*_21); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_22); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _22 = Option::::None; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _16 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _17, move _18, move _20, move _22) -> bb19; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r &[u8], &'s &[u8; 4], Option>) -> ! {core::panicking::assert_failed::<&[u8], &[u8; 4]>}, val: Value() } + } + + bb4: { + goto -> bb7; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb5: { + StorageDead(_22); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_20); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_18); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_17); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_21); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_19); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_16); // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_15); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + unreachable; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb6: { + goto -> bb8; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb7: { + _1 = const (); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb8; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb8: { + StorageDead(_10); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_9); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_8); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb9; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb9: { + StorageDead(_7); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_6); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_4); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_1); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_23); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_24); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_25); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_26); // scope 0 at $DIR/issue-99325.rs:11:16: 11:70 + _26 = function_with_bytes::<&*b"AAAA">() -> [return: bb10, unwind: bb19]; // scope 0 at $DIR/issue-99325.rs:11:16: 11:70 + // mir::Constant + // + span: $DIR/issue-99325.rs:11:16: 11:68 + // + user_ty: UserType(1) + // + literal: Const { ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}, val: Value() } + } + + bb10: { + _25 = &_26; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_27); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_28); // scope 0 at $DIR/issue-99325.rs:11:72: 11:79 + _28 = const b"AAAA"; // scope 0 at $DIR/issue-99325.rs:11:72: 11:79 + // mir::Constant + // + span: $DIR/issue-99325.rs:11:72: 11:79 + // + literal: Const { ty: &[u8; 4], val: Value(Scalar(alloc4)) } + _27 = &_28; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _24 = (move _25, move _27); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_27); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_25); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + FakeRead(ForMatchedPlace(None), _24); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_29); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _29 = (_24.0: &&[u8]); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_30); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _30 = (_24.1: &&[u8; 4]); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_31); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_33); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _33 = &(*_29); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_34); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _34 = &(*_30); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _32 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _33, move _34) -> [return: bb11, unwind: bb19]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's> fn(&'r &[u8], &'s &[u8; 4]) -> bool {<&[u8] as PartialEq<&[u8; 4]>>::eq}, val: Value() } + } + + bb11: { + StorageDead(_34); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_33); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _31 = Not(move _32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_32); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + switchInt(move _31) -> [false: bb13, otherwise: bb12]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb12: { + StorageLive(_36); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _36 = core::panicking::AssertKind::Eq; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + FakeRead(ForLet(None), _36); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_37); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_38); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _38 = move _36; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_39); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_40); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _40 = &(*_29); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _39 = &(*_40); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_41); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_42); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _42 = &(*_30); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _41 = &(*_42); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageLive(_43); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _43 = Option::::None; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _37 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _38, move _39, move _41, move _43) -> bb19; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + // mir::Constant + // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL + // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r &[u8], &'s &[u8; 4], Option>) -> ! {core::panicking::assert_failed::<&[u8], &[u8; 4]>}, val: Value() } + } + + bb13: { + goto -> bb16; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb14: { + StorageDead(_43); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_41); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_39); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_38); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_42); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_40); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_37); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_36); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + unreachable; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb15: { + goto -> bb17; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb16: { + _23 = const (); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb17; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb17: { + StorageDead(_31); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_30); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_29); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + goto -> bb18; // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + } + + bb18: { + StorageDead(_28); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_26); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_24); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + StorageDead(_23); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL + _0 = const (); // scope 0 at $DIR/issue-99325.rs:9:15: 12:2 + return; // scope 0 at $DIR/issue-99325.rs:12:2: 12:2 + } + + bb19 (cleanup): { + resume; // scope 0 at $DIR/issue-99325.rs:9:1: 12:2 + } +} + +alloc4 (size: 4, align: 1) { + 41 41 41 41 │ AAAA +} diff --git a/src/test/rustdoc-gui/docblock-details.goml b/src/test/rustdoc-gui/docblock-details.goml index 2edbf1e4e2d8c..f6287ade2f295 100644 --- a/src/test/rustdoc-gui/docblock-details.goml +++ b/src/test/rustdoc-gui/docblock-details.goml @@ -9,12 +9,12 @@ reload: assert-text: (".top-doc .docblock > h3", "Hello") assert-css: ( ".top-doc .docblock > h3", - {"border-bottom": "1px solid rgb(221, 221, 221)"}, + {"border-bottom": "1px solid rgb(210, 210, 210)"}, ) // We now check that the `` doesn't have a bottom border and has the correct display. assert-css: ( ".top-doc .docblock summary h4", - {"border-bottom": "0px none rgb(221, 221, 221)"}, + {"border-bottom": "0px none rgb(210, 210, 210)"}, ) // This allows to ensure that summary is on one line only! assert-property: (".top-doc .docblock summary h4", {"offsetHeight": "33"}) diff --git a/src/test/rustdoc-gui/headings.goml b/src/test/rustdoc-gui/headings.goml index cc3dd61e99a7a..8c2c3df1588f5 100644 --- a/src/test/rustdoc-gui/headings.goml +++ b/src/test/rustdoc-gui/headings.goml @@ -150,15 +150,109 @@ assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"}) assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) -goto: file://|DOC_PATH|/staged_api/struct.Foo.html +// Checking colors now. show-text: true local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"} +goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html +assert-css: ( + ".top-doc .docblock h2", + {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"}, +) +assert-css: ( + ".top-doc .docblock h3", + {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"}, +) +assert-css: ( + ".top-doc .docblock h4", + {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"}, +) +assert-css: ( + ".top-doc .docblock h5", + {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"}, +) +assert-css: ( + "#implementations-list .docblock h4", + {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"}, +) +assert-css: ( + "#implementations-list .docblock h5", + {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"}, +) +assert-css: ( + "#implementations-list .docblock h6", + {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"}, +) + +local-storage: {"rustdoc-theme": "dark"} +reload: +assert-css: ( + ".top-doc .docblock h2", + {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"}, +) +assert-css: ( + ".top-doc .docblock h3", + {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"}, +) +assert-css: ( + ".top-doc .docblock h4", + {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"}, +) +assert-css: ( + ".top-doc .docblock h5", + {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"}, +) +assert-css: ( + "#implementations-list .docblock h4", + {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"}, +) +assert-css: ( + "#implementations-list .docblock h5", + {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"}, +) +assert-css: ( + "#implementations-list .docblock h6", + {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"}, +) + +local-storage: {"rustdoc-theme": "ayu"} +reload: +assert-css: ( + ".top-doc .docblock h2", + {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"}, +) +assert-css: ( + ".top-doc .docblock h2", + {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"}, +) +assert-css: ( + ".top-doc .docblock h4", + {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"}, +) +assert-css: ( + ".top-doc .docblock h5", + {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"}, +) +assert-css: ( + "#implementations-list .docblock h4", + {"color": "rgb(255, 255, 255)", "border-bottom": "0px none rgb(92, 103, 115)"}, +) +assert-css: ( + "#implementations-list .docblock h5", + {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"}, +) +assert-css: ( + "#implementations-list .docblock h6", + {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"}, +) + +local-storage: {"rustdoc-theme": "light"} +goto: file://|DOC_PATH|/staged_api/struct.Foo.html assert-css: (".since", {"color": "rgb(128, 128, 128)"}) -local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"} +local-storage: {"rustdoc-theme": "dark"} reload: assert-css: (".since", {"color": "rgb(128, 128, 128)"}) -local-storage: {"rustdoc-theme": "ayu", "rustdoc-use-system-theme": "false"} +local-storage: {"rustdoc-theme": "ayu"} reload: assert-css: (".since", {"color": "rgb(128, 128, 128)"}) diff --git a/src/test/rustdoc-gui/src/test_docs/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs index aa2f78289bea2..1b26aaecb5ef3 100644 --- a/src/test/rustdoc-gui/src/test_docs/lib.rs +++ b/src/test/rustdoc-gui/src/test_docs/lib.rs @@ -146,6 +146,10 @@ pub use crate::repro as repro2; /// ### Top-doc Prose sub-sub-heading /// /// Text below sub-sub-heading +/// +/// #### You know the drill. +/// +/// More text. pub struct HeavilyDocumentedStruct { /// # Title for field /// ## Sub-heading for field diff --git a/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs b/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs new file mode 100644 index 0000000000000..d6bd6945e150a --- /dev/null +++ b/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs @@ -0,0 +1,22 @@ +// compile-flags: -Z unstable-options + +// Test that accessing command line options by field access triggers a lint for those fields +// that have wrapper functions which should be used. + +#![crate_type = "lib"] +#![feature(rustc_private)] +#![deny(rustc::bad_opt_access)] + +extern crate rustc_session; +use rustc_session::Session; + +pub fn access_bad_option(sess: Session) { + let _ = sess.opts.cg.split_debuginfo; + //~^ ERROR use `Session::split_debuginfo` instead of this field + + let _ = sess.opts.crate_types; + //~^ ERROR use `Session::crate_types` instead of this field + + let _ = sess.opts.crate_name; + // okay! +} diff --git a/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr b/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr new file mode 100644 index 0000000000000..e4145bff8bee9 --- /dev/null +++ b/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr @@ -0,0 +1,20 @@ +error: use `Session::split_debuginfo` instead of this field + --> $DIR/bad_opt_access.rs:14:13 + | +LL | let _ = sess.opts.cg.split_debuginfo; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/bad_opt_access.rs:8:9 + | +LL | #![deny(rustc::bad_opt_access)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: use `Session::crate_types` instead of this field + --> $DIR/bad_opt_access.rs:17:13 + | +LL | let _ = sess.opts.crate_types; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/const-generics/fn-const-param-call.full.stderr b/src/test/ui/const-generics/fn-const-param-call.full.stderr index d984449e6ca6e..b55c2449858c9 100644 --- a/src/test/ui/const-generics/fn-const-param-call.full.stderr +++ b/src/test/ui/const-generics/fn-const-param-call.full.stderr @@ -1,10 +1,10 @@ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/fn-const-param-call.rs:11:25 | LL | struct Wrapper u32>; | ^^^^^^^^^^^ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/fn-const-param-call.rs:13:15 | LL | impl u32> Wrapper { @@ -12,3 +12,4 @@ LL | impl u32> Wrapper { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/fn-const-param-infer.full.stderr b/src/test/ui/const-generics/fn-const-param-infer.full.stderr index f0767a10994a5..2d66a19233269 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.full.stderr +++ b/src/test/ui/const-generics/fn-const-param-infer.full.stderr @@ -1,4 +1,4 @@ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/fn-const-param-infer.rs:6:25 | LL | struct Checked bool>; @@ -6,3 +6,4 @@ LL | struct Checked bool>; error: aborting due to previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-71381.full.stderr b/src/test/ui/const-generics/issues/issue-71381.full.stderr index 3950317b37053..e17cf96aa3e3d 100644 --- a/src/test/ui/const-generics/issues/issue-71381.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71381.full.stderr @@ -10,13 +10,13 @@ error[E0770]: the type of const parameters must not depend on other generic para LL | const FN: unsafe extern "C" fn(Args), | ^^^^ the type must not depend on the parameter `Args` -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-71381.rs:14:61 | LL | pub fn call_me(&self) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-71381.rs:23:19 | LL | const FN: unsafe extern "C" fn(Args), @@ -24,4 +24,5 @@ LL | const FN: unsafe extern "C" fn(Args), error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0770`. +Some errors have detailed explanations: E0741, E0770. +For more information about an error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-71382.full.stderr b/src/test/ui/const-generics/issues/issue-71382.full.stderr index 715037bd5f1e8..ab2a4e64a8389 100644 --- a/src/test/ui/const-generics/issues/issue-71382.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71382.full.stderr @@ -1,4 +1,4 @@ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-71382.rs:16:23 | LL | fn test(&self) { @@ -6,3 +6,4 @@ LL | fn test(&self) { error: aborting due to previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-71611.full.stderr b/src/test/ui/const-generics/issues/issue-71611.full.stderr index 01a85b745ce39..656aa29e19b25 100644 --- a/src/test/ui/const-generics/issues/issue-71611.full.stderr +++ b/src/test/ui/const-generics/issues/issue-71611.full.stderr @@ -4,7 +4,7 @@ error[E0770]: the type of const parameters must not depend on other generic para LL | fn func(outer: A) { | ^ the type must not depend on the parameter `A` -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-71611.rs:5:21 | LL | fn func(outer: A) { @@ -12,4 +12,5 @@ LL | fn func(outer: A) { error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0770`. +Some errors have detailed explanations: E0741, E0770. +For more information about an error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-72352.full.stderr b/src/test/ui/const-generics/issues/issue-72352.full.stderr index eedd73c4dcc0a..92580b33685d1 100644 --- a/src/test/ui/const-generics/issues/issue-72352.full.stderr +++ b/src/test/ui/const-generics/issues/issue-72352.full.stderr @@ -1,4 +1,4 @@ -error: using function pointers as const generic parameters is forbidden +error[E0741]: using function pointers as const generic parameters is forbidden --> $DIR/issue-72352.rs:7:42 | LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8) -> usize { @@ -6,3 +6,4 @@ LL | unsafe fn unsafely_do_the_thing usize>(ptr: *const i8 error: aborting due to previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-99641.rs b/src/test/ui/const-generics/issues/issue-99641.rs new file mode 100644 index 0000000000000..fae6d3fc41fb8 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-99641.rs @@ -0,0 +1,18 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +fn main() { + pub struct Color; + //~^ ERROR using function pointers + + impl Color { + //~^ ERROR using function pointers + pub fn new() -> Self { + Color:: + } + } + + pub const D65: (fn(),) = (|| {},); + + Color::::new(); +} diff --git a/src/test/ui/const-generics/issues/issue-99641.stderr b/src/test/ui/const-generics/issues/issue-99641.stderr new file mode 100644 index 0000000000000..349ebba08d53f --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-99641.stderr @@ -0,0 +1,15 @@ +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/issue-99641.rs:5:35 + | +LL | pub struct Color; + | ^^^^^^^ + +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/issue-99641.rs:8:23 + | +LL | impl Color { + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr index 04bc46cb4ab12..657eee2be2443 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr @@ -1,10 +1,10 @@ -error: using raw pointers as const generic parameters is forbidden +error[E0741]: using raw pointers as const generic parameters is forbidden --> $DIR/raw-ptr-const-param-deref.rs:9:23 | LL | struct Const; | ^^^^^^^^^^ -error: using raw pointers as const generic parameters is forbidden +error[E0741]: using raw pointers as const generic parameters is forbidden --> $DIR/raw-ptr-const-param-deref.rs:11:15 | LL | impl Const

{ @@ -12,3 +12,4 @@ LL | impl Const

{ error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr index 310422aafcd35..69f1aae5681a4 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr +++ b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr @@ -1,4 +1,4 @@ -error: using raw pointers as const generic parameters is forbidden +error[E0741]: using raw pointers as const generic parameters is forbidden --> $DIR/raw-ptr-const-param.rs:6:23 | LL | struct Const; @@ -6,3 +6,4 @@ LL | struct Const; error: aborting due to previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/consts/refs_check_const_eq-issue-88384.rs b/src/test/ui/consts/refs_check_const_eq-issue-88384.rs index 204d18ea25de4..1496b28bd3ee6 100644 --- a/src/test/ui/consts/refs_check_const_eq-issue-88384.rs +++ b/src/test/ui/consts/refs_check_const_eq-issue-88384.rs @@ -1,5 +1,3 @@ -// check-pass - #![feature(fn_traits)] #![feature(adt_const_params)] //~^ WARNING the feature `adt_const_params` is incomplete @@ -10,8 +8,10 @@ struct CompileTimeSettings{ } struct Foo; +//~^ ERROR using function pointers as const generic parameters is forbidden impl Foo { + //~^ ERROR using function pointers as const generic parameters is forbidden fn call_hooks(){ } } diff --git a/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr b/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr index f2bad2f552759..4f2f5e244b67e 100644 --- a/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr +++ b/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr @@ -1,5 +1,5 @@ warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/refs_check_const_eq-issue-88384.rs:4:12 + --> $DIR/refs_check_const_eq-issue-88384.rs:2:12 | LL | #![feature(adt_const_params)] | ^^^^^^^^^^^^^^^^ @@ -7,5 +7,18 @@ LL | #![feature(adt_const_params)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #95174 for more information -warning: 1 warning emitted +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/refs_check_const_eq-issue-88384.rs:10:21 + | +LL | struct Foo; + | ^^^^^^^^^^^^^^^^^^^ + +error[E0741]: using function pointers as const generic parameters is forbidden + --> $DIR/refs_check_const_eq-issue-88384.rs:13:15 + | +LL | impl Foo { + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs b/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs new file mode 100644 index 0000000000000..929b82bfc432e --- /dev/null +++ b/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs @@ -0,0 +1,24 @@ +// check-pass +// Verify that we do not ICE when anonymous lifetimes appear inside an AnonConst. + +pub struct EntriesBuffer(Box<[[u8; HashesEntry::LEN]; 5]>); + +impl EntriesBuffer { + pub fn iter_child_buffers(&mut self) -> impl Iterator { + self.0.iter_mut() + } + + pub fn iter_child_buffers_explicit( + &mut self, + ) -> impl Iterator::LEN]> { + self.0.iter_mut() + } +} + +pub struct HashesEntry<'a>(&'a [u8]); + +impl HashesEntry<'_> { + pub const LEN: usize = 1; +} + +fn main() {} diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 826353aafc069..3f69cc2038839 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -615,7 +615,7 @@ struct BodyLifetimeChecker { impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - if lifetime.name.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime { + if lifetime.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime { self.lifetimes_used_in_body = true; } } diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 945880d21471b..a0ca7e6ff1e22 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -166,7 +166,7 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) // - There's only one output lifetime bound using `+ '_` // - All input lifetimes are explicitly bound to the output input_lifetimes.is_empty() - || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore)) + || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Infer)) || input_lifetimes .iter() .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt)) diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 8534d8a29f10d..3c5ea2d94144f 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -351,7 +351,7 @@ impl fmt::Display for RefPrefix { name.fmt(f)?; f.write_char(' ')?; }, - LifetimeName::Underscore => f.write_str("'_ ")?, + LifetimeName::Infer => f.write_str("'_ ")?, LifetimeName::Static => f.write_str("'static ")?, _ => (), } diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index c219c7de830ef..c1ec2bd5bd665 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -94,6 +94,8 @@ struct ClippyCallbacks { } impl rustc_driver::Callbacks for ClippyCallbacks { + // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` + #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))] fn config(&mut self, config: &mut interface::Config) { let previous = config.register_lints.take(); let clippy_args_var = self.clippy_args_var.take();