From 2db927d8d82260759e4aaa183dbebd0adbb00177 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 4 Jul 2021 12:24:20 +0800 Subject: [PATCH 01/12] Add #[default_method_body_is_const] --- compiler/rustc_feature/src/builtin_attrs.rs | 5 ++ compiler/rustc_passes/src/check_const.rs | 49 ++++++++++++------- compiler/rustc_span/src/symbol.rs | 1 + .../impl-with-default-fn.rs | 20 +++++++- .../impl-with-default-fn.stderr | 29 +++++++++-- 5 files changed, 82 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 259a6328a22f6..dbdc14dced8c0 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -470,6 +470,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL), rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE), + gated!( + default_method_body_is_const, AssumedUsed, template!(Word), const_trait_impl, + "the `#[default_method_body_is_const]` attribute marks a default method of a trait \ + as const, so it does not need to be duplicated by a const impl." + ), // ========================================================================== // Internal attributes, Layout related: diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index d783852aacadd..eaeec19eb3148 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -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,46 @@ 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(); + + 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) + { + match ( + self.tcx.has_attr(*did, sym::default_method_body_is_const), + defaultness.has_value(), + ) { + (false, true) => { + to_implement.insert(ident); + } + // ignore functions that do not have default bodies + // if those are unimplemented it will be catched by + // typeck. + _ => {} + } + } + } - let impl_fn_cnt = imp + 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::>().join("`, `"))) .emit(); } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9051c9d69b5c1..f7a11876f7de4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -462,6 +462,7 @@ symbols! { decode, default_alloc_error_handler, default_lib_allocator, + default_method_body_is_const, default_type_parameter_fallback, default_type_params, delay_span_bug_from_inside_query, diff --git a/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs b/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs index 4ff4fa0d83b2c..def7c34b4e50e 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.rs @@ -8,13 +8,31 @@ trait Tr { println!("lul"); self.req(); } + + #[default_method_body_is_const] + fn default() {} } struct S; impl const Tr for S { fn req(&self) {} +} //~^^ ERROR const trait implementations may not use non-const default functions + +impl const Tr for u8 { + fn req(&self) {} + fn prov(&self) {} } -//~^^^ ERROR const trait implementations may not use default functions + +impl const Tr for u16 { + fn prov(&self) {} + fn default() {} +} //~^^^ ERROR not all trait items implemented + + +impl const Tr for u32 { + fn req(&self) {} + fn default() {} +} //~^^^ ERROR const trait implementations may not use non-const default functions fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr b/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr index 51a7b18fa8d55..eb7f899b4dee2 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/impl-with-default-fn.stderr @@ -1,10 +1,33 @@ -error: const trait implementations may not use default functions - --> $DIR/impl-with-default-fn.rs:15:1 +error: const trait implementations may not use non-const default functions + --> $DIR/impl-with-default-fn.rs:18:1 | LL | / impl const Tr for S { LL | | fn req(&self) {} LL | | } | |_^ + | + = note: `prov` not implemented + +error: const trait implementations may not use non-const default functions + --> $DIR/impl-with-default-fn.rs:33:1 + | +LL | / impl const Tr for u32 { +LL | | fn req(&self) {} +LL | | fn default() {} +LL | | } + | |_^ + | + = note: `prov` not implemented + +error[E0046]: not all trait items implemented, missing: `req` + --> $DIR/impl-with-default-fn.rs:27:1 + | +LL | fn req(&self); + | -------------- `req` from trait +... +LL | impl const Tr for u16 { + | ^^^^^^^^^^^^^^^^^^^^^ missing `req` in implementation -error: aborting due to previous error +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0046`. From 3660a4e97259a23b9a24c30aa097932ae6f0eb18 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 4 Jul 2021 23:50:34 +0800 Subject: [PATCH 02/12] Applied suggestions --- compiler/rustc_feature/src/builtin_attrs.rs | 11 ++++++----- compiler/rustc_passes/src/check_const.rs | 17 ++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index dbdc14dced8c0..24a5a007dedf6 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -349,6 +349,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!(cmse_nonsecure_entry, AssumedUsed, template!(Word), experimental!(cmse_nonsecure_entry)), + // RFC 2632 + gated!( + default_method_body_is_const, AssumedUsed, template!(Word), const_trait_impl, + "`default_method_body_is_const` is a temporary placeholder for declaring default bodies \ + as `const`, which may be removed or renamed in the future." + ), // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: @@ -470,11 +476,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL), rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE), - gated!( - default_method_body_is_const, AssumedUsed, template!(Word), const_trait_impl, - "the `#[default_method_body_is_const]` attribute marks a default method of a trait \ - as const, so it does not need to be duplicated by a const impl." - ), // ========================================================================== // Internal attributes, Layout related: diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index eaeec19eb3148..6ee54cfe37f30 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -93,17 +93,12 @@ impl<'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for CheckConstTraitVisitor< kind: ty::AssocKind::Fn, ident, defaultness, .. } = self.tcx.associated_item(*did) { - match ( - self.tcx.has_attr(*did, sym::default_method_body_is_const), - defaultness.has_value(), - ) { - (false, true) => { - to_implement.insert(ident); - } - // ignore functions that do not have default bodies - // if those are unimplemented it will be catched by - // typeck. - _ => {} + // 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); } } } From d8d4cc3b98b78fae879b9540f237ad31268d6430 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 6 Jul 2021 13:05:24 +0800 Subject: [PATCH 03/12] Treat trait fns marked with the attr as const --- .../rustc_mir/src/const_eval/fn_queries.rs | 5 ++- .../const-default-method-bodies.rs | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs diff --git a/compiler/rustc_mir/src/const_eval/fn_queries.rs b/compiler/rustc_mir/src/const_eval/fn_queries.rs index 40419a4d201ac..fc7877b0a0b8e 100644 --- a/compiler/rustc_mir/src/const_eval/fn_queries.rs +++ b/compiler/rustc_mir/src/const_eval/fn_queries.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{sym, Symbol}; use rustc_target::spec::abi::Abi; /// Whether the `def_id` counts as const fn in your current crate, considering all active @@ -60,6 +60,9 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { return true; } + if tcx.has_attr(def_id, sym::default_method_body_is_const) { + return true; + } // If the function itself is not annotated with `const`, it may still be a `const fn` // if it resides in a const trait impl. is_parent_const_impl_raw(tcx, hir_id) diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs b/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs new file mode 100644 index 0000000000000..bc2fcf81c63e3 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs @@ -0,0 +1,32 @@ +// TODO fix this test + +#![feature(const_trait_impl)] +#![allow(incomplete_features)] + +trait ConstDefaultFn: Sized { + fn b(self); + + #[default_method_body_is_const] + fn a(self) { + self.b(); + } +} + +struct NonConstImpl; +struct ConstImpl; + +impl ConstDefaultFn for NonConstImpl { + fn b(self) {} +} + +impl const ConstDefaultFn for ConstImpl { + fn b(self) {} +} + +const fn test() { + NonConstImpl.a(); + //~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple variants + ConstImpl.a(); +} + +fn main() {} From 56d79adf3be666a4583ffeddd276de5c8b6ab32e Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 7 Jul 2021 11:52:40 +0800 Subject: [PATCH 04/12] Skip check for calling functions in same trait --- .../src/transform/check_consts/validation.rs | 14 +++++++++++--- .../const-default-method-bodies.rs | 6 ++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index 6216ff6656e28..e6a4f08836adc 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -885,9 +885,17 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { return; } - if !tcx.is_const_fn_raw(callee) { - self.check_op(ops::FnCallNonConst); - return; + let caller_has_attr = tcx.has_attr(caller, sym::default_method_body_is_const); + let in_same_trait = match (tcx.trait_of_item(caller), tcx.trait_of_item(callee)) { + (Some(t1), Some(t2)) => t1 == t2, + _ => false + }; + + if !(caller_has_attr && in_same_trait) { + if !tcx.is_const_fn_raw(callee) { + self.check_op(ops::FnCallNonConst); + return; + } } // If the `const fn` we are trying to call is not const-stable, ensure that we have diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs b/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs index bc2fcf81c63e3..6a1a72f29709d 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs @@ -1,6 +1,8 @@ -// TODO fix this test +// check-pass +// TODO remove this^ #![feature(const_trait_impl)] +#![feature(const_fn_trait_bound)] // FIXME is this needed? #![allow(incomplete_features)] trait ConstDefaultFn: Sized { @@ -25,7 +27,7 @@ impl const ConstDefaultFn for ConstImpl { const fn test() { NonConstImpl.a(); - //~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple variants + // TODO ~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple variants ConstImpl.a(); } From 032cbe4ccec5bb6565a5c7656971aa83f56774a9 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jul 2021 11:13:52 +0800 Subject: [PATCH 05/12] Check if the attribute is applied correctly --- compiler/rustc_passes/src/check_attr.rs | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d0795841c5359..71231830e99a7 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -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> { From 89d190f090c06cca830831158fc052f902ddc1df Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jul 2021 11:15:11 +0800 Subject: [PATCH 06/12] Add impl_constness query --- compiler/rustc_metadata/src/rmeta/decoder.rs | 4 ++++ .../rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 11 ++++++++--- compiler/rustc_metadata/src/rmeta/mod.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 4 ++++ compiler/rustc_ty_utils/src/ty.rs | 11 +++++++++++ 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 3860d0daadf98..344c5e8ee6187 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -959,6 +959,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.get_impl_data(id).defaultness } + fn get_impl_constness(&self, id: DefIndex) -> hir::Constness { + self.get_impl_data(id).constness + } + fn get_coerce_unsized_info(&self, id: DefIndex) -> Option { self.get_impl_data(id).coerce_unsized_info } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 68cf4304b7165..c0e85d82f7e27 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -168,6 +168,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, is_no_builtins => { cdata.root.no_builtins } symbol_mangling_version => { cdata.root.symbol_mangling_version } impl_defaultness => { cdata.get_impl_defaultness(def_id.index) } + impl_constness => { cdata.get_impl_constness(def_id.index) } reachable_non_generics => { let reachable_non_generics = tcx .exported_symbols(cdata.cnum) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 2478d15e554c8..5b10d53159ebd 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1412,7 +1412,7 @@ impl EncodeContext<'a, 'tcx> { adt_def.repr, ) } - hir::ItemKind::Impl(hir::Impl { defaultness, .. }) => { + hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => { let trait_ref = self.tcx.impl_trait_ref(def_id); let polarity = self.tcx.impl_polarity(def_id); let parent = if let Some(trait_ref) = trait_ref { @@ -1437,8 +1437,13 @@ impl EncodeContext<'a, 'tcx> { } }); - let data = - ImplData { polarity, defaultness, parent_impl: parent, coerce_unsized_info }; + let data = ImplData { + polarity, + defaultness, + constness, + parent_impl: parent, + coerce_unsized_info, + }; EntryKind::Impl(self.lazy(data)) } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 3793058062347..0d67ff32a3f3e 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -390,6 +390,7 @@ struct TraitData { #[derive(TyEncodable, TyDecodable)] struct ImplData { polarity: ty::ImplPolarity, + constness: hir::Constness, defaultness: hir::Defaultness, parent_impl: Option, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 9a2f1149316e2..1a1bb37f70a59 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1136,6 +1136,10 @@ rustc_queries! { desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } } + query impl_constness(def_id: DefId) -> hir::Constness { + desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + } + query check_item_well_formed(key: LocalDefId) -> () { desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index ffde9b79265a5..b0d644ae028ce 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -168,6 +168,16 @@ fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness { } } +fn impl_constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + let item = tcx.hir().expect_item(hir_id); + if let hir::ItemKind::Impl(impl_) = &item.kind { + impl_.constness + } else { + bug!("`impl_constness` called on {:?}", item); + } +} + /// Calculates the `Sized` constraint. /// /// 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) { instance_def_size_estimate, issue33140_self_ty, impl_defaultness, + impl_constness, conservative_is_privately_uninhabited: conservative_is_privately_uninhabited_raw, ..*providers }; From 27e863b3df28aeb9b1cfe693b1a6d09a289b7ad2 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jul 2021 11:16:03 +0800 Subject: [PATCH 07/12] functions marked with attr are not const --- compiler/rustc_middle/src/hir/map/mod.rs | 10 +++++++++- compiler/rustc_mir/src/const_eval/fn_queries.rs | 3 --- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 07b39c97c492a..f37ccf1787dd6 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -18,7 +18,7 @@ use rustc_index::vec::Idx; use rustc_span::def_id::StableCrateId; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::spec::abi::Abi; @@ -457,6 +457,9 @@ impl<'hir> Map<'hir> { /// Returns the `ConstContext` of the body associated with this `LocalDefId`. /// /// Panics if `LocalDefId` does not have an associated body. + /// + /// This should only be used for determining the context of a body, a return + /// value of `Some` does not always suggest that the owner of the body is `const`. pub fn body_const_context(&self, did: LocalDefId) -> Option { let hir_id = self.local_def_id_to_hir_id(did); let ccx = match self.body_owner_kind(hir_id) { @@ -465,6 +468,11 @@ impl<'hir> Map<'hir> { BodyOwnerKind::Fn if self.tcx.is_constructor(did.to_def_id()) => return None, BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(did.to_def_id()) => ConstContext::ConstFn, + BodyOwnerKind::Fn + if self.tcx.has_attr(did.to_def_id(), sym::default_method_body_is_const) => + { + ConstContext::ConstFn + } BodyOwnerKind::Fn | BodyOwnerKind::Closure => return None, }; diff --git a/compiler/rustc_mir/src/const_eval/fn_queries.rs b/compiler/rustc_mir/src/const_eval/fn_queries.rs index fc7877b0a0b8e..1ff8df0884d10 100644 --- a/compiler/rustc_mir/src/const_eval/fn_queries.rs +++ b/compiler/rustc_mir/src/const_eval/fn_queries.rs @@ -60,9 +60,6 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool { return true; } - if tcx.has_attr(def_id, sym::default_method_body_is_const) { - return true; - } // If the function itself is not annotated with `const`, it may still be a `const fn` // if it resides in a const trait impl. is_parent_const_impl_raw(tcx, hir_id) From 554fad7bdac1655119f2450d3286463aa2290200 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jul 2021 11:16:46 +0800 Subject: [PATCH 08/12] Permit calls to default const fns of impl const --- .../src/transform/check_consts/validation.rs | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index e6a4f08836adc..646ae8ced7eb4 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -885,14 +885,32 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { return; } - let caller_has_attr = tcx.has_attr(caller, sym::default_method_body_is_const); - let in_same_trait = match (tcx.trait_of_item(caller), tcx.trait_of_item(callee)) { - (Some(t1), Some(t2)) => t1 == t2, - _ => false - }; + if !tcx.is_const_fn_raw(callee) { + let mut permitted = false; + + let callee_trait = tcx.trait_of_item(callee); + if let Some(trait_id) = callee_trait { + if tcx.has_attr(caller, sym::default_method_body_is_const) { + // permit call to non-const fn when caller has default_method_body_is_const.. + if tcx.trait_of_item(caller) == callee_trait { + // ..and caller and callee are in the same trait. + permitted = true; + } + } + let mut const_impls = true; + tcx.for_each_relevant_impl(trait_id, substs.type_at(0), |imp| { + if const_impls { + if let hir::Constness::NotConst = tcx.impl_constness(imp) { + const_impls = false; + } + } + }); + if const_impls { + permitted = true; + } + } - if !(caller_has_attr && in_same_trait) { - if !tcx.is_const_fn_raw(callee) { + if !permitted { self.check_op(ops::FnCallNonConst); return; } From 5e695bbba1a682735e850e4e348804edb169bf53 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jul 2021 11:17:14 +0800 Subject: [PATCH 09/12] Update CTFE to allow fns marked with the attr --- compiler/rustc_mir/src/const_eval/machine.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index 279f414e7fef1..f8b66badb8a4c 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -235,12 +235,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, // sensitive check here. But we can at least rule out functions that are not const // at all. if !ecx.tcx.is_const_fn_raw(def.did) { - // Some functions we support even if they are non-const -- but avoid testing - // that for const fn! - ecx.hook_panic_fn(instance, args)?; - // We certainly do *not* want to actually call the fn - // though, so be sure we return here. - throw_unsup_format!("calling non-const function `{}`", instance) + // allow calling functions marked with #[default_method_body_is_const]. + if !ecx.tcx.has_attr(def.did, sym::default_method_body_is_const) { + // Some functions we support even if they are non-const -- but avoid testing + // that for const fn! + ecx.hook_panic_fn(instance, args)?; + // We certainly do *not* want to actually call the fn + // though, so be sure we return here. + throw_unsup_format!("calling non-const function `{}`", instance) + } } } // This is a const fn. Call it. From a79e08ca2ab4fcc2fd32296e652b62deef0f7c64 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jul 2021 15:17:09 +0800 Subject: [PATCH 10/12] Update tests --- compiler/rustc_mir/src/const_eval/fn_queries.rs | 2 +- .../const-default-method-bodies.rs | 5 +---- .../const-default-method-bodies.stderr | 9 +++++++++ 3 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr diff --git a/compiler/rustc_mir/src/const_eval/fn_queries.rs b/compiler/rustc_mir/src/const_eval/fn_queries.rs index 1ff8df0884d10..40419a4d201ac 100644 --- a/compiler/rustc_mir/src/const_eval/fn_queries.rs +++ b/compiler/rustc_mir/src/const_eval/fn_queries.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::Symbol; use rustc_target::spec::abi::Abi; /// Whether the `def_id` counts as const fn in your current crate, considering all active diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs b/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs index 6a1a72f29709d..d08c01750c379 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs @@ -1,6 +1,3 @@ -// check-pass -// TODO remove this^ - #![feature(const_trait_impl)] #![feature(const_fn_trait_bound)] // FIXME is this needed? #![allow(incomplete_features)] @@ -27,7 +24,7 @@ impl const ConstDefaultFn for ConstImpl { const fn test() { NonConstImpl.a(); - // TODO ~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple variants + //~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple variants ConstImpl.a(); } diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr new file mode 100644 index 0000000000000..d52e83609489b --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr @@ -0,0 +1,9 @@ +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/const-default-method-bodies.rs:26:5 + | +LL | NonConstImpl.a(); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0015`. From 88b29f5fb269eaf3463225b4bf6a04c2ed07669f Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jul 2021 15:58:16 +0800 Subject: [PATCH 11/12] Test for misusing attribute --- compiler/rustc_middle/src/query/mod.rs | 2 +- .../rfc-2632-const-trait-impl/attr-misuse.rs | 14 ++++++++ .../attr-misuse.stderr | 32 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/attr-misuse.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/attr-misuse.stderr diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1a1bb37f70a59..c3b13278c009b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1137,7 +1137,7 @@ rustc_queries! { } query impl_constness(def_id: DefId) -> hir::Constness { - desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + desc { |tcx| "looking up whether `{}` is a const impl", tcx.def_path_str(def_id) } } query check_item_well_formed(key: LocalDefId) -> () { diff --git a/src/test/ui/rfc-2632-const-trait-impl/attr-misuse.rs b/src/test/ui/rfc-2632-const-trait-impl/attr-misuse.rs new file mode 100644 index 0000000000000..338ac3d250536 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/attr-misuse.rs @@ -0,0 +1,14 @@ +#![feature(const_trait_impl)] +#![allow(incomplete_features)] + +#[default_method_body_is_const] //~ ERROR attribute should be applied +trait A { + #[default_method_body_is_const] //~ ERROR attribute should be applied + fn no_body(self); + + #[default_method_body_is_const] + fn correct_use(&self) {} +} + +#[default_method_body_is_const] //~ ERROR attribute should be applied +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/attr-misuse.stderr b/src/test/ui/rfc-2632-const-trait-impl/attr-misuse.stderr new file mode 100644 index 0000000000000..3af71d6ff78fb --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/attr-misuse.stderr @@ -0,0 +1,32 @@ +error: attribute should be applied to a trait method with body + --> $DIR/attr-misuse.rs:4:1 + | +LL | #[default_method_body_is_const] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | / trait A { +LL | | #[default_method_body_is_const] +LL | | fn no_body(self); +LL | | +LL | | #[default_method_body_is_const] +LL | | fn correct_use(&self) {} +LL | | } + | |_- not a trait method or missing a body + +error: attribute should be applied to a trait method with body + --> $DIR/attr-misuse.rs:13:1 + | +LL | #[default_method_body_is_const] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn main() {} + | ------------ not a trait method or missing a body + +error: attribute should be applied to a trait method with body + --> $DIR/attr-misuse.rs:6:5 + | +LL | #[default_method_body_is_const] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn no_body(self); + | ----------------- not a trait method or missing a body + +error: aborting due to 3 previous errors + From 7c9e214bc3c2d61b15ded23cf219c8415795789c Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 10 Jul 2021 21:46:31 +0800 Subject: [PATCH 12/12] Update `DepNode`'s size --- compiler/rustc_middle/src/dep_graph/dep_node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index aa54d1ae7b9d1..8476929eaeced 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -285,7 +285,7 @@ pub type DepNode = rustc_query_system::dep_graph::DepNode; // required that their size stay the same, but we don't want to change // it inadvertently. This assert just ensures we're aware of any change. #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -static_assert_size!(DepNode, 17); +static_assert_size!(DepNode, 18); #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] static_assert_size!(DepNode, 24);