Skip to content

Commit 394804b

Browse files
committed
Auto merge of #86857 - fee1-dead:add-attr, r=oli-obk
Add #[default_method_body_is_const] `@rustbot` label F-const_trait_impl
2 parents 1f0db5e + 7c9e214 commit 394804b

File tree

19 files changed

+265
-34
lines changed

19 files changed

+265
-34
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+6
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
349349
),
350350

351351
gated!(cmse_nonsecure_entry, AssumedUsed, template!(Word), experimental!(cmse_nonsecure_entry)),
352+
// RFC 2632
353+
gated!(
354+
default_method_body_is_const, AssumedUsed, template!(Word), const_trait_impl,
355+
"`default_method_body_is_const` is a temporary placeholder for declaring default bodies \
356+
as `const`, which may be removed or renamed in the future."
357+
),
352358

353359
// ==========================================================================
354360
// Internal attributes: Stability, deprecation, and unsafe:

compiler/rustc_metadata/src/rmeta/decoder.rs

+4
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
952952
self.get_impl_data(id).defaultness
953953
}
954954

955+
fn get_impl_constness(&self, id: DefIndex) -> hir::Constness {
956+
self.get_impl_data(id).constness
957+
}
958+
955959
fn get_coerce_unsized_info(&self, id: DefIndex) -> Option<ty::adjustment::CoerceUnsizedInfo> {
956960
self.get_impl_data(id).coerce_unsized_info
957961
}

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
168168
is_no_builtins => { cdata.root.no_builtins }
169169
symbol_mangling_version => { cdata.root.symbol_mangling_version }
170170
impl_defaultness => { cdata.get_impl_defaultness(def_id.index) }
171+
impl_constness => { cdata.get_impl_constness(def_id.index) }
171172
reachable_non_generics => {
172173
let reachable_non_generics = tcx
173174
.exported_symbols(cdata.cnum)

compiler/rustc_metadata/src/rmeta/encoder.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -1412,7 +1412,7 @@ impl EncodeContext<'a, 'tcx> {
14121412
adt_def.repr,
14131413
)
14141414
}
1415-
hir::ItemKind::Impl(hir::Impl { defaultness, .. }) => {
1415+
hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
14161416
let trait_ref = self.tcx.impl_trait_ref(def_id);
14171417
let polarity = self.tcx.impl_polarity(def_id);
14181418
let parent = if let Some(trait_ref) = trait_ref {
@@ -1437,8 +1437,13 @@ impl EncodeContext<'a, 'tcx> {
14371437
}
14381438
});
14391439

1440-
let data =
1441-
ImplData { polarity, defaultness, parent_impl: parent, coerce_unsized_info };
1440+
let data = ImplData {
1441+
polarity,
1442+
defaultness,
1443+
constness,
1444+
parent_impl: parent,
1445+
coerce_unsized_info,
1446+
};
14421447

14431448
EntryKind::Impl(self.lazy(data))
14441449
}

