Auto merge of #68847 - ecstatic-morse:const-impl, r=oli-obk

Allow trait methods to be called on concrete types in a const context

This partially implements [RFC 2632](https://github.com/rust-lang/rfcs/pull/2632) by const-checking methods inside an `impl const` block and allowing those methods to be called on concrete types. Calling trait methods on type parameters in a const context is not yet allowed. Implementing this will require much more work. Since we are only concerned with methods on concrete types, we are able to take advantage of the machinery in `Instance::resolve`, which is doing most of the work.

This also propagates `#[rustc_const_unstable]` from parent items to child items, making that attribute behave like `#[stable]` and `#[unstable]` do. This allows trait methods to be marked as unstably const.

cc #67792 #57563
cc @rust-lang/wg-const-eval
r? @oli-obk
This commit is contained in:
bors 2020-02-20 08:41:17 +00:00
commit 6af388b250
22 changed files with 597 additions and 221 deletions

View File

@ -279,6 +279,14 @@ rustc_queries! {
desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) }
}
/// Returns `true` if this is a const `impl`. **Do not call this function manually.**
///
/// This query caches the base data for the `is_const_impl` helper function, which also
/// takes into account stability attributes (e.g., `#[rustc_const_unstable]`).
query is_const_impl_raw(key: DefId) -> bool {
desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) }
}
query asyncness(key: DefId) -> hir::IsAsync {
desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) }
}

View File

