diff options
| author | bors <bors@rust-lang.org> | 2020-02-20 08:41:17 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2020-02-20 08:41:17 +0000 |
| commit | 6af388b25050bca26710be7e4030e17bf6d8d2f7 (patch) | |
| tree | 7126a3429ceb0e584cdcca8b7b75ecaaebf88d02 | |
| parent | de362d88ea17ab23ca2483cb798bc7aeb81a48f5 (diff) | |
| parent | 19801b12c950f727a4e6938a87b0b2fdece73c07 (diff) | |
| download | rust-6af388b25050bca26710be7e4030e17bf6d8d2f7.tar.gz rust-6af388b25050bca26710be7e4030e17bf6d8d2f7.zip | |
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
22 files changed, 597 insertions, 221 deletions
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index 45ab3fc0b85..3cee10f1356 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 e0db8606bc2..ad7221b16b2 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 4144bbc41d2..27efcd50841 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<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 + } +} - 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<bool> { + 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 c5a51bfb282..1553f826c7e 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 b12f4ce3269..5a99ee27301 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 99f005c29e8..d9296deb1d9 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<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", - ); + 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::<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); + } + + 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 00000000000..194929fa287 --- /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 00000000000..8e6ef12810c --- /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 00000000000..0c320d54c76 --- /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 00000000000..6a2112ea554 --- /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 00000000000..e148ad9a0ee --- /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 00000000000..b57472d9595 --- /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 00000000000..3278f35bad2 --- /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 00000000000..7fe3a9fd852 --- /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 <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`. 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 061af3c94b4..d1ab99e33e9 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 49b6c0926c5..d9772431941 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 cfe226ea7a7..724090e49cd 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 <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`. 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 00000000000..7829ffe2a38 --- /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<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>)); +} 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 7f064c0c53a..04123a532bd 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 bdc95ff2a57..3ea58a3728a 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 00000000000..03a6fb51503 --- /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 00000000000..1ecff62955b --- /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 <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`. |