compiler/rustc_metadata/src/rmeta/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ struct TraitData {
390390
#[derive(TyEncodable, TyDecodable)]
391391
struct ImplData {
392392
polarity: ty::ImplPolarity,
393+
constness: hir::Constness,
393394
defaultness: hir::Defaultness,
394395
parent_impl: Option<DefId>,
395396

compiler/rustc_middle/src/hir/map/mod.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_index::vec::Idx;
1818
use rustc_span::def_id::StableCrateId;
1919
use rustc_span::hygiene::MacroKind;
2020
use rustc_span::source_map::Spanned;
21-
use rustc_span::symbol::{kw, Ident, Symbol};
21+
use rustc_span::symbol::{kw, sym, Ident, Symbol};
2222
use rustc_span::Span;
2323
use rustc_target::spec::abi::Abi;
2424

@@ -465,6 +465,9 @@ impl<'hir> Map<'hir> {
465465
/// Returns the `ConstContext` of the body associated with this `LocalDefId`.
466466
///
467467
/// Panics if `LocalDefId` does not have an associated body.
468+
///
469+
/// This should only be used for determining the context of a body, a return
470+
/// value of `Some` does not always suggest that the owner of the body is `const`.
468471
pub fn body_const_context(&self, did: LocalDefId) -> Option<ConstContext> {
469472
let hir_id = self.local_def_id_to_hir_id(did);
470473
let ccx = match self.body_owner_kind(hir_id) {
@@ -473,6 +476,11 @@ impl<'hir> Map<'hir> {
473476

474477
BodyOwnerKind::Fn if self.tcx.is_constructor(did.to_def_id()) => return None,
475478
BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(did.to_def_id()) => ConstContext::ConstFn,
479+
BodyOwnerKind::Fn
480+
if self.tcx.has_attr(did.to_def_id(), sym::default_method_body_is_const) =>
481+
{
482+
ConstContext::ConstFn
483+
}
476484
BodyOwnerKind::Fn | BodyOwnerKind::Closure => return None,
477485
};
478486

compiler/rustc_middle/src/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,10 @@ rustc_queries! {
11441144
desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) }
11451145
}
11461146

1147+
query impl_constness(def_id: DefId) -> hir::Constness {
1148+
desc { |tcx| "looking up whether `{}` is a const impl", tcx.def_path_str(def_id) }
1149+
}
1150+
11471151
query check_item_well_formed(key: LocalDefId) -> () {
11481152
desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) }
11491153
}

compiler/rustc_mir/src/const_eval/machine.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
235235
// sensitive check here. But we can at least rule out functions that are not const
236236
// at all.
237237
if !ecx.tcx.is_const_fn_raw(def.did) {
238-
// Some functions we support even if they are non-const -- but avoid testing
239-
// that for const fn!
240-
ecx.hook_panic_fn(instance, args)?;
241-
// We certainly do *not* want to actually call the fn
242-
// though, so be sure we return here.
243-
throw_unsup_format!("calling non-const function `{}`", instance)
238+
// allow calling functions marked with #[default_method_body_is_const].
239+
if !ecx.tcx.has_attr(def.did, sym::default_method_body_is_const) {
240+
// Some functions we support even if they are non-const -- but avoid testing
241+
// that for const fn!
242+
ecx.hook_panic_fn(instance, args)?;
243+
// We certainly do *not* want to actually call the fn
244+
// though, so be sure we return here.
245+
throw_unsup_format!("calling non-const function `{}`", instance)
246+
}
244247
}
245248
}
246249
// This is a const fn. Call it.

compiler/rustc_mir/src/transform/check_consts/validation.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,34 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
886886
}
887887

888888
if !tcx.is_const_fn_raw(callee) {
889-
self.check_op(ops::FnCallNonConst);
890-
return;
889+
let mut permitted = false;
890+
891+
let callee_trait = tcx.trait_of_item(callee);
892+
if let Some(trait_id) = callee_trait {
893+
if tcx.has_attr(caller, sym::default_method_body_is_const) {
894+
// permit call to non-const fn when caller has default_method_body_is_const..
895+
if tcx.trait_of_item(caller) == callee_trait {
896+
// ..and caller and callee are in the same trait.
897+
permitted = true;
898+
}
899+
}
900+
let mut const_impls = true;
901+
tcx.for_each_relevant_impl(trait_id, substs.type_at(0), |imp| {
902+
if const_impls {
903+
if let hir::Constness::NotConst = tcx.impl_constness(imp) {
904+
const_impls = false;
905+
}
906+
}
907+
});
908+
if const_impls {
909+
permitted = true;
910+
}
911+
}
912+
913+
if !permitted {
914+
self.check_op(ops::FnCallNonConst);
915+
return;
916+
}
891917
}
892918

893919
// If the `const fn` we are trying to call is not const-stable, ensure that we have

compiler/rustc_passes/src/check_attr.rs

+26
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ impl CheckAttrVisitor<'tcx> {
9898
| sym::rustc_if_this_changed
9999
| sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
100100
sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target),
101+
sym::default_method_body_is_const => {
102+
self.check_default_method_body_is_const(attr, span, target)
103+
}
101104
_ => true,
102105
};
103106
// lint-only checks
@@ -1465,6 +1468,29 @@ impl CheckAttrVisitor<'tcx> {
14651468
}
14661469
}
14671470
}
1471+
1472+
/// default_method_body_is_const should only be applied to trait methods with default bodies.
1473+
fn check_default_method_body_is_const(
1474+
&self,
1475+
attr: &Attribute,
1476+
span: &Span,
1477+
target: Target,
1478+
) -> bool {
1479+
match target {
1480+
Target::Method(MethodKind::Trait { body: true }) => true,
1481+
_ => {
1482+
self.tcx
1483+
.sess
1484+
.struct_span_err(
1485+
attr.span,
1486+
"attribute should be applied to a trait method with body",
1487+
)
1488+
.span_label(*span, "not a trait method or missing a body")
1489+
.emit();
1490+
false
1491+
}
1492+
}
1493+
}
14681494
}
14691495