@ -66,15 +66,7 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> {
if let Some(hir_id) = item_hir_id {
self.lctx.with_parent_item_lifetime_defs(hir_id, |this| {
let this = &mut ItemLowerer { lctx: this };
if let ItemKind::Impl { constness, ref of_trait, .. } = item.kind {
if let Const::Yes(span) = constness {
this.lctx
.diagnostic()
.struct_span_err(item.span, "const trait impls are not yet implemented")
.span_label(span, "const because of this")
.emit();
}
if let ItemKind::Impl { ref of_trait, .. } = item.kind {
this.with_trait_impl_ref(of_trait, |this| visit::walk_item(this, item));
} else {
visit::walk_item(this, item);

View File

@ -3,7 +3,7 @@ use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_attr as attr;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_span::symbol::Symbol;
use rustc_target::spec::abi::Abi;
@ -82,72 +82,96 @@ pub fn is_min_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
}
}
pub fn provide(providers: &mut Providers<'_>) {
/// Const evaluability whitelist is here to check evaluability at the
/// top level beforehand.
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
if tcx.is_closure(def_id) {
return None;
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
let parent_id = tcx.hir().get_parent_did(hir_id);
if !parent_id.is_top_level_module() {
is_const_impl_raw(tcx, LocalDefId::from_def_id(parent_id))
} else {
false
}
}
/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
/// said intrinsic is on the whitelist for being const callable.
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let hir_id =
tcx.hir().as_local_hir_id(def_id).expect("Non-local call to local provider is_const_fn");
let node = tcx.hir().get(hir_id);
if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
whitelisted
} else if let Some(fn_like) = FnLikeNode::from_node(node) {
if fn_like.constness() == hir::Constness::Const {
return true;
}
match tcx.fn_sig(def_id).abi() {
Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
Some(tcx.lookup_const_stability(def_id).is_some())
}
_ => None,
}
// 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)
} else if let hir::Node::Ctor(_) = node {
true
} else {
false
}
}
/// Const evaluability whitelist is here to check evaluability at the
/// top level beforehand.
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
if tcx.is_closure(def_id) {
return None;
}
/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
/// said intrinsic is on the whitelist for being const callable.
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let hir_id = tcx
.hir()
.as_local_hir_id(def_id)
.expect("Non-local call to local provider is_const_fn");
let node = tcx.hir().get(hir_id);
if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
whitelisted
} else if let Some(fn_like) = FnLikeNode::from_node(node) {
fn_like.constness() == hir::Constness::Const
} else if let hir::Node::Ctor(_) = node {
true
} else {
false
match tcx.fn_sig(def_id).abi() {
Abi::RustIntrinsic | Abi::PlatformIntrinsic => {
Some(tcx.lookup_const_stability(def_id).is_some())
}
_ => None,
}
}
fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
is_const_fn(tcx, def_id)
&& match tcx.lookup_const_stability(def_id) {
Some(stab) => {
if cfg!(debug_assertions) && stab.promotable {
let sig = tcx.fn_sig(def_id);
assert_eq!(
sig.unsafety(),
hir::Unsafety::Normal,
"don't mark const unsafe fns as promotable",
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
);
}
stab.promotable
/// Checks whether the given item is an `impl` that has a `const` modifier.
fn is_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let node = tcx.hir().get(hir_id);
matches!(
node,
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Impl { constness: hir::Constness::Const, .. },
..
})
)
}
fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
is_const_fn(tcx, def_id)
&& match tcx.lookup_const_stability(def_id) {
Some(stab) => {
if cfg!(debug_assertions) && stab.promotable {
let sig = tcx.fn_sig(def_id);
assert_eq!(
sig.unsafety(),
hir::Unsafety::Normal,
"don't mark const unsafe fns as promotable",
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
);
}
None => false,
stab.promotable
}
}
None => false,
}
}
fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
is_const_fn(tcx, def_id)
&& tcx
.lookup_const_stability(def_id)
.map(|stab| stab.allow_const_fn_ptr)
.unwrap_or(false)
}
fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
is_const_fn(tcx, def_id)
&& tcx.lookup_const_stability(def_id).map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
}
pub fn provide(providers: &mut Providers<'_>) {
*providers = Providers {
is_const_fn_raw,
is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, LocalDefId::from_def_id(def_id)),
is_promotable_const_fn,
const_fn_is_allowed_fn_ptr,
..*providers

View File

@ -4,7 +4,7 @@ use rustc::middle::lang_items;
use rustc::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc::mir::*;
use rustc::ty::cast::CastTy;
use rustc::ty::{self, TyCtxt};
use rustc::ty::{self, Instance, InstanceDef, TyCtxt};
use rustc_errors::struct_span_err;
use rustc_hir::{def_id::DefId, HirId};
use rustc_index::bit_set::BitSet;
@ -502,8 +502,8 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
TerminatorKind::Call { func, .. } => {
let fn_ty = func.ty(*self.body, self.tcx);
let def_id = match fn_ty.kind {
ty::FnDef(def_id, _) => def_id,
let (def_id, substs) = match fn_ty.kind {
ty::FnDef(def_id, substs) => (def_id, substs),
ty::FnPtr(_) => {
self.check_op(ops::FnCallIndirect);
@ -520,6 +520,20 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
return;
}
// See if this is a trait method for a concrete type whose impl of that trait is
// `const`.
if self.tcx.features().const_trait_impl {
let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs);
debug!("Resolving ({:?}) -> {:?}", def_id, instance);
if let Some(func) = instance {
if let InstanceDef::Item(def_id) = func.def {
if is_const_fn(self.tcx, def_id) {
return;
}
}
}
}
if is_lang_panic_fn(self.tcx, def_id) {
self.check_op(ops::Panic);
} else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {

View File

@ -10,6 +10,14 @@ use std::borrow::Cow;
type McfResult = Result<(), (Span, Cow<'static, str>)>;
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
// Prevent const trait methods from being annotated as `stable`.
if tcx.features().staged_api {
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
return Err((body.span, "trait methods cannot be stable const fn".into()));
}
}
let mut current = def_id;
loop {
let predicates = tcx.predicates_of(current);

View File

@ -9,7 +9,7 @@ use rustc::session::parse::feature_err;
use rustc::session::Session;
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use rustc_attr::{self as attr, Stability};
use rustc_attr::{self as attr, ConstStability, Stability};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
@ -41,6 +41,7 @@ struct Annotator<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
index: &'a mut Index<'tcx>,
parent_stab: Option<&'tcx Stability>,
parent_const_stab: Option<&'tcx ConstStability>,
parent_depr: Option<DeprecationEntry>,
in_trait_impl: bool,
}
@ -58,144 +59,197 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
) where
F: FnOnce(&mut Self),
{
if self.tcx.features().staged_api {
// This crate explicitly wants staged API.
debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
if let Some(..) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
self.tcx.sess.span_err(
item_sp,
"`#[deprecated]` cannot be used in staged API; \
use `#[rustc_deprecated]` instead",
);
}
let (stab, const_stab) =
attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
if let Some(const_stab) = const_stab {
let const_stab = self.tcx.intern_const_stability(const_stab);
self.index.const_stab_map.insert(hir_id, const_stab);
}
if let Some(mut stab) = stab {
// Error if prohibited, or can't inherit anything from a container.
if kind == AnnotationKind::Prohibited
|| (kind == AnnotationKind::Container
&& stab.level.is_stable()
&& stab.rustc_depr.is_none())
{
self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
if !self.tcx.features().staged_api {
self.forbid_staged_api_attrs(hir_id, attrs, item_sp, kind, visit_children);
return;
}
// This crate explicitly wants staged API.
debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
if let Some(..) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
self.tcx.sess.span_err(
item_sp,
"`#[deprecated]` cannot be used in staged API; \
use `#[rustc_deprecated]` instead",
);
}
let (stab, const_stab) = attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
let const_stab = const_stab.map(|const_stab| {
let const_stab = self.tcx.intern_const_stability(const_stab);
self.index.const_stab_map.insert(hir_id, const_stab);
const_stab
});
if const_stab.is_none() {
debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
if let Some(parent) = self.parent_const_stab {
if parent.level.is_unstable() {
self.index.const_stab_map.insert(hir_id, parent);
}
}
}
debug!("annotate: found {:?}", stab);
// If parent is deprecated and we're not, inherit this by merging
// deprecated_since and its reason.
if let Some(parent_stab) = self.parent_stab {
if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
stab.rustc_depr = parent_stab.rustc_depr
}
let stab = stab.map(|mut stab| {
// Error if prohibited, or can't inherit anything from a container.
if kind == AnnotationKind::Prohibited
|| (kind == AnnotationKind::Container
&& stab.level.is_stable()
&& stab.rustc_depr.is_none())
{
self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
}
debug!("annotate: found {:?}", stab);
// If parent is deprecated and we're not, inherit this by merging
// deprecated_since and its reason.
if let Some(parent_stab) = self.parent_stab {
if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
stab.rustc_depr = parent_stab.rustc_depr
}
}
let stab = self.tcx.intern_stability(stab);
let stab = self.tcx.intern_stability(stab);
// Check if deprecated_since < stable_since. If it is,
// this is *almost surely* an accident.
if let (
&Some(attr::RustcDeprecation { since: dep_since, .. }),
&attr::Stable { since: stab_since },
) = (&stab.rustc_depr, &stab.level)
// Check if deprecated_since < stable_since. If it is,
// this is *almost surely* an accident.
if let (
&Some(attr::RustcDeprecation { since: dep_since, .. }),
&attr::Stable { since: stab_since },
) = (&stab.rustc_depr, &stab.level)
{
// Explicit version of iter::order::lt to handle parse errors properly
for (dep_v, stab_v) in
dep_since.as_str().split('.').zip(stab_since.as_str().split('.'))
{
// Explicit version of iter::order::lt to handle parse errors properly
for (dep_v, stab_v) in
dep_since.as_str().split('.').zip(stab_since.as_str().split('.'))
{
if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
match dep_v.cmp(&stab_v) {
Ordering::Less => {
self.tcx.sess.span_err(
item_sp,
"An API can't be stabilized \
after it is deprecated",
);
break;
}
Ordering::Equal => continue,
Ordering::Greater => break,
if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
match dep_v.cmp(&stab_v) {
Ordering::Less => {
self.tcx.sess.span_err(
item_sp,
"An API can't be stabilized \
after it is deprecated",
);
break;
}
} else {
// Act like it isn't less because the question is now nonsensical,
// and this makes us not do anything else interesting.
self.tcx.sess.span_err(
item_sp,
"Invalid stability or deprecation \
version found",
);
break;
Ordering::Equal => continue,
Ordering::Greater => break,
}
} else {
// Act like it isn't less because the question is now nonsensical,
// and this makes us not do anything else interesting.
self.tcx.sess.span_err(
item_sp,
"Invalid stability or deprecation \
version found",
);
break;
}
}
self.index.stab_map.insert(hir_id, stab);
let orig_parent_stab = replace(&mut self.parent_stab, Some(stab));
visit_children(self);
self.parent_stab = orig_parent_stab;
} else {
debug!("annotate: not found, parent = {:?}", self.parent_stab);
if let Some(stab) = self.parent_stab {
if stab.level.is_unstable() {
self.index.stab_map.insert(hir_id, stab);
}
}
visit_children(self);
}
} else {
// Emit errors for non-staged-api crates.
let unstable_attrs = [
sym::unstable,
sym::stable,
sym::rustc_deprecated,
sym::rustc_const_unstable,
sym::rustc_const_stable,
];
for attr in attrs {
let name = attr.name_or_empty();
if unstable_attrs.contains(&name) {
attr::mark_used(attr);
struct_span_err!(
self.tcx.sess,
attr.span,
E0734,
"stability attributes may not be used outside of the standard library",
)
.emit();
}
}
// Propagate unstability. This can happen even for non-staged-api crates in case
// -Zforce-unstable-if-unmarked is set.
self.index.stab_map.insert(hir_id, stab);
stab
});
if stab.is_none() {
debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
if let Some(stab) = self.parent_stab {
if stab.level.is_unstable() {
self.index.stab_map.insert(hir_id, stab);
}
}
}
if let Some(depr) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
if kind == AnnotationKind::Prohibited {
self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
}
self.recurse_with_stability_attrs(stab, const_stab, visit_children);
}
// `Deprecation` is just two pointers, no need to intern it
let depr_entry = DeprecationEntry::local(depr, hir_id);
self.index.depr_map.insert(hir_id, depr_entry.clone());
fn recurse_with_stability_attrs(
&mut self,
stab: Option<&'tcx Stability>,
const_stab: Option<&'tcx ConstStability>,
f: impl FnOnce(&mut Self),
) {
// These will be `Some` if this item changes the corresponding stability attribute.
let mut replaced_parent_stab = None;
let mut replaced_parent_const_stab = None;
let orig_parent_depr = replace(&mut self.parent_depr, Some(depr_entry));
visit_children(self);
self.parent_depr = orig_parent_depr;
} else if let Some(parent_depr) = self.parent_depr.clone() {
self.index.depr_map.insert(hir_id, parent_depr);
visit_children(self);
} else {
visit_children(self);
if let Some(stab) = stab {
replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab)));
}
if let Some(const_stab) = const_stab {
replaced_parent_const_stab =
Some(replace(&mut self.parent_const_stab, Some(const_stab)));
}
f(self);
if let Some(orig_parent_stab) = replaced_parent_stab {
self.parent_stab = orig_parent_stab;
}
if let Some(orig_parent_const_stab) = replaced_parent_const_stab {
self.parent_const_stab = orig_parent_const_stab;
}
}
fn forbid_staged_api_attrs(
&mut self,
hir_id: HirId,
attrs: &[Attribute],
item_sp: Span,
kind: AnnotationKind,
visit_children: impl FnOnce(&mut Self),
) {
// Emit errors for non-staged-api crates.
let unstable_attrs = [
sym::unstable,
sym::stable,
sym::rustc_deprecated,
sym::rustc_const_unstable,
sym::rustc_const_stable,
];
for attr in attrs {
let name = attr.name_or_empty();
if unstable_attrs.contains(&name) {
attr::mark_used(attr);
struct_span_err!(
self.tcx.sess,
attr.span,
E0734,
"stability attributes may not be used outside of the standard library",
)
.emit();
}
}
// Propagate unstability. This can happen even for non-staged-api crates in case
// -Zforce-unstable-if-unmarked is set.
if let Some(stab) = self.parent_stab {
if stab.level.is_unstable() {
self.index.stab_map.insert(hir_id, stab);
}
}
if let Some(depr) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
if kind == AnnotationKind::Prohibited {
self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
}
// `Deprecation` is just two pointers, no need to intern it
let depr_entry = DeprecationEntry::local(depr, hir_id);
self.index.depr_map.insert(hir_id, depr_entry.clone());
let orig_parent_depr = replace(&mut self.parent_depr, Some(depr_entry));
visit_children(self);
self.parent_depr = orig_parent_depr;
} else if let Some(parent_depr) = self.parent_depr.clone() {
self.index.depr_map.insert(hir_id, parent_depr);
visit_children(self);
} else {
visit_children(self);
}
}
}
@ -376,6 +430,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> {
tcx,
index: &mut index,
parent_stab: None,
parent_const_stab: None,
parent_depr: None,
in_trait_impl: false,
};

