From 177af4710464d9f743d64f8792afcb0ee553ba6b Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 27 Apr 2022 18:14:19 +0400 Subject: [PATCH 1/8] Implement `#[rustc_default_body_unstable]` This attribute allows to mark default body of a trait function as unstable. This means that implementing the trait without implementing the function will require enabling unstable feature. This is useful in conjunction with `#[rustc_must_implement_one_of]`, we may want to relax requirements for a trait, for example allowing implementing either of `PartialEq::{eq, ne}`, but do so in a safe way -- making implementation of only `PartialEq::ne` unstable. --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_attr/src/builtin.rs | 31 +++++++-- compiler/rustc_expand/src/base.rs | 13 +++- compiler/rustc_feature/src/builtin_attrs.rs | 4 ++ .../src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 13 ++++ compiler/rustc_metadata/src/rmeta/mod.rs | 1 + compiler/rustc_middle/src/middle/stability.rs | 64 ++++++++++++++++++- compiler/rustc_middle/src/query/mod.rs | 5 ++ compiler/rustc_middle/src/ty/parameterized.rs | 1 + compiler/rustc_passes/src/stability.rs | 36 +++++++++-- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/check/check.rs | 30 +++++++-- compiler/rustc_typeck/src/check/mod.rs | 22 ++++++- 14 files changed, 206 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index ad9ed798e558b..3b58996e501d6 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -417,6 +417,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { || attr.has_name(sym::stable) || attr.has_name(sym::rustc_const_unstable) || attr.has_name(sym::rustc_const_stable) + || attr.has_name(sym::rustc_default_body_unstable) { struct_span_err!( self.sess, diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 10a9cfb626e63..2eb50d648028e 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -131,6 +131,14 @@ impl ConstStability { } } +/// Represents the `#[rustc_default_body_unstable]` attribute. +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct DefaultBodyStability { + pub level: StabilityLevel, + pub feature: Symbol, +} + /// The available stability levels. #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(HashStable_Generic)] @@ -214,7 +222,8 @@ pub fn find_stability( sess: &Session, attrs: &[Attribute], item_sp: Span, -) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) { +) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>, Option<(DefaultBodyStability, Span)>) +{ find_stability_generic(sess, attrs.iter(), item_sp) } @@ -222,7 +231,7 @@ fn find_stability_generic<'a, I>( sess: &Session, attrs_iter: I, item_sp: Span, -) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>) +) -> (Option<(Stability, Span)>, Option<(ConstStability, Span)>, Option<(DefaultBodyStability, Span)>) where I: Iterator, { @@ -230,6 +239,7 @@ where let mut stab: Option<(Stability, Span)> = None; let mut const_stab: Option<(ConstStability, Span)> = None; + let mut body_stab: Option<(DefaultBodyStability, Span)> = None; let mut promotable = false; let mut allowed_through_unstable_modules = false; @@ -243,6 +253,7 @@ where sym::stable, sym::rustc_promotable, sym::rustc_allowed_through_unstable_modules, + sym::rustc_default_body_unstable, ] .iter() .any(|&s| attr.has_name(s)) @@ -280,7 +291,7 @@ where let meta_name = meta.name_or_empty(); match meta_name { - sym::rustc_const_unstable | sym::unstable => { + sym::rustc_const_unstable | sym::rustc_default_body_unstable | sym::unstable => { if meta_name == sym::unstable && stab.is_some() { handle_errors( &sess.parse_sess, @@ -295,6 +306,13 @@ where AttrError::MultipleStabilityLevels, ); break; + } else if meta_name == sym::rustc_default_body_unstable && body_stab.is_some() { + handle_errors( + &sess.parse_sess, + attr.span, + AttrError::MultipleStabilityLevels, + ); + break; } let mut feature = None; @@ -405,11 +423,14 @@ where }; if sym::unstable == meta_name { stab = Some((Stability { level, feature }, attr.span)); - } else { + } else if sym::rustc_const_unstable == meta_name { const_stab = Some(( ConstStability { level, feature, promotable: false }, attr.span, )); + } else { + body_stab = + Some((DefaultBodyStability { level, feature }, attr.span)); } } (None, _, _) => { @@ -542,7 +563,7 @@ where } } - (stab, const_stab) + (stab, const_stab, body_stab) } pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 6e093811fcf6e..852ea806b20ff 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -772,7 +772,7 @@ impl SyntaxExtension { ) }) .unwrap_or_else(|| (None, helper_attrs)); - let (stability, const_stability) = attr::find_stability(&sess, attrs, span); + let (stability, const_stability, body_stability) = attr::find_stability(&sess, attrs, span); if let Some((_, sp)) = const_stability { sess.parse_sess .span_diagnostic @@ -784,6 +784,17 @@ impl SyntaxExtension { ) .emit(); } + if let Some((_, sp)) = body_stability { + sess.parse_sess + .span_diagnostic + .struct_span_err(sp, "macros cannot have body stability attributes") + .span_label(sp, "invalid body stability attribute") + .span_label( + sess.source_map().guess_head_span(span), + "body stability attribute affects this macro", + ) + .emit(); + } SyntaxExtension { kind, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c806df8214586..d8fd627c100d3 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -499,6 +499,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), + ungated!( + rustc_default_body_unstable, Normal, + template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk + ), gated!( allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, "allow_internal_unstable side-steps feature gating and stability checks", diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 65cae29c58dcb..35a6719a2fba6 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -207,6 +207,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, def_ident_span => { table } lookup_stability => { table } lookup_const_stability => { table } + lookup_default_body_stability => { table } lookup_deprecation_entry => { table } visibility => { table } unused_generic_params => { table } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 50d983754e89c..f30f139584aa0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1029,6 +1029,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if should_encode_stability(def_kind) { self.encode_stability(def_id); self.encode_const_stability(def_id); + self.encode_default_body_stability(def_id); self.encode_deprecation(def_id); } if should_encode_variances(def_kind) { @@ -1397,6 +1398,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } + fn encode_default_body_stability(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_default_body_stability({:?})", def_id); + + // The query lookup can take a measurable amount of time in crates with many items. Check if + // the stability attributes are even enabled before using their queries. + if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { + if let Some(stab) = self.tcx.lookup_default_body_stability(def_id) { + record!(self.tables.lookup_default_body_stability[def_id] <- stab) + } + } + } + fn encode_deprecation(&mut self, def_id: DefId) { debug!("EncodeContext::encode_deprecation({:?})", def_id); if let Some(depr) = self.tcx.lookup_deprecation(def_id) { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 0f291f9264777..a1d3c918ebdfc 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -343,6 +343,7 @@ define_tables! { def_ident_span: Table>, lookup_stability: Table>, lookup_const_stability: Table>, + lookup_default_body_stability: Table>, lookup_deprecation_entry: Table>, // As an optimization, a missing entry indicates an empty `&[]`. explicit_item_bounds: Table, Span)>>, diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 414912dd0f7d8..24b74b7f65f52 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -5,7 +5,7 @@ pub use self::StabilityLevel::*; use crate::ty::{self, DefIdTree, TyCtxt}; use rustc_ast::NodeId; -use rustc_attr::{self as attr, ConstStability, Deprecation, Stability}; +use rustc_attr::{self as attr, ConstStability, DefaultBodyStability, Deprecation, Stability}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diagnostic}; use rustc_feature::GateIssue; @@ -61,6 +61,7 @@ pub struct Index { /// are filled by the annotator. pub stab_map: FxHashMap, pub const_stab_map: FxHashMap, + pub default_body_stab_map: FxHashMap, pub depr_map: FxHashMap, /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute @@ -86,6 +87,10 @@ impl Index { self.const_stab_map.get(&def_id).copied() } + pub fn local_default_body_stability(&self, def_id: LocalDefId) -> Option { + self.default_body_stab_map.get(&def_id).copied() + } + pub fn local_deprecation_entry(&self, def_id: LocalDefId) -> Option { self.depr_map.get(&def_id).cloned() } @@ -492,6 +497,63 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Evaluates the default-impl stability of an item. + /// + /// Returns `EvalResult::Allow` if the item's default implementation is stable, or unstable but the corresponding + /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending + /// unstable feature otherwise. + pub fn eval_default_body_stability(self, def_id: DefId, span: Span) -> EvalResult { + let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some(); + if !is_staged_api { + return EvalResult::Allow; + } + + let stability = self.lookup_default_body_stability(def_id); + debug!( + "body stability: inspecting def_id={def_id:?} span={span:?} of stability={stability:?}" + ); + + // Only the cross-crate scenario matters when checking unstable APIs + let cross_crate = !def_id.is_local(); + if !cross_crate { + return EvalResult::Allow; + } + + // Issue #38412: private items lack stability markers. + if skip_stability_check_due_to_privacy(self, def_id) { + return EvalResult::Allow; + } + + match stability { + Some(DefaultBodyStability { + level: attr::Unstable { reason, issue, is_soft, .. }, + feature, + }) => { + if span.allows_unstable(feature) { + debug!("body stability: skipping span={:?} since it is internal", span); + return EvalResult::Allow; + } + if self.features().active(feature) { + return EvalResult::Allow; + } + + //let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); + EvalResult::Deny { + feature, + reason: reason.to_opt_reason(), + issue, + suggestion: None, + is_soft, + } + } + Some(_) => { + // Stable APIs are always ok to call + EvalResult::Allow + } + None => EvalResult::Unmarked, + } + } + /// Checks if an item is stable or error out. /// /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 466a0fc25f7d1..0d03f506aac2b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1094,6 +1094,11 @@ rustc_queries! { separate_provide_extern } + query lookup_default_body_stability(def_id: DefId) -> Option { + desc { |tcx| "looking up default body stability of `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + query should_inherit_track_caller(def_id: DefId) -> bool { desc { |tcx| "computing should_inherit_track_caller of `{}`", tcx.def_path_str(def_id) } } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index e189ee2fc4db1..ae6e2eecbffb5 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -64,6 +64,7 @@ trivially_parameterized_over_tcx! { rustc_ast::Attribute, rustc_ast::MacArgs, rustc_attr::ConstStability, + rustc_attr::DefaultBodyStability, rustc_attr::Deprecation, rustc_attr::Stability, rustc_hir::Constness, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index ca6a2ac3db34c..2a3da4ec30e35 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -1,8 +1,10 @@ //! A pass that annotates every item and method with its stability level, //! propagating default levels lexically from parent to children ast nodes. -use attr::StabilityLevel; -use rustc_attr::{self as attr, ConstStability, Stability, Unstable, UnstableReason}; +use rustc_attr::{ + self as attr, ConstStability, DefaultBodyStability, Stability, StabilityLevel, Unstable, + UnstableReason, +}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; @@ -89,6 +91,7 @@ struct Annotator<'a, 'tcx> { index: &'a mut Index, parent_stab: Option, parent_const_stab: Option, + parent_body_stab: Option, parent_depr: Option, in_trait_impl: bool, } @@ -156,12 +159,13 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { depr.map(|(d, _)| DeprecationEntry::local(d, def_id)), None, None, + None, visit_children, ); return; } - let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); + let (stab, const_stab, body_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); let mut const_span = None; let const_stab = const_stab.map(|(const_stab, const_span_node)| { @@ -209,6 +213,15 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + let body_stab = body_stab.map(|(body_stab, _span)| { + // FIXME: check that this item can have body stability + + self.index.default_body_stab_map.insert(def_id, body_stab); + debug!(?self.index.default_body_stab_map); + + body_stab + }); + let stab = stab.map(|(stab, span)| { // Error if prohibited, or can't inherit anything from a container. if kind == AnnotationKind::Prohibited @@ -286,6 +299,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { depr.map(|(d, _)| DeprecationEntry::local(d, def_id)), stab, if inherit_const_stability.yes() { const_stab } else { None }, + body_stab, visit_children, ); } @@ -295,12 +309,14 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { depr: Option, stab: Option, const_stab: Option, + body_stab: Option, f: impl FnOnce(&mut Self), ) { // These will be `Some` if this item changes the corresponding stability attribute. let mut replaced_parent_depr = None; let mut replaced_parent_stab = None; let mut replaced_parent_const_stab = None; + let mut replaced_parent_body_stab = None; if let Some(depr) = depr { replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr))); @@ -312,6 +328,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { replaced_parent_const_stab = Some(replace(&mut self.parent_const_stab, Some(const_stab))); } + if let Some(body_stab) = body_stab { + replaced_parent_body_stab = Some(self.parent_body_stab.replace(body_stab)); + } f(self); @@ -324,6 +343,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let Some(orig_parent_const_stab) = replaced_parent_const_stab { self.parent_const_stab = orig_parent_const_stab; } + if let Some(orig_parent_body_stab) = replaced_parent_body_stab { + self.parent_body_stab = orig_parent_body_stab; + } } } @@ -613,6 +635,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { let mut index = Index { stab_map: Default::default(), const_stab_map: Default::default(), + default_body_stab_map: Default::default(), depr_map: Default::default(), implications: Default::default(), }; @@ -623,6 +646,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { index: &mut index, parent_stab: None, parent_const_stab: None, + parent_body_stab: None, parent_depr: None, in_trait_impl: false, }; @@ -673,6 +697,9 @@ pub(crate) fn provide(providers: &mut Providers) { stability_implications: |tcx, _| tcx.stability().implications.clone(), lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()), lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()), + lookup_default_body_stability: |tcx, id| { + tcx.stability().local_default_body_stability(id.expect_local()) + }, lookup_deprecation_entry: |tcx, id| { tcx.stability().local_deprecation_entry(id.expect_local()) }, @@ -723,7 +750,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { let features = self.tcx.features(); if features.staged_api { let attrs = self.tcx.hir().attrs(item.hir_id()); - let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item.span); + let (stab, const_stab, _) = + attr::find_stability(&self.tcx.sess, attrs, item.span); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 54d67c5254188..036f087e2d23b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1215,6 +1215,7 @@ symbols! { rustc_const_unstable, rustc_conversion_suggestion, rustc_def_path, + rustc_default_body_unstable, rustc_diagnostic_item, rustc_diagnostic_macros, rustc_dirty, diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 9497d5c4528cc..6254825d96dc8 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -18,6 +18,7 @@ use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::hir::nested_filter; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt}; @@ -1104,12 +1105,31 @@ fn check_impl_items_against_trait<'tcx>( missing_items.push(tcx.associated_item(trait_item_id)); } - if let Some(required_items) = &must_implement_one_of { - // true if this item is specifically implemented in this impl - let is_implemented_here = ancestors - .leaf_def(tcx, trait_item_id) - .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); + // true if this item is specifically implemented in this impl + let is_implemented_here = ancestors + .leaf_def(tcx, trait_item_id) + .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); + + if !is_implemented_here { + match tcx.eval_default_body_stability(trait_item_id, full_impl_span) { + EvalResult::Deny { feature, reason, issue, is_soft, .. } => { + default_body_is_unstable( + tcx, + full_impl_span, + trait_item_id, + feature, + reason, + issue, + is_soft, + ) + } + // Unmarked default bodies are considered stable (at least for now). + EvalResult::Allow | EvalResult::Unmarked => {} + } + } + + if let Some(required_items) = &must_implement_one_of { if is_implemented_here { let trait_item = tcx.associated_item(trait_item_id); if required_items.contains(&trait_item.ident(tcx)) { diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 17c2e4868aac7..acd7e76fe2598 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -99,6 +99,7 @@ pub use expectation::Expectation; pub use fn_ctxt::*; use hir::def::CtorOf; pub use inherited::{Inherited, InheritedBuilder}; +use rustc_middle::middle::stability::report_unstable; use crate::astconv::AstConv; use crate::check::gather_locals::GatherLocalsVisitor; @@ -122,13 +123,14 @@ use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, Ident}; -use rustc_span::{self, BytePos, Span}; +use rustc_span::{self, BytePos, Span, Symbol}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; use std::cell::RefCell; +use std::num::NonZeroU32; use crate::require_c_abi_if_c_variadic; use crate::util::common::indenter; @@ -662,6 +664,24 @@ fn missing_items_must_implement_one_of_err( err.emit(); } +fn default_body_is_unstable( + tcx: TyCtxt<'_>, + impl_span: Span, + _item_did: DefId, + feature: Symbol, + reason: Option, + issue: Option, + is_soft: bool, +) { + let soft_handler = |lint, span, msg: &_| { + tcx.struct_span_lint_hir(lint, hir::CRATE_HIR_ID, span, |lint| { + lint.build(msg).emit(); + }) + }; + + report_unstable(tcx.sess, feature, reason, issue, None, is_soft, impl_span, soft_handler) +} + /// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. fn bounds_from_generic_predicates<'tcx>( tcx: TyCtxt<'tcx>, From 9015a5110fd7b887dafaa1007afc02da90963f2a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 27 Apr 2022 19:00:17 +0400 Subject: [PATCH 2/8] Collect features from `#[rustc_default_body_unstable]` --- compiler/rustc_passes/src/lib_features.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index e05994f13e4d9..868887c66cdec 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -29,11 +29,16 @@ impl<'tcx> LibFeatureCollector<'tcx> { } fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option, Span)> { - let stab_attrs = - [sym::stable, sym::unstable, sym::rustc_const_stable, sym::rustc_const_unstable]; + let stab_attrs = [ + sym::stable, + sym::unstable, + sym::rustc_const_stable, + sym::rustc_const_unstable, + sym::rustc_default_body_unstable, + ]; // Find a stability attribute: one of #[stable(…)], #[unstable(…)], - // #[rustc_const_stable(…)], or #[rustc_const_unstable(…)]. + // #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable]. if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) { let meta_kind = attr.meta_kind(); if let Some(MetaItemKind::List(ref metas)) = meta_kind { @@ -53,8 +58,12 @@ impl<'tcx> LibFeatureCollector<'tcx> { // This additional check for stability is to make sure we // don't emit additional, irrelevant errors for malformed // attributes. - let is_unstable = - matches!(*stab_attr, sym::unstable | sym::rustc_const_unstable); + let is_unstable = matches!( + *stab_attr, + sym::unstable + | sym::rustc_const_unstable + | sym::rustc_default_body_unstable + ); if since.is_some() || is_unstable { return Some((feature, since, attr.span)); } From d65fa276f3f2a239805f8458e210fb92fcfdca24 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 27 Apr 2022 19:01:05 +0400 Subject: [PATCH 3/8] Add tests for `#[rustc_default_body_unstable]` --- .../auxiliary/default_body.rs | 29 +++++++++++++++++ .../default-body-stability-err.rs | 19 +++++++++++ .../default-body-stability-err.stderr | 32 +++++++++++++++++++ .../default-body-stability-ok-enables.rs | 18 +++++++++++ .../default-body-stability-ok-impls.rs | 21 ++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 src/test/ui/stability-attribute/auxiliary/default_body.rs create mode 100644 src/test/ui/stability-attribute/default-body-stability-err.rs create mode 100644 src/test/ui/stability-attribute/default-body-stability-err.stderr create mode 100644 src/test/ui/stability-attribute/default-body-stability-ok-enables.rs create mode 100644 src/test/ui/stability-attribute/default-body-stability-ok-impls.rs diff --git a/src/test/ui/stability-attribute/auxiliary/default_body.rs b/src/test/ui/stability-attribute/auxiliary/default_body.rs new file mode 100644 index 0000000000000..3a177419d666d --- /dev/null +++ b/src/test/ui/stability-attribute/auxiliary/default_body.rs @@ -0,0 +1,29 @@ +#![crate_type = "lib"] +#![feature(staged_api, rustc_attrs)] +#![stable(feature = "stable_feature", since = "1.0.0")] + +#[stable(feature = "stable_feature", since = "1.0.0")] +pub trait JustTrait { + #[stable(feature = "stable_feature", since = "1.0.0")] + #[rustc_default_body_unstable(feature = "constant_default_body", issue = "none")] + const CONSTANT: usize = 0; + + #[rustc_default_body_unstable(feature = "fun_default_body", issue = "none")] + #[stable(feature = "stable_feature", since = "1.0.0")] + fn fun() {} +} + +#[rustc_must_implement_one_of(eq, neq)] +#[stable(feature = "stable_feature", since = "1.0.0")] +pub trait Equal { + #[rustc_default_body_unstable(feature = "eq_default_body", issue = "none")] + #[stable(feature = "stable_feature", since = "1.0.0")] + fn eq(&self, other: &Self) -> bool { + !self.neq(other) + } + + #[stable(feature = "stable_feature", since = "1.0.0")] + fn neq(&self, other: &Self) -> bool { + !self.eq(other) + } +} diff --git a/src/test/ui/stability-attribute/default-body-stability-err.rs b/src/test/ui/stability-attribute/default-body-stability-err.rs new file mode 100644 index 0000000000000..8f970d0c9f661 --- /dev/null +++ b/src/test/ui/stability-attribute/default-body-stability-err.rs @@ -0,0 +1,19 @@ +// aux-build:default_body.rs +#![crate_type = "lib"] + +extern crate default_body; + +use default_body::{Equal, JustTrait}; + +struct Type; + +impl JustTrait for Type {} +//~^ ERROR use of unstable library feature 'fun_default_body' +//~| ERROR use of unstable library feature 'constant_default_body' + +impl Equal for Type { + //~^ ERROR use of unstable library feature 'eq_default_body' + fn neq(&self, other: &Self) -> bool { + false + } +} diff --git a/src/test/ui/stability-attribute/default-body-stability-err.stderr b/src/test/ui/stability-attribute/default-body-stability-err.stderr new file mode 100644 index 0000000000000..6abf68bbcae53 --- /dev/null +++ b/src/test/ui/stability-attribute/default-body-stability-err.stderr @@ -0,0 +1,32 @@ +error[E0658]: use of unstable library feature 'constant_default_body' + --> $DIR/default-body-stability-err.rs:10:1 + | +LL | impl JustTrait for Type {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(constant_default_body)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'fun_default_body' + --> $DIR/default-body-stability-err.rs:10:1 + | +LL | impl JustTrait for Type {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(fun_default_body)]` to the crate attributes to enable + +error[E0658]: use of unstable library feature 'eq_default_body' + --> $DIR/default-body-stability-err.rs:14:1 + | +LL | / impl Equal for Type { +LL | | +LL | | fn neq(&self, other: &Self) -> bool { +LL | | false +LL | | } +LL | | } + | |_^ + | + = help: add `#![feature(eq_default_body)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/stability-attribute/default-body-stability-ok-enables.rs b/src/test/ui/stability-attribute/default-body-stability-ok-enables.rs new file mode 100644 index 0000000000000..bdc7522f48dde --- /dev/null +++ b/src/test/ui/stability-attribute/default-body-stability-ok-enables.rs @@ -0,0 +1,18 @@ +// check-pass +// aux-build:default_body.rs +#![crate_type = "lib"] +#![feature(fun_default_body, eq_default_body, constant_default_body)] + +extern crate default_body; + +use default_body::{Equal, JustTrait}; + +struct Type; + +impl JustTrait for Type {} + +impl Equal for Type { + fn neq(&self, other: &Self) -> bool { + false + } +} diff --git a/src/test/ui/stability-attribute/default-body-stability-ok-impls.rs b/src/test/ui/stability-attribute/default-body-stability-ok-impls.rs new file mode 100644 index 0000000000000..e1f5c017096ab --- /dev/null +++ b/src/test/ui/stability-attribute/default-body-stability-ok-impls.rs @@ -0,0 +1,21 @@ +// check-pass +// aux-build:default_body.rs +#![crate_type = "lib"] + +extern crate default_body; + +use default_body::{Equal, JustTrait}; + +struct Type; + +impl JustTrait for Type { + const CONSTANT: usize = 1; + + fn fun() {} +} + +impl Equal for Type { + fn eq(&self, other: &Self) -> bool { + false + } +} From 198443711501234a5d98b6d621aae2cead73c8cb Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 20 Jun 2022 15:24:04 +0400 Subject: [PATCH 4/8] remove commented out code --- compiler/rustc_middle/src/middle/stability.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 24b74b7f65f52..0d1223ed28965 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -537,7 +537,6 @@ impl<'tcx> TyCtxt<'tcx> { return EvalResult::Allow; } - //let suggestion = suggestion_for_allocator_api(self, def_id, span, feature); EvalResult::Deny { feature, reason: reason.to_opt_reason(), From 1e1d6fe84dd086a9075fcfe5fc63d81738c02f12 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 20 Jun 2022 16:07:14 +0400 Subject: [PATCH 5/8] Improve error message for unstable default body --- Cargo.lock | 1 + compiler/rustc_typeck/Cargo.toml | 1 + compiler/rustc_typeck/src/check/check.rs | 19 ++++++------- compiler/rustc_typeck/src/check/mod.rs | 28 +++++++++++++------ .../default-body-stability-err.rs | 6 ++-- .../default-body-stability-err.stderr | 14 +++++++--- 6 files changed, 43 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed3e30342f2f6..bb7598bbf3e6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4616,6 +4616,7 @@ dependencies = [ "rustc_attr", "rustc_data_structures", "rustc_errors", + "rustc_feature", "rustc_graphviz", "rustc_hir", "rustc_hir_pretty", diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index faf52e2695a33..cae29c1d3c5f9 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -30,3 +30,4 @@ rustc_ty_utils = { path = "../rustc_ty_utils" } rustc_lint = { path = "../rustc_lint" } rustc_serialize = { path = "../rustc_serialize" } rustc_type_ir = { path = "../rustc_type_ir" } +rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 6254825d96dc8..0293bf7803a66 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -1112,17 +1112,14 @@ fn check_impl_items_against_trait<'tcx>( if !is_implemented_here { match tcx.eval_default_body_stability(trait_item_id, full_impl_span) { - EvalResult::Deny { feature, reason, issue, is_soft, .. } => { - default_body_is_unstable( - tcx, - full_impl_span, - trait_item_id, - feature, - reason, - issue, - is_soft, - ) - } + EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable( + tcx, + full_impl_span, + trait_item_id, + feature, + reason, + issue, + ), // Unmarked default bodies are considered stable (at least for now). EvalResult::Allow | EvalResult::Unmarked => {} diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index acd7e76fe2598..b5201c737dfe3 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -99,7 +99,6 @@ pub use expectation::Expectation; pub use fn_ctxt::*; use hir::def::CtorOf; pub use inherited::{Inherited, InheritedBuilder}; -use rustc_middle::middle::stability::report_unstable; use crate::astconv::AstConv; use crate::check::gather_locals::GatherLocalsVisitor; @@ -667,19 +666,32 @@ fn missing_items_must_implement_one_of_err( fn default_body_is_unstable( tcx: TyCtxt<'_>, impl_span: Span, - _item_did: DefId, + item_did: DefId, feature: Symbol, reason: Option, issue: Option, - is_soft: bool, ) { - let soft_handler = |lint, span, msg: &_| { - tcx.struct_span_lint_hir(lint, hir::CRATE_HIR_ID, span, |lint| { - lint.build(msg).emit(); - }) + let missing_item_name = &tcx.associated_item(item_did).name; + let use_of_unstable_library_feature_note = match reason { + Some(r) => format!("use of unstable library feature '{feature}': {r}"), + None => format!("use of unstable library feature '{feature}'"), }; - report_unstable(tcx.sess, feature, reason, issue, None, is_soft, impl_span, soft_handler) + let mut err = struct_span_err!( + tcx.sess, + impl_span, + E0046, + "not all trait items implemented, missing: `{missing_item_name}`", + ); + err.note(format!("default implementation of `{missing_item_name}` is unstable")); + err.note(use_of_unstable_library_feature_note); + rustc_session::parse::add_feature_diagnostics_for_issue( + &mut err, + &tcx.sess.parse_sess, + feature, + rustc_feature::GateIssue::Library(issue), + ); + err.emit(); } /// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. diff --git a/src/test/ui/stability-attribute/default-body-stability-err.rs b/src/test/ui/stability-attribute/default-body-stability-err.rs index 8f970d0c9f661..ecb281bccf604 100644 --- a/src/test/ui/stability-attribute/default-body-stability-err.rs +++ b/src/test/ui/stability-attribute/default-body-stability-err.rs @@ -8,11 +8,11 @@ use default_body::{Equal, JustTrait}; struct Type; impl JustTrait for Type {} -//~^ ERROR use of unstable library feature 'fun_default_body' -//~| ERROR use of unstable library feature 'constant_default_body' +//~^ ERROR not all trait items implemented, missing: `CONSTANT` [E0046] +//~| ERROR not all trait items implemented, missing: `fun` [E0046] impl Equal for Type { - //~^ ERROR use of unstable library feature 'eq_default_body' + //~^ ERROR not all trait items implemented, missing: `eq` [E0046] fn neq(&self, other: &Self) -> bool { false } diff --git a/src/test/ui/stability-attribute/default-body-stability-err.stderr b/src/test/ui/stability-attribute/default-body-stability-err.stderr index 6abf68bbcae53..ef666f30fc2a2 100644 --- a/src/test/ui/stability-attribute/default-body-stability-err.stderr +++ b/src/test/ui/stability-attribute/default-body-stability-err.stderr @@ -1,20 +1,24 @@ -error[E0658]: use of unstable library feature 'constant_default_body' +error[E0046]: not all trait items implemented, missing: `CONSTANT` --> $DIR/default-body-stability-err.rs:10:1 | LL | impl JustTrait for Type {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: default implementation of `CONSTANT` is unstable + = note: use of unstable library feature 'constant_default_body' = help: add `#![feature(constant_default_body)]` to the crate attributes to enable -error[E0658]: use of unstable library feature 'fun_default_body' +error[E0046]: not all trait items implemented, missing: `fun` --> $DIR/default-body-stability-err.rs:10:1 | LL | impl JustTrait for Type {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: default implementation of `fun` is unstable + = note: use of unstable library feature 'fun_default_body' = help: add `#![feature(fun_default_body)]` to the crate attributes to enable -error[E0658]: use of unstable library feature 'eq_default_body' +error[E0046]: not all trait items implemented, missing: `eq` --> $DIR/default-body-stability-err.rs:14:1 | LL | / impl Equal for Type { @@ -25,8 +29,10 @@ LL | | } LL | | } | |_^ | + = note: default implementation of `eq` is unstable + = note: use of unstable library feature 'eq_default_body' = help: add `#![feature(eq_default_body)]` to the crate attributes to enable error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0046`. From e11b4b8e02495e9753df983de7bcec9ef2b8ae31 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 31 Jul 2022 16:02:45 +0400 Subject: [PATCH 6/8] Panic when checking an unknown stability attribute --- compiler/rustc_attr/src/builtin.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 2eb50d648028e..62ccd734fe720 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -428,9 +428,11 @@ where ConstStability { level, feature, promotable: false }, attr.span, )); - } else { + } else if sym::rustc_default_body_unstable == meta_name { body_stab = Some((DefaultBodyStability { level, feature }, attr.span)); + } else { + unreachable!("Unknown stability attribute {meta_name}"); } } (None, _, _) => { From 963e402f4384806a4cfb9020208287972e5534d2 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 31 Jul 2022 16:29:31 +0400 Subject: [PATCH 7/8] Don't track parent body stability (it wasn't used anyway) --- compiler/rustc_passes/src/stability.rs | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 2a3da4ec30e35..be920601ee43f 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -2,8 +2,7 @@ //! propagating default levels lexically from parent to children ast nodes. use rustc_attr::{ - self as attr, ConstStability, DefaultBodyStability, Stability, StabilityLevel, Unstable, - UnstableReason, + self as attr, ConstStability, Stability, StabilityLevel, Unstable, UnstableReason, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::{struct_span_err, Applicability}; @@ -91,7 +90,6 @@ struct Annotator<'a, 'tcx> { index: &'a mut Index, parent_stab: Option, parent_const_stab: Option, - parent_body_stab: Option, parent_depr: Option, in_trait_impl: bool, } @@ -159,7 +157,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { depr.map(|(d, _)| DeprecationEntry::local(d, def_id)), None, None, - None, visit_children, ); return; @@ -213,14 +210,12 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - let body_stab = body_stab.map(|(body_stab, _span)| { + if let Some((body_stab, _span)) = body_stab { // FIXME: check that this item can have body stability self.index.default_body_stab_map.insert(def_id, body_stab); debug!(?self.index.default_body_stab_map); - - body_stab - }); + } let stab = stab.map(|(stab, span)| { // Error if prohibited, or can't inherit anything from a container. @@ -299,7 +294,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { depr.map(|(d, _)| DeprecationEntry::local(d, def_id)), stab, if inherit_const_stability.yes() { const_stab } else { None }, - body_stab, visit_children, ); } @@ -309,14 +303,12 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { depr: Option, stab: Option, const_stab: Option, - body_stab: Option, f: impl FnOnce(&mut Self), ) { // These will be `Some` if this item changes the corresponding stability attribute. let mut replaced_parent_depr = None; let mut replaced_parent_stab = None; let mut replaced_parent_const_stab = None; - let mut replaced_parent_body_stab = None; if let Some(depr) = depr { replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr))); @@ -328,9 +320,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { replaced_parent_const_stab = Some(replace(&mut self.parent_const_stab, Some(const_stab))); } - if let Some(body_stab) = body_stab { - replaced_parent_body_stab = Some(self.parent_body_stab.replace(body_stab)); - } f(self); @@ -343,9 +332,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if let Some(orig_parent_const_stab) = replaced_parent_const_stab { self.parent_const_stab = orig_parent_const_stab; } - if let Some(orig_parent_body_stab) = replaced_parent_body_stab { - self.parent_body_stab = orig_parent_body_stab; - } } } @@ -646,7 +632,6 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { index: &mut index, parent_stab: None, parent_const_stab: None, - parent_body_stab: None, parent_depr: None, in_trait_impl: false, }; From 95bf0fb917577c61e4a9d41d837e6fa132515b3d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 5 Aug 2022 18:13:22 +0400 Subject: [PATCH 8/8] Move stability lookup after cross-crate check --- compiler/rustc_middle/src/middle/stability.rs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 0d1223ed28965..7a9ad44d1d9ae 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -421,6 +421,12 @@ impl<'tcx> TyCtxt<'tcx> { return EvalResult::Allow; } + // Only the cross-crate scenario matters when checking unstable APIs + let cross_crate = !def_id.is_local(); + if !cross_crate { + return EvalResult::Allow; + } + let stability = self.lookup_stability(def_id); debug!( "stability: \ @@ -428,12 +434,6 @@ impl<'tcx> TyCtxt<'tcx> { def_id, span, stability ); - // Only the cross-crate scenario matters when checking unstable APIs - let cross_crate = !def_id.is_local(); - if !cross_crate { - return EvalResult::Allow; - } - // Issue #38412: private items lack stability markers. if skip_stability_check_due_to_privacy(self, def_id) { return EvalResult::Allow; @@ -508,17 +508,17 @@ impl<'tcx> TyCtxt<'tcx> { return EvalResult::Allow; } - let stability = self.lookup_default_body_stability(def_id); - debug!( - "body stability: inspecting def_id={def_id:?} span={span:?} of stability={stability:?}" - ); - // Only the cross-crate scenario matters when checking unstable APIs let cross_crate = !def_id.is_local(); if !cross_crate { return EvalResult::Allow; } + let stability = self.lookup_default_body_stability(def_id); + debug!( + "body stability: inspecting def_id={def_id:?} span={span:?} of stability={stability:?}" + ); + // Issue #38412: private items lack stability markers. if skip_stability_check_due_to_privacy(self, def_id) { return EvalResult::Allow;