14701496
impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {

compiler/rustc_passes/src/check_const.rs

+26-18
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,41 @@ 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+
// we can ignore functions that do not have default bodies:
97+
// if those are unimplemented it will be catched by typeck.
98+
if defaultness.has_value()
99+
&& !self.tcx.has_attr(*did, sym::default_method_body_is_const)
100+
{
101+
to_implement.insert(ident);
102+
}
103+
}
104+
}
99105

100-
let impl_fn_cnt = imp
106+
for it in imp
101107
.items
102108
.iter()
103109
.filter(|it| matches!(it.kind, hir::AssocItemKind::Fn { .. }))
104-
.count();
110+
{
111+
to_implement.remove(&it.ident);
112+
}
105113

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 {
114+
// all nonconst trait functions (not marked with #[default_method_body_is_const])
115+
// must be implemented
116+
if !to_implement.is_empty() {
110117
self.tcx
111118
.sess
112119
.struct_span_err(
113120
item.span,
114-
"const trait implementations may not use default functions",
121+
"const trait implementations may not use non-const default functions",
115122
)
123+
.note(&format!("`{}` not implemented", to_implement.into_iter().map(|id| id.to_string()).collect::<Vec<_>>().join("`, `")))
116124
.emit();
117125
}
118126
}

compiler/rustc_span/src/symbol.rs

+1
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,

compiler/rustc_ty_utils/src/ty.rs

+11
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,16 @@ fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness {
168168
}
169169
}
170170

171+
fn impl_constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness {
172+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
173+
let item = tcx.hir().expect_item(hir_id);
174+
if let hir::ItemKind::Impl(impl_) = &item.kind {
175+
impl_.constness
176+
} else {
177+
bug!("`impl_constness` called on {:?}", item);
178+
}
179+
}
180+
171181
/// Calculates the `Sized` constraint.
172182
///
173183
/// In fact, there are only a few options for the types in the constraint:
@@ -535,6 +545,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
535545
instance_def_size_estimate,
536546
issue33140_self_ty,
537547
impl_defaultness,
548+
impl_constness,
538549
conservative_is_privately_uninhabited: conservative_is_privately_uninhabited_raw,
539550
..*providers
540551
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(const_trait_impl)]
2+
#![allow(incomplete_features)]
3+
4+
#[default_method_body_is_const] //~ ERROR attribute should be applied
5+
trait A {
6+
#[default_method_body_is_const] //~ ERROR attribute should be applied
7+
fn no_body(self);
8+
9+
#[default_method_body_is_const]
10+
fn correct_use(&self) {}
11+
}
12+
13+
#[default_method_body_is_const] //~ ERROR attribute should be applied
14+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
error: attribute should be applied to a trait method with body
2+
--> $DIR/attr-misuse.rs:4:1
3+
|
4+
LL | #[default_method_body_is_const]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
LL | / trait A {
7+
LL | | #[default_method_body_is_const]
8+
LL | | fn no_body(self);
9+
LL | |
10+
LL | | #[default_method_body_is_const]
11+
LL | | fn correct_use(&self) {}
12+
LL | | }
13+
| |_- not a trait method or missing a body
14+
15+
error: attribute should be applied to a trait method with body
16+
--> $DIR/attr-misuse.rs:13:1
17+
|
18+
LL | #[default_method_body_is_const]
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
LL | fn main() {}
21+
| ------------ not a trait method or missing a body
22+
23+
error: attribute should be applied to a trait method with body
24+
--> $DIR/attr-misuse.rs:6:5
25+
|
26+
LL | #[default_method_body_is_const]
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28+
LL | fn no_body(self);
29+
| ----------------- not a trait method or missing a body
30+
31+
error: aborting due to 3 previous errors
32+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#![feature(const_trait_impl)]
2+
#![feature(const_fn_trait_bound)] // FIXME is this needed?
3+
#![allow(incomplete_features)]
4+
5+
trait ConstDefaultFn: Sized {
6+
fn b(self);
7+
8+
#[default_method_body_is_const]
9+
fn a(self) {
10+
self.b();
11+
}
12+
}
13+
14+
struct NonConstImpl;
15+
struct ConstImpl;
16+
17+
impl ConstDefaultFn for NonConstImpl {
18+
fn b(self) {}
19+
}
20+
21+
impl const ConstDefaultFn for ConstImpl {
22+
fn b(self) {}
23+
}
24+
25+
const fn test() {
26+
NonConstImpl.a();
27+
//~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple variants
28+
ConstImpl.a();
29+
}
30+
31+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
2+
--> $DIR/const-default-method-bodies.rs:26:5
3+
|
4+
LL | NonConstImpl.a();
5+
| ^^^^^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0015`.

0 commit comments

Comments
 (0)