1
Fork 0

Auto merge of #86857 - fee1-dead:add-attr, r=oli-obk

Add #[default_method_body_is_const]

`@rustbot` label F-const_trait_impl
This commit is contained in:
bors 2021-07-13 06:59:34 +00:00
commit 394804bb23
19 changed files with 265 additions and 34 deletions

View file

@ -98,6 +98,9 @@ impl CheckAttrVisitor<'tcx> {
| sym::rustc_if_this_changed
| sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target),
sym::default_method_body_is_const => {
self.check_default_method_body_is_const(attr, span, target)
}
_ => true,
};
// lint-only checks
@ -1465,6 +1468,29 @@ impl CheckAttrVisitor<'tcx> {
}
}
}
/// default_method_body_is_const should only be applied to trait methods with default bodies.
fn check_default_method_body_is_const(
&self,
attr: &Attribute,
span: &Span,
target: Target,
) -> bool {
match target {
Target::Method(MethodKind::Trait { body: true }) => true,
_ => {
self.tcx
.sess
.struct_span_err(
attr.span,
"attribute should be applied to a trait method with body",
)
.span_label(*span, "not a trait method or missing a body")
.emit();
false
}
}
}
}
impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {

View file

@ -8,6 +8,7 @@
//! through, but errors for structured control flow in a `const` should be emitted here.
use rustc_attr as attr;
use rustc_data_structures::stable_set::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
@ -85,34 +86,41 @@ impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<
if let hir::ItemKind::Impl(ref imp) = item.kind {
if let hir::Constness::Const = imp.constness {
let did = imp.of_trait.as_ref()?.trait_def_id()?;
let trait_fn_cnt = self
.tcx
.associated_item_def_ids(did)
.iter()
.filter(|did| {
matches!(
self.tcx.associated_item(**did),
ty::AssocItem { kind: ty::AssocKind::Fn, .. }
)
})
.count();
let mut to_implement = FxHashSet::default();
let impl_fn_cnt = imp
for did in self.tcx.associated_item_def_ids(did) {
if let ty::AssocItem {
kind: ty::AssocKind::Fn, ident, defaultness, ..
} = self.tcx.associated_item(*did)
{
// we can ignore functions that do not have default bodies:
// if those are unimplemented it will be catched by typeck.
if defaultness.has_value()
&& !self.tcx.has_attr(*did, sym::default_method_body_is_const)
{
to_implement.insert(ident);
}
}
}
for it in imp
.items
.iter()
.filter(|it| matches!(it.kind, hir::AssocItemKind::Fn { .. }))
.count();
{
to_implement.remove(&it.ident);
}
// number of trait functions unequal to functions in impl,
// meaning that one or more provided/default functions of the
// trait are used.
if trait_fn_cnt != impl_fn_cnt {
// all nonconst trait functions (not marked with #[default_method_body_is_const])
// must be implemented
if !to_implement.is_empty() {
self.tcx
.sess
.struct_span_err(
item.span,
"const trait implementations may not use default functions",
"const trait implementations may not use non-const default functions",
)
.note(&format!("`{}` not implemented", to_implement.into_iter().map(|id| id.to_string()).collect::<Vec<_>>().join("`, `")))
.emit();
}
}