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:
commit
6af388b250
@ -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) }
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
28
src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs
Normal file
28
src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs
Normal 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() {}
|
@ -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() {}
|
@ -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`.
|
@ -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));
|
||||
}
|
@ -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() {}
|
@ -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`.
|
@ -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() {}
|
@ -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`.
|
@ -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
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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`.
|
||||
|
32
src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs
Normal file
32
src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs
Normal 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>));
|
||||
}
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
||||
|
43
src/test/ui/rfc-2632-const-trait-impl/stability.rs
Normal file
43
src/test/ui/rfc-2632-const-trait-impl/stability.rs
Normal 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() {}
|
24
src/test/ui/rfc-2632-const-trait-impl/stability.stderr
Normal file
24
src/test/ui/rfc-2632-const-trait-impl/stability.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user