View File

@ -0,0 +1,28 @@
// ignore-test
// FIXME: This test should fail since, within a const impl of `Foo`, the bound on `Foo::Bar` should
// require a const impl of `Add` for the associated type.
#![allow(incomplete_features)]
#![feature(const_trait_impl)]
#![feature(const_fn)]
struct NonConstAdd(i32);
impl std::ops::Add for NonConstAdd {
type Output = Self;
fn add(self, rhs: Self) -> Self {
NonConstAdd(self.0 + rhs.0)
}
}
trait Foo {
type Bar: std::ops::Add;
}
impl const Foo for NonConstAdd {
type Bar = NonConstAdd;
}
fn main() {}

View File

@ -0,0 +1,30 @@
#![allow(incomplete_features)]
#![feature(const_trait_impl)]
#![feature(const_fn)]
pub trait Plus {
fn plus(self, rhs: Self) -> Self;
}
impl const Plus for i32 {
fn plus(self, rhs: Self) -> Self {
self + rhs
}
}
impl Plus for u32 {
fn plus(self, rhs: Self) -> Self {
self + rhs
}
}
pub const fn add_i32(a: i32, b: i32) -> i32 {
a.plus(b) // ok
}
pub const fn add_u32(a: u32, b: u32) -> u32 {
a.plus(b)
//~^ ERROR calls in constant functions are limited to constant functions
}
fn main() {}

