diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 45ab3fc0b85a6..3cee10f1356bf 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -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) } } diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs index e0db8606bc203..ad7221b16b2d9 100644 --- a/src/librustc_ast_lowering/item.rs +++ b/src/librustc_ast_lowering/item.rs @@ -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); diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs index 4144bbc41d217..27efcd508414a 100644 --- a/src/librustc_mir/const_eval/fn_queries.rs +++ b/src/librustc_mir/const_eval/fn_queries.rs @@ -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 { - 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 + } +} - match tcx.fn_sig(def_id).abi() { - Abi::RustIntrinsic | Abi::PlatformIntrinsic => { - Some(tcx.lookup_const_stability(def_id).is_some()) - } - _ => 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) { + if fn_like.constness() == hir::Constness::Const { + return true; } - } - /// 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"); + // 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 + } +} - let node = tcx.hir().get(hir_id); +/// Const evaluability whitelist is here to check evaluability at the +/// top level beforehand. +fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + if tcx.is_closure(def_id) { + return None; + } - 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 diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index c5a51bfb2829e..1553f826c7e4b 100644 --- a/src/librustc_mir/transform/check_consts/validation.rs +++ b/src/librustc_mir/transform/check_consts/validation.rs @@ -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) { diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index b12f4ce32698f..5a99ee27301ad 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -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); diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs index 99f005c29e875..d9296deb1d963 100644 --- a/src/librustc_passes/stability.rs +++ b/src/librustc_passes/stability.rs @@ -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, 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", - ); + 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); + } } - 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); + } + + 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"); } - 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"); - } - 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 - } + 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); - - // 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) + 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) + { + // 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::(), 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::(), 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); + } + + 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; + + 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))); + } - // `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); + 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, }; diff --git a/src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs b/src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs new file mode 100644 index 0000000000000..194929fa28717 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs new file mode 100644 index 0000000000000..8e6ef12810c2c --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr new file mode 100644 index 0000000000000..0c320d54c766d --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr @@ -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`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs new file mode 100644 index 0000000000000..6a2112ea55490 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs @@ -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)); +} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs new file mode 100644 index 0000000000000..e148ad9a0ee14 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr new file mode 100644 index 0000000000000..b57472d9595f9 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr @@ -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`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs new file mode 100644 index 0000000000000..3278f35bad2b2 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr new file mode 100644 index 0000000000000..7fe3a9fd8522c --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr @@ -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 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`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr index 061af3c94b44d..d1ab99e33e992 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr @@ -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 diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs index 49b6c0926c50c..d9772431941d3 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs @@ -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] diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr index cfe226ea7a7ce..724090e49cd41 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr @@ -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 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`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs b/src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs new file mode 100644 index 0000000000000..7829ffe2a38d9 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs @@ -0,0 +1,32 @@ +// run-pass + +#![allow(incomplete_features)] +#![feature(const_trait_impl)] +#![feature(const_fn)] + +use std::marker::PhantomData; + +struct S(PhantomData); + +impl Copy for S {} +impl Clone for S { + fn clone(&self) -> Self { + S(PhantomData) + } +} + +impl const std::ops::Add for S { + type Output = Self; + + fn add(self, _: Self) -> Self { + S(std::marker::PhantomData) + } +} + +const fn twice(arg: S) -> S { + arg + arg +} + +fn main() { + let _ = twice(S(PhantomData::)); +} diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs index 7f064c0c53ade..04123a532bd9f 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr index bdc95ff2a57b5..3ea58a3728a5d 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr @@ -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 diff --git a/src/test/ui/rfc-2632-const-trait-impl/stability.rs b/src/test/ui/rfc-2632-const-trait-impl/stability.rs new file mode 100644 index 0000000000000..03a6fb51503b5 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/stability.stderr b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr new file mode 100644 index 0000000000000..1ecff62955b99 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr @@ -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 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 ::add` is not stable as `const fn` + --> $DIR/stability.rs:32:5 + | +LL | Int(1i32) + Int(2i32) + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #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`.