Rollup merge of #96478 - WaffleLapkin:rustc_default_body_unstable, r=Aaron1011

Implement `#[rustc_default_body_unstable]`

This PR implements a new stability attribute — `#[rustc_default_body_unstable]`.

`#[rustc_default_body_unstable]` controls the stability of default bodies in traits.
For example:
```rust
pub trait Trait {
    #[rustc_default_body_unstable(feature = "feat", isssue = "none")]
    fn item() {}
}
```
In order to implement `Trait` user needs to either
- implement `item` (even though it has a default implementation)
- enable `#![feature(feat)]`

This is useful in conjunction with [`#[rustc_must_implement_one_of]`](https://github.com/rust-lang/rust/pull/92164), 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.

r? `@Aaron1011`
cc `@nrc` (iirc you were interested in this wrt `read_buf`), `@danielhenrymantilla` (you were interested in the related `#[rustc_must_implement_one_of]`)
P.S. This is my first time working with stability attributes, so I'm not sure if I did everything right 😅
This commit is contained in:
Dylan DPC 2022-08-09 17:34:50 +05:30 committed by GitHub
commit 1dc4858914
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 348 additions and 28 deletions

View file

@ -4626,6 +4626,7 @@ dependencies = [
"rustc_attr",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_graphviz",
"rustc_hir",
"rustc_hir_pretty",

View file

@ -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,

View file

@ -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<Item = &'a Attribute>,
{
@ -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,16 @@ 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 if sym::rustc_default_body_unstable == meta_name {
body_stab =
Some((DefaultBodyStability { level, feature }, attr.span));
} else {
unreachable!("Unknown stability attribute {meta_name}");
}
}
(None, _, _) => {
@ -542,7 +565,7 @@ where
}
}
(stab, const_stab)
(stab, const_stab, body_stab)
}
pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option<Symbol> {

View file

@ -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,

View file

@ -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",

View file

@ -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 }

View file

@ -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) {
@ -1385,6 +1386,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) {

View file

@ -343,6 +343,7 @@ define_tables! {
def_ident_span: Table<DefIndex, LazyValue<Span>>,
lookup_stability: Table<DefIndex, LazyValue<attr::Stability>>,
lookup_const_stability: Table<DefIndex, LazyValue<attr::ConstStability>>,
lookup_default_body_stability: Table<DefIndex, LazyValue<attr::DefaultBodyStability>>,
lookup_deprecation_entry: Table<DefIndex, LazyValue<attr::Deprecation>>,
// As an optimization, a missing entry indicates an empty `&[]`.
explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,

View file

@ -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<LocalDefId, Stability>,
pub const_stab_map: FxHashMap<LocalDefId, ConstStability>,
pub default_body_stab_map: FxHashMap<LocalDefId, DefaultBodyStability>,
pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>,
/// 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<DefaultBodyStability> {
self.default_body_stab_map.get(&def_id).copied()
}
pub fn local_deprecation_entry(&self, def_id: LocalDefId) -> Option<DeprecationEntry> {
self.depr_map.get(&def_id).cloned()
}
@ -416,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: \
@ -423,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;
@ -492,6 +497,62 @@ 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;
}
// 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;
}
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;
}
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

View file

@ -1094,6 +1094,11 @@ rustc_queries! {
separate_provide_extern
}
query lookup_default_body_stability(def_id: DefId) -> Option<attr::DefaultBodyStability> {
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) }
}

View file

@ -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,

View file

@ -29,11 +29,16 @@ impl<'tcx> LibFeatureCollector<'tcx> {
}
fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, 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));
}

View file

@ -1,8 +1,9 @@
//! 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, Stability, StabilityLevel, Unstable, UnstableReason,
};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
@ -161,7 +162,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
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 +210,13 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}
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);
}
let stab = stab.map(|(stab, span)| {
// Error if prohibited, or can't inherit anything from a container.
if kind == AnnotationKind::Prohibited
@ -613,6 +621,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(),
};
@ -673,6 +682,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 +735,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

View file

@ -1218,6 +1218,7 @@ symbols! {
rustc_conversion_suggestion,
rustc_deallocator,
rustc_def_path,
rustc_default_body_unstable,
rustc_diagnostic_item,
rustc_diagnostic_macros,
rustc_dirty,

View file

@ -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" }

View file

@ -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};
@ -1103,12 +1104,28 @@ 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, .. } => 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 => {}
}
}
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)) {

View file

@ -121,13 +121,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;
@ -661,6 +662,37 @@ 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<Symbol>,
issue: Option<NonZeroU32>,
) {
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}'"),
};
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.
fn bounds_from_generic_predicates<'tcx>(
tcx: TyCtxt<'tcx>,

View file

@ -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)
}
}

View file

@ -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 not all trait items implemented, missing: `CONSTANT` [E0046]
//~| ERROR not all trait items implemented, missing: `fun` [E0046]
impl Equal for Type {
//~^ ERROR not all trait items implemented, missing: `eq` [E0046]
fn neq(&self, other: &Self) -> bool {
false
}
}

View file

@ -0,0 +1,38 @@
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[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[E0046]: not all trait items implemented, missing: `eq`
--> $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 | | }
| |_^
|
= 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 E0046`.

View file

@ -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
}
}

View file

@ -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
}
}