Skip to content

Commit 2db927d

Browse files
committed
Add #[default_method_body_is_const]
1 parent a84d1b2 commit 2db927d

File tree

5 files changed

+82
-22
lines changed

5 files changed

+82
-22
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
470470

471471
rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
472472
rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
473+
gated!(
474+
default_method_body_is_const, AssumedUsed, template!(Word), const_trait_impl,
475+
"the `#[default_method_body_is_const]` attribute marks a default method of a trait \
476+
as const, so it does not need to be duplicated by a const impl."
477+
),
473478

474479
// ==========================================================================
475480
// Internal attributes, Layout related:

compiler/rustc_passes/src/check_const.rs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//! through, but errors for structured control flow in a `const` should be emitted here.
99
1010
use rustc_attr as attr;
11+
use rustc_data_structures::stable_set::FxHashSet;
1112
use rustc_errors::struct_span_err;
1213
use rustc_hir as hir;
1314
use rustc_hir::def_id::LocalDefId;
@@ -85,34 +86,46 @@ impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor<
8586
if let hir::ItemKind::Impl(ref imp) = item.kind {
8687
if let hir::Constness::Const = imp.constness {
8788
let did = imp.of_trait.as_ref()?.trait_def_id()?;
88-
let trait_fn_cnt = self
89-
.tcx
90-
.associated_item_def_ids(did)
91-
.iter()
92-
.filter(|did| {
93-
matches!(
94-
self.tcx.associated_item(**did),
95-
ty::AssocItem { kind: ty::AssocKind::Fn, .. }
96-
)
97-
})
98-
.count();
89+
let mut to_implement = FxHashSet::default();
90+
91+
for did in self.tcx.associated_item_def_ids(did) {
92+
if let ty::AssocItem {
93+
kind: ty::AssocKind::Fn, ident, defaultness, ..
94+
} = self.tcx.associated_item(*did)
95+
{
96+
match (
97+
self.tcx.has_attr(*did, sym::default_method_body_is_const),
98+
defaultness.has_value(),
99+
) {
100+
(false, true) => {
101+
to_implement.insert(ident);
102+
}
103+
// ignore functions that do not have default bodies
104+
// if those are unimplemented it will be catched by
105+
// typeck.
106+
_ => {}
107+
}
108+
}
109+
}
99110

100-
let impl_fn_cnt = imp
111+
for it in imp
101112
.items
102113
.iter()
103114
.filter(|it| matches!(it.kind, hir::AssocItemKind::Fn { .. }))
104-
.count();
115+
{
116+
to_implement.remove(&it.ident);
117+
}
105118

106-
// number of trait functions unequal to functions in impl,
107-
// meaning that one or more provided/default functions of the
108-
// trait are used.
109-
if trait_fn_cnt != impl_fn_cnt {
119+
// all nonconst trait functions (not marked with #[default_method_body_is_const])
120+
// must be implemented
121+
if !to_implement.is_empty() {
110122
self.tcx
111123
.sess
112124
.struct_span_err(
113125
item.span,
114-
"const trait implementations may not use default functions",
126+
"const trait implementations may not use non-const default functions",
115127
)
128+
.note(&format!("`{}` not implemented", to_implement.into_iter().map(|id| id.to_string()).collect::<Vec<_>>().join("`, `")))
116129
.emit();
117130
}
118131
}

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ symbols! {
462462
decode,
463463
default_alloc_error_handler,
464464
default_lib_allocator,
465+
default_method_body_is_const,
465466
default_type_parameter_fallback,
466467
default_type_params,
467468
delay_span_bug_from_inside_query,

src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,31 @@ trait Tr {
88
println!("lul");
99
self.req();
1010
}
11+
12+
#[default_method_body_is_const]
13+
fn default() {}
1114
}
1215

1316
struct S;
1417

1518
impl const Tr for S {
1619
fn req(&self) {}
20+
} //~^^ ERROR const trait implementations may not use non-const default functions
21+
22+
impl const Tr for u8 {
23+
fn req(&self) {}
24+
fn prov(&self) {}
1725
}
18-
//~^^^ ERROR const trait implementations may not use default functions
26+
27+
impl const Tr for u16 {
28+
fn prov(&self) {}
29+
fn default() {}
30+
} //~^^^ ERROR not all trait items implemented
31+
32+
33+
impl const Tr for u32 {
34+
fn req(&self) {}
35+
fn default() {}
36+
} //~^^^ ERROR const trait implementations may not use non-const default functions
1937

2038
fn main() {}
Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
1-
error: const trait implementations may not use default functions
2-
--> $DIR/impl-with-default-fn.rs:15:1
1+
error: const trait implementations may not use non-const default functions
2+
--> $DIR/impl-with-default-fn.rs:18:1
33
|
44
LL | / impl const Tr for S {
55
LL | | fn req(&self) {}
66
LL | | }
77
| |_^
8+
|
9+
= note: `prov` not implemented
10+
11+
error: const trait implementations may not use non-const default functions
12+
--> $DIR/impl-with-default-fn.rs:33:1
13+
|
14+
LL | / impl const Tr for u32 {
15+
LL | | fn req(&self) {}
16+
LL | | fn default() {}
17+
LL | | }
18+
| |_^
19+
|
20+
= note: `prov` not implemented
21+
22+
error[E0046]: not all trait items implemented, missing: `req`
23+
--> $DIR/impl-with-default-fn.rs:27:1
24+
|
25+
LL | fn req(&self);
26+
| -------------- `req` from trait
27+
...
28+
LL | impl const Tr for u16 {
29+
| ^^^^^^^^^^^^^^^^^^^^^ missing `req` in implementation
830

9-
error: aborting due to previous error
31+
error: aborting due to 3 previous errors
1032

33+
For more information about this error, try `rustc --explain E0046`.

0 commit comments

Comments
 (0)