View File

@ -0,0 +1,9 @@
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
--> $DIR/call-const-trait-method-fail.rs:26:5
|
LL | a.plus(b)
| ^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0015`.

View File

@ -0,0 +1,41 @@
// run-pass
#![allow(incomplete_features)]
#![feature(const_trait_impl)]
#![feature(const_fn)]
struct Int(i32);
impl const std::ops::Add for Int {
type Output = Int;
fn add(self, rhs: Self) -> Self {
Int(self.0.plus(rhs.0))
}
}
impl const PartialEq for Int {
fn eq(&self, rhs: &Self) -> bool {
self.0 == rhs.0
}
}
pub trait Plus {
fn plus(self, rhs: Self) -> Self;
}
impl const Plus for i32 {
fn plus(self, rhs: Self) -> Self {
self + rhs
}
}
pub const fn add_i32(a: i32, b: i32) -> i32 {
a.plus(b)
}
const ADD_INT: Int = Int(1i32) + Int(2i32);
fn main() {
assert!(ADD_INT == Int(3i32));
}

View File

@ -0,0 +1,33 @@
#![allow(incomplete_features)]
#![feature(const_trait_impl)]
pub struct Int(i32);
impl const std::ops::Add for i32 {
//~^ ERROR conflicting implementations of trait
//~| ERROR only traits defined in the current crate can be implemented for arbitrary types
type Output = Self;
fn add(self, rhs: Self) -> Self {
self + rhs
}
}
impl std::ops::Add for Int {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Int(self.0 + rhs.0)
}
}
impl const std::ops::Add for Int {
//~^ ERROR conflicting implementations of trait
type Output = Self;
fn add(self, rhs: Self) -> Self {
Int(self.0 + rhs.0)
}
}
fn main() {}

View File

@ -0,0 +1,34 @@
error[E0119]: conflicting implementations of trait `std::ops::Add` for type `i32`:
--> $DIR/const-and-non-const-impl.rs:6:1
|
LL | impl const std::ops::Add for i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl std::ops::Add for i32;
error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int`:
--> $DIR/const-and-non-const-impl.rs:24:1
|
LL | impl std::ops::Add for Int {
| -------------------------- first implementation here
...
LL | impl const std::ops::Add for Int {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Int`
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/const-and-non-const-impl.rs:6:1
|
LL | impl const std::ops::Add for i32 {
| ^^^^^^^^^^^-------------^^^^^---
| | | |
| | | `i32` is not defined in the current crate
| | `i32` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0117, E0119.
For more information about an error, try `rustc --explain E0117`.

View File

@ -0,0 +1,16 @@
#![allow(incomplete_features)]
#![feature(const_trait_impl)]
struct S;
trait T {
fn foo();
}
fn non_const() {}
impl const T for S {
fn foo() { non_const() }
//~^ ERROR can only call other `const fn`
}
fn main() {}

View File

@ -0,0 +1,12 @@
error[E0723]: can only call other `const fn` within a `const fn`, but `const non_const` is not stable as `const fn`
--> $DIR/const-check-fns-in-const-impl.rs:12:16
|
LL | fn foo() { non_const() }
| ^^^^^^^^^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
= help: add `#![feature(const_fn)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0723`.

View File

@ -1,10 +1,8 @@
error: const trait impls are not yet implemented
--> $DIR/feature-gate.rs:9:1
error: fatal error triggered by #[rustc_error]
--> $DIR/feature-gate.rs:14:1
|
LL | impl const T for S {}
| ^^^^^-----^^^^^^^^^^^
| |
| const because of this
LL | fn main() {}
| ^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -3,11 +3,12 @@
#![cfg_attr(gated, feature(const_trait_impl))]
#![allow(incomplete_features)]
#![feature(rustc_attrs)]
struct S;
trait T {}
impl const T for S {}
//[stock]~^ ERROR const trait impls are experimental
//[stock,gated]~^^ ERROR const trait impls are not yet implemented
fn main() {}
#[rustc_error]
fn main() {} //[gated]~ ERROR fatal error triggered by #[rustc_error]

View File

@ -1,5 +1,5 @@
error[E0658]: const trait impls are experimental
--> $DIR/feature-gate.rs:9:6
--> $DIR/feature-gate.rs:10:6
|
LL | impl const T for S {}
| ^^^^^
@ -7,14 +7,6 @@ LL | impl const T for S {}
= note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: const trait impls are not yet implemented
--> $DIR/feature-gate.rs:9:1
|
LL | impl const T for S {}
| ^^^^^-----^^^^^^^^^^^
| |
| const because of this
error: aborting due to 2 previous errors
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,32 @@
// run-pass
#![allow(incomplete_features)]
#![feature(const_trait_impl)]
#![feature(const_fn)]
use std::marker::PhantomData;
struct S<T>(PhantomData<T>);
impl<T> Copy for S<T> {}
impl<T> Clone for S<T> {
fn clone(&self) -> Self {
S(PhantomData)
}
}
impl<T> const std::ops::Add for S<T> {
type Output = Self;
fn add(self, _: Self) -> Self {
S(std::marker::PhantomData)
}
}
const fn twice<T: std::ops::Add>(arg: S<T>) -> S<T> {
arg + arg
}
fn main() {
let _ = twice(S(PhantomData::<i32>));
}

View File

@ -8,10 +8,8 @@ trait T {}
impl const S {}
//~^ ERROR inherent impls cannot be `const`
//~| ERROR const trait impls are not yet implemented
impl const T {}
//~^ ERROR inherent impls cannot be `const`
//~| ERROR const trait impls are not yet implemented
fn main() {}

View File

@ -9,7 +9,7 @@ LL | impl const S {}
= note: only trait implementations may be annotated with `const`
error: inherent impls cannot be `const`
--> $DIR/inherent-impl.rs:13:1
--> $DIR/inherent-impl.rs:12:1
|
LL | impl const T {}
| ^^^^^-----^^^^^
@ -18,21 +18,5 @@ LL | impl const T {}
|
= note: only trait implementations may be annotated with `const`
error: const trait impls are not yet implemented
--> $DIR/inherent-impl.rs:9:1
|
LL | impl const S {}
| ^^^^^-----^^^^^
| |
| const because of this
error: const trait impls are not yet implemented
--> $DIR/inherent-impl.rs:13:1
|
LL | impl const T {}
| ^^^^^-----^^^^^
| |
| const because of this
error: aborting due to 4 previous errors
error: aborting due to 2 previous errors

View File

@ -0,0 +1,43 @@
#![allow(incomplete_features)]
#![feature(allow_internal_unstable)]
#![feature(const_add)]
#![feature(const_trait_impl)]
#![feature(staged_api)]
pub struct Int(i32);
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "rust1", since = "1.0.0")]
impl const std::ops::Sub for Int {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
//~^ ERROR trait methods cannot be stable const fn
Int(self.0 - rhs.0)
}
}
#[rustc_const_unstable(feature = "const_add", issue = "none")]
impl const std::ops::Add for Int {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Int(self.0 + rhs.0)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "rust1", since = "1.0.0")]
pub const fn foo() -> Int {
Int(1i32) + Int(2i32)
//~^ ERROR can only call other `const fn` within a `const fn`
}
// ok
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "bar", issue = "none")]
pub const fn bar() -> Int {
Int(1i32) + Int(2i32)
}
fn main() {}

View File

@ -0,0 +1,24 @@
error[E0723]: trait methods cannot be stable const fn
--> $DIR/stability.rs:14:5
|
LL | / fn sub(self, rhs: Self) -> Self {
LL | |
LL | | Int(self.0 - rhs.0)
LL | | }
| |_____^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
= help: add `#![feature(const_fn)]` to the crate attributes to enable
error[E0723]: can only call other `const fn` within a `const fn`, but `const <Int as std::ops::Add>::add` is not stable as `const fn`
--> $DIR/stability.rs:32:5
|
LL | Int(1i32) + Int(2i32)
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
= help: add `#![feature(const_fn)]` to the crate attributes to enable
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0723`.