// 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) {
- // allow calling functions marked with #[default_method_body_is_const].
- if !ecx.tcx.has_attr(def.did, sym::default_method_body_is_const) {
+ // allow calling functions inside a trait marked with #[const_trait].
+ if !matches!(ecx.tcx.trait_of_item(def.did), Some(trait_id) if ecx.tcx.has_attr(trait_id, sym::const_trait))
+ {
// 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)
#![feature(control_flow_enum)]
#![feature(decl_macro)]
#![feature(exact_size_is_empty)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
}
}
_ if !tcx.is_const_fn_raw(callee) => {
- // At this point, it is only legal when the caller is marked with
- // #[default_method_body_is_const], and the callee is in the same
- // trait.
- let callee_trait = tcx.trait_of_item(callee);
- if callee_trait.is_some()
- && tcx.has_attr(caller.to_def_id(), sym::default_method_body_is_const)
- && callee_trait == tcx.trait_of_item(caller)
+ // At this point, it is only legal when the caller is in a trait
+ // marked with #[const_trait], and the callee is in the same trait.
+ if let Some(callee_trait) = tcx.trait_of_item(callee)
+ && tcx.has_attr(callee_trait, sym::const_trait)
+ && Some(callee_trait) == tcx.trait_of_item(caller)
// Can only call methods when it's `<Self as TheTrait>::f`.
&& tcx.types.self_param == substs.type_at(0)
{
if !tcx.is_const_fn_raw(callee) {
if tcx.trait_of_item(callee).is_some() {
- if tcx.has_attr(callee, sym::default_method_body_is_const) {
+ if let Some(callee_trait) = tcx.trait_of_item(callee) && tcx.has_attr(callee_trait, sym::const_trait) {
// To get to here we must have already found a const impl for the
// trait, but for it to still be non-const can be that the impl is
// using default method bodies.
// functions are subject to more stringent restrictions than "const-unstable" functions: They
// cannot use unstable features and can only call other "const-stable" functions.
pub fn is_const_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- // A default body marked const is not const-stable because const
+ // A default body in a `#[const_trait]` is not const-stable because const
// trait fns currently cannot be const-stable. We shouldn't
// restrict default bodies to only call const-stable functions.
- if tcx.has_attr(def_id, sym::default_method_body_is_const) {
+ if let Some(trait_id) = tcx.trait_of_item(def_id) && tcx.has_attr(trait_id, sym::const_trait) {
return false;
}
),
// RFC 2632
gated!(
- default_method_body_is_const, Normal, template!(Word), WarnFollowing, 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."
+ const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl,
+ "`const` is a temporary placeholder for marking a trait that is suitable for `const` \
+ `impls` and all default bodies as `const`, which may be removed or renamed in the \
+ future."
),
// lang-team MCP 147
gated!(
let needs_inline = (generics.requires_monomorphization(tcx)
|| tcx.codegen_fn_attrs(def_id).requests_inline())
&& tcx.sess.opts.output_types.should_codegen();
- // The function has a `const` modifier or is annotated with `default_method_body_is_const`.
- let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
- || tcx.has_attr(def_id.to_def_id(), sym::default_method_body_is_const);
+ // The function has a `const` modifier or is in a `#[const_trait]`.
+ let mut is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id());
+ if let Some(trait_) = tcx.trait_of_item(def_id.to_def_id()) {
+ is_const_fn = is_const_fn || tcx.has_attr(trait_, sym::const_trait);
+ }
let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir;
(is_const_fn, needs_inline || always_encode_mir)
}
BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(def_id.to_def_id()) => {
ConstContext::ConstFn
}
- BodyOwnerKind::Fn
- if self.tcx.has_attr(def_id.to_def_id(), sym::default_method_body_is_const) =>
- {
+ BodyOwnerKind::Fn if matches!(self.tcx.trait_of_item(def_id.to_def_id()), Some(trait_id) if self.tcx.has_attr(trait_id, sym::const_trait)) => {
ConstContext::ConstFn
}
BodyOwnerKind::Fn | BodyOwnerKind::Closure => return None,
| 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)
- }
+ sym::const_trait => self.check_const_trait(attr, span, target),
sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
sym::must_use => self.check_must_use(hir_id, &attr, span, target),
sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
}
}
- /// 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 {
+ /// `#[const_trait]` only applies to traits.
+ fn check_const_trait(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
match target {
- Target::Method(MethodKind::Trait { body: true }) => true,
+ Target::Trait => 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")
+ .struct_span_err(attr.span, "attribute should be applied to a trait")
.emit();
false
}
"attribute `{}` without any lints has no effect",
attr.name_or_empty()
)
+ } else if attr.name_or_empty() == sym::default_method_body_is_const {
+ format!("`default_method_body_is_const` has been removed")
} else {
return;
};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::hir::nested_filter;
-use rustc_middle::ty;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err;
*providers = Providers { check_mod_const_bodies, ..*providers };
}
-fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
- let _: Option<_> = try {
- if let hir::ItemKind::Impl(ref imp) = item.kind && let hir::Constness::Const = imp.constness {
- let trait_def_id = imp.of_trait.as_ref()?.trait_def_id()?;
- let ancestors = tcx
- .trait_def(trait_def_id)
- .ancestors(tcx, item.def_id.to_def_id())
- .ok()?;
- let mut to_implement = Vec::new();
-
- for trait_item in tcx.associated_items(trait_def_id).in_definition_order()
- {
- if let ty::AssocItem {
- kind: ty::AssocKind::Fn,
- defaultness,
- def_id: trait_item_id,
- ..
- } = *trait_item
- {
- // we can ignore functions that do not have default bodies:
- // if those are unimplemented it will be caught by typeck.
- if !defaultness.has_value()
- || tcx
- .has_attr(trait_item_id, sym::default_method_body_is_const)
- {
- continue;
- }
-
- let is_implemented = ancestors
- .leaf_def(tcx, trait_item_id)
- .map(|node_item| !node_item.defining_node.is_from_trait())
- .unwrap_or(false);
-
- if !is_implemented {
- to_implement.push(trait_item_id);
- }
- }
- }
-
- // all nonconst trait functions (not marked with #[default_method_body_is_const])
- // must be implemented
- if !to_implement.is_empty() {
- let not_implemented = to_implement
- .into_iter()
- .map(|did| tcx.item_name(did).to_string())
- .collect::<Vec<_>>()
- .join("`, `");
- tcx
- .sess
- .struct_span_err(
- item.span,
- "const trait implementations may not use non-const default functions",
- )
- .note(&format!("`{}` not implemented", not_implemented))
- .emit();
- }
- }
- };
-}
-
#[derive(Copy, Clone)]
struct CheckConstVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
intravisit::walk_item(self, item);
- check_item(self.tcx, item);
}
fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
const_raw_ptr_deref,
const_raw_ptr_to_usize_cast,
const_refs_to_cell,
+ const_trait,
const_trait_bound_opt_out,
const_trait_impl,
const_transmute,
let constness = match hir_id {
Some(hir_id) => match tcx.hir().get(hir_id) {
- hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
- if tcx.has_attr(def_id, sym::default_method_body_is_const) =>
- {
+ hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) if matches!(tcx.trait_of_item(def_id), Some(trait_id) if tcx.has_attr(trait_id, sym::const_trait)) => {
hir::Constness::Const
}
#[lang = "clone"]
#[rustc_diagnostic_item = "Clone"]
#[rustc_trivial_field_reads]
+#[cfg_attr(not(bootstrap), const_trait)]
pub trait Clone: Sized {
/// Returns a copy of the value.
///
/// allocations.
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- #[default_method_body_is_const]
+ #[cfg_attr(bootstrap, default_method_body_is_const)]
fn clone_from(&mut self, source: &Self)
where
Self: ~const Destruct,
append_const_msg,
)
)]
+#[cfg_attr(not(bootstrap), const_trait)]
#[rustc_diagnostic_item = "PartialEq"]
pub trait PartialEq<Rhs: ?Sized = Self> {
/// This method tests for `self` and `other` values to be equal, and is used
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[default_method_body_is_const]
+ #[cfg_attr(bootstrap, default_method_body_is_const)]
fn ne(&self, other: &Rhs) -> bool {
!self.eq(other)
}
append_const_msg,
)
)]
+#[cfg_attr(not(bootstrap), const_trait)]
#[rustc_diagnostic_item = "PartialOrd"]
pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
/// This method returns an ordering between `self` and `other` values if one exists.
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[default_method_body_is_const]
+ #[cfg_attr(bootstrap, default_method_body_is_const)]
fn lt(&self, other: &Rhs) -> bool {
matches!(self.partial_cmp(other), Some(Less))
}
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[default_method_body_is_const]
+ #[cfg_attr(bootstrap, default_method_body_is_const)]
fn le(&self, other: &Rhs) -> bool {
// Pattern `Some(Less | Eq)` optimizes worse than negating `None | Some(Greater)`.
// FIXME: The root cause was fixed upstream in LLVM with:
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[default_method_body_is_const]
+ #[cfg_attr(bootstrap, default_method_body_is_const)]
fn gt(&self, other: &Rhs) -> bool {
matches!(self.partial_cmp(other), Some(Greater))
}
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[default_method_body_is_const]
+ #[cfg_attr(bootstrap, default_method_body_is_const)]
fn ge(&self, other: &Rhs) -> bool {
matches!(self.partial_cmp(other), Some(Greater | Equal))
}
// @has - '//pre[@class="rust trait"]/code/a[@class="trait"]' 'Clone'
// @!has - '//pre[@class="rust trait"]/code/span[@class="where"]' '~const'
// @has - '//pre[@class="rust trait"]/code/span[@class="where"]' ': Clone'
+#[const_trait]
pub trait Tr<T> {
// @!has - '//div[@id="method.a"]/h4[@class="code-header"]' '~const'
// @has - '//div[@id="method.a"]/h4[@class="code-header"]/a[@class="trait"]' 'Clone'
// @!has - '//div[@id="method.a"]/h4[@class="code-header"]/span[@class="where"]' '~const'
// @has - '//div[@id="method.a"]/h4[@class="code-header"]/span[@class="where fmt-newline"]' ': Clone'
- #[default_method_body_is_const]
fn a<A: ~const Clone + ~const Destruct>()
where
Option<A>: ~const Clone + ~const Destruct,
#![feature(const_trait_impl)]
-#[default_method_body_is_const] //~ ERROR attribute should be applied
+#[const_trait]
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) {}
+ #[const_trait] //~ ERROR attribute should be applied
+ fn foo(self);
}
-#[default_method_body_is_const] //~ ERROR attribute should be applied
+#[const_trait] //~ ERROR attribute should be applied
fn main() {}
-error: attribute should be applied to a trait method with body
- --> $DIR/attr-misuse.rs:3:1
+error: attribute should be applied to a trait
+ --> $DIR/attr-misuse.rs:9: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
+LL | #[const_trait]
+ | ^^^^^^^^^^^^^^
-error: attribute should be applied to a trait method with body
- --> $DIR/attr-misuse.rs:12: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
+error: attribute should be applied to a trait
--> $DIR/attr-misuse.rs:5:5
|
-LL | #[default_method_body_is_const]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-LL | fn no_body(self);
- | ----------------- not a trait method or missing a body
+LL | #[const_trait]
+ | ^^^^^^^^^^^^^^
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
#![feature(const_trait_impl)]
+#[const_trait]
pub trait MyTrait {
- #[default_method_body_is_const]
fn defaulted_func(&self) {}
fn func(self);
}
pub struct Int(i32);
-impl const std::ops::Add for i32 { //~ ERROR type annotations needed
+impl const std::ops::Add for i32 {
//~^ ERROR only traits defined in the current crate can be implemented for primitive types
type Output = Self;
}
}
-impl std::ops::Add for Int { //~ ERROR type annotations needed
+impl std::ops::Add for Int {
type Output = Self;
fn add(self, rhs: Self) -> Self {
}
}
-impl const std::ops::Add for Int { //~ ERROR type annotations needed
+impl const std::ops::Add for Int {
//~^ ERROR conflicting implementations of trait
type Output = Self;
LL | impl const std::ops::Add for Int {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Int`
-error[E0283]: type annotations needed
- --> $DIR/const-and-non-const-impl.rs:5:12
- |
-LL | impl const std::ops::Add for i32 {
- | ^^^^^^^^^^^^^ cannot infer type for type `i32`
- |
-note: multiple `impl`s satisfying `i32: Add` found
- --> $DIR/const-and-non-const-impl.rs:5:1
- |
-LL | impl const std::ops::Add for i32 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: and another `impl` found in the `core` crate: `impl Add for i32;`
-
-error[E0283]: type annotations needed
- --> $DIR/const-and-non-const-impl.rs:14:6
- |
-LL | impl std::ops::Add for Int {
- | ^^^^^^^^^^^^^ cannot infer type for struct `Int`
- |
-note: multiple `impl`s satisfying `Int: Add` found
- --> $DIR/const-and-non-const-impl.rs:14:1
- |
-LL | impl std::ops::Add for Int {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | impl const std::ops::Add for Int {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0283]: type annotations needed
- --> $DIR/const-and-non-const-impl.rs:22:12
- |
-LL | impl const std::ops::Add for Int {
- | ^^^^^^^^^^^^^ cannot infer type for struct `Int`
- |
-note: multiple `impl`s satisfying `Int: Add` found
- --> $DIR/const-and-non-const-impl.rs:14:1
- |
-LL | impl std::ops::Add for Int {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | impl const std::ops::Add for Int {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 5 previous errors
+error: aborting due to 2 previous errors
-Some errors have detailed explanations: E0117, E0119, E0283.
+Some errors have detailed explanations: E0117, E0119.
For more information about an error, try `rustc --explain E0117`.
#![feature(const_trait_impl)]
+#[const_trait]
trait ConstDefaultFn: Sized {
fn b(self);
- #[default_method_body_is_const]
fn a(self) {
self.b();
}
-// This tests that `default_method_body_is_const` methods can
+// This tests that `const_trait` default methods can
// be called from a const context when used across crates.
//
// check-pass
const fn foo<T>() where T: ~const Tr {}
+#[const_trait]
pub trait Foo {
- #[default_method_body_is_const]
fn foo() {
foo::<()>();
//~^ ERROR the trait bound `(): ~const Tr` is not satisfied
#![feature(const_trait_impl)]
+#[const_trait]
pub trait Tr {
- #[default_method_body_is_const]
fn a(&self) {}
- #[default_method_body_is_const]
fn b(&self) {
().a()
//~^ ERROR the trait bound
error[E0277]: the trait bound `(): ~const Tr` is not satisfied
- --> $DIR/default-method-body-is-const-same-trait-ck.rs:9:12
+ --> $DIR/default-method-body-is-const-same-trait-ck.rs:8:12
|
LL | ().a()
| ^^^ the trait `~const Tr` is not implemented for `()`
|
note: the trait `Tr` is implemented for `()`, but that implementation is not `const`
- --> $DIR/default-method-body-is-const-same-trait-ck.rs:9:12
+ --> $DIR/default-method-body-is-const-same-trait-ck.rs:8:12
|
LL | ().a()
| ^^^
error[E0015]: cannot call non-const fn `<() as Tr>::a` in constant functions
- --> $DIR/default-method-body-is-const-same-trait-ck.rs:9:12
+ --> $DIR/default-method-body-is-const-same-trait-ck.rs:8:12
|
LL | ().a()
| ^^^
#![feature(const_trait_impl)]
#![stable(since = "1", feature = "foo")]
+#[const_trait]
trait Tr {
- #[default_method_body_is_const]
fn a() {}
}
#![feature(const_trait_impl)]
+#[const_trait]
trait Tr {
fn req(&self);
- fn prov(&self) {
- 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 u16 {
- fn prov(&self) {}
fn default() {}
-} //~^^^ ERROR not all trait items implemented
+} //~^^ 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() {}
-error: const trait implementations may not use non-const default functions
- --> $DIR/impl-with-default-fn-fail.rs:17: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-fail.rs:27: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-fail.rs:21:1
+ --> $DIR/impl-with-default-fn-fail.rs:12:1
|
LL | fn req(&self);
| -------------- `req` from trait
LL | impl const Tr for u16 {
| ^^^^^^^^^^^^^^^^^^^^^ missing `req` in implementation
-error: aborting due to 3 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0046`.
#![feature(const_trait_impl)]
+#[const_trait]
trait Tr {
fn req(&self);
- fn prov(&self) {
- println!("lul");
- self.req();
- }
-
- #[default_method_body_is_const]
fn default() {}
}
impl const Tr for u8 {
fn req(&self) {}
- fn prov(&self) {}
}
macro_rules! impl_tr {
($ty: ty) => {
impl const Tr for $ty {
fn req(&self) {}
- fn prov(&self) {}
}
}
}
}
#[stable(feature = "foo", since = "1.0")]
+#[const_trait]
pub trait Tr {
- #[default_method_body_is_const]
#[stable(feature = "foo", since = "1.0")]
fn bar() -> T {
T?
fn bar() -> u8;
}
+#[const_trait]
trait Foo {
- #[default_method_body_is_const]
fn foo() -> u8 where Self: ~const Bar {
<Self as Bar>::bar() * 6
}