From 62ff11f1a4d27a61ea60d309ed8fe738c07ce2ef Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 3 Feb 2020 12:21:27 -0800 Subject: [PATCH 01/16] Add `is_const_impl_raw` query --- src/librustc/query/mod.rs | 8 ++++++++ src/librustc_mir/const_eval/fn_queries.rs | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) 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_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs index 4144bbc41d217..5706d435f7e92 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; @@ -119,6 +119,19 @@ pub fn provide(providers: &mut Providers<'_>) { } } + /// 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) { @@ -148,6 +161,7 @@ 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 From 3e0b0605a04d029ff812147f3d54f95a211769b1 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 3 Feb 2020 14:57:45 -0800 Subject: [PATCH 02/16] Const-check functions in a `const` impl --- src/librustc_mir/const_eval/fn_queries.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs index 5706d435f7e92..b4bbcd1ca4f9c 100644 --- a/src/librustc_mir/const_eval/fn_queries.rs +++ b/src/librustc_mir/const_eval/fn_queries.rs @@ -26,6 +26,8 @@ pub fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + // FIXME: check for `rustc_const_unstable` on the containing impl. This should be done by + // propagating it down so it is return by the `lookup_const_stability` query. if tcx.is_const_fn_raw(def_id) { let const_stab = tcx.lookup_const_stability(def_id)?; if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None } @@ -111,7 +113,18 @@ pub fn provide(providers: &mut Providers<'_>) { 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 + if fn_like.constness() == hir::Constness::Const { + return true; + } + + // If the function itself is not annotated with `const`, it may still be a `const fn` + // if it resides in a const trait impl. + let parent_id = tcx.hir().get_parent_did(hir_id); + if def_id != parent_id && !parent_id.is_top_level_module() { + is_const_impl_raw(tcx, LocalDefId::from_def_id(parent_id)) + } else { + false + } } else if let hir::Node::Ctor(_) = node { true } else { From 5e422efba1be879633c7be7ad178915f2ace7f04 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 3 Feb 2020 15:02:30 -0800 Subject: [PATCH 03/16] Remove "not yet implemented" warning --- src/librustc_ast_lowering/item.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) 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); From 7a019b1bd2b13993bb2039053d815df02504083e Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 4 Feb 2020 14:03:16 -0800 Subject: [PATCH 04/16] Check for trait methods on concrete types in const checking --- .../transform/check_consts/validation.rs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs index 167d8145c030f..1a1c34e1c6742 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; @@ -501,8 +501,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); @@ -519,6 +519,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) { From 323ff193b8eda1c89eada54a1b3ac5c20d9e24c7 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 4 Feb 2020 14:03:37 -0800 Subject: [PATCH 05/16] Add tests for calling trait methods on concrete types --- .../call-const-trait-method.rs | 30 +++++++++++++++++++ .../call-const-trait-method.stderr | 9 ++++++ .../const-check-fns-in-const-impl.rs | 16 ++++++++++ .../const-check-fns-in-const-impl.stderr | 12 ++++++++ .../feature-gate.gated.stderr | 10 +++---- .../rfc-2632-const-trait-impl/feature-gate.rs | 5 ++-- .../feature-gate.stock.stderr | 12 ++------ .../inherent-impl.rs | 2 -- .../inherent-impl.stderr | 20 ++----------- 9 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.rs b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.rs new file mode 100644 index 0000000000000..ed6c07e4653a8 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.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) +} + +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.stderr b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.stderr new file mode 100644 index 0000000000000..7216876c83adf --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.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.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/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..cad4f69033b2a --- /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 +} + +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..8c220dc44ab47 --- /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: for more information, see issue https://github.com/rust-lang/rust/issues/57563 + = 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/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 From d6d6d25c343fb4fdf6c853bd23524a77efc4c53b Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 5 Feb 2020 09:35:32 -0800 Subject: [PATCH 06/16] Split const trait method test and impl `ops::Add` --- ...hod.rs => call-const-trait-method-fail.rs} | 2 +- ...rr => call-const-trait-method-fail.stderr} | 2 +- .../call-const-trait-method-pass.rs | 41 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) rename src/test/ui/rfc-2632-const-trait-impl/{call-const-trait-method.rs => call-const-trait-method-fail.rs} (96%) rename src/test/ui/rfc-2632-const-trait-impl/{call-const-trait-method.stderr => call-const-trait-method-fail.stderr} (84%) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.rs b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs similarity index 96% rename from src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.rs rename to src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs index ed6c07e4653a8..8e6ef12810c2c 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs @@ -19,7 +19,7 @@ impl Plus for u32 { } pub const fn add_i32(a: i32, b: i32) -> i32 { - a.plus(b) + a.plus(b) // ok } pub const fn add_u32(a: u32, b: u32) -> u32 { diff --git a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.stderr b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr similarity index 84% rename from src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.stderr rename to src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr index 7216876c83adf..0c320d54c766d 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr @@ -1,5 +1,5 @@ error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants - --> $DIR/call-const-trait-method.rs:26:5 + --> $DIR/call-const-trait-method-fail.rs:26:5 | LL | a.plus(b) | ^^^^^^^^^ 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)); +} From 70f78797d5f3e4ea7fb4ae853dc0f368007c56a2 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Wed, 5 Feb 2020 09:40:47 -0800 Subject: [PATCH 07/16] Ensure const impl cannot coexist with non-const impl --- .../const-and-non-const-impl.rs | 33 ++++++++++++++++++ .../const-and-non-const-impl.stderr | 34 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr 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`. From 0a5abcac7f43dde694ae202678b8271e8b57279f Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Fri, 7 Feb 2020 09:56:56 -0800 Subject: [PATCH 08/16] Use early return when forbidding unstable attrs --- src/librustc_passes/stability.rs | 246 ++++++++++++++++--------------- 1 file changed, 129 insertions(+), 117 deletions(-) diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs index 99f005c29e875..7d93666b1a629 100644 --- a/src/librustc_passes/stability.rs +++ b/src/librustc_passes/stability.rs @@ -58,144 +58,156 @@ 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 !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); + 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 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); + 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); - } + let orig_parent_stab = replace(&mut self.parent_stab, Some(stab)); + visit_children(self); + self.parent_stab = orig_parent_stab; } 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. + 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); + } + } - 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"); - } + 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(); + } + } - // `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); + // 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); + } } } From 8e7609b9fe12f4a972a55afb75b1a0098ada1378 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Fri, 7 Feb 2020 10:24:22 -0800 Subject: [PATCH 09/16] Propagate `rustc_const_unstable` to children --- src/librustc_passes/stability.rs | 65 ++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs index 7d93666b1a629..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, } @@ -64,6 +65,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } // 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( @@ -72,13 +74,25 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { 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 (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); + } + } } - if let Some(mut stab) = 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 @@ -137,18 +151,46 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } self.index.stab_map.insert(hir_id, stab); + 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 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); } } - visit_children(self); + } + + 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))); + } + + 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; } } @@ -388,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, }; From 4992eb2c6fab0c7eb4a841385607723620a38cf6 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Fri, 7 Feb 2020 11:06:12 -0800 Subject: [PATCH 10/16] Test `rustc_const_unstable` on trait fns --- .../ui/rfc-2632-const-trait-impl/stability.rs | 33 +++++++++++++++++++ .../stability.stderr | 12 +++++++ 2 files changed, 45 insertions(+) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/stability.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/stability.stderr 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..36cfaff2ffad4 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.rs @@ -0,0 +1,33 @@ +#![allow(incomplete_features)] +#![feature(allow_internal_unstable)] +#![feature(const_add)] +#![feature(const_trait_impl)] +#![feature(staged_api)] + +pub struct Int(i32); + +#[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..ce3bada01e853 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr @@ -0,0 +1,12 @@ +error[E0723]: can only call other `const fn` within a `const fn`, but `const ::add` is not stable as `const fn` + --> $DIR/stability.rs:21:5 + | +LL | Int(1i32) + Int(2i32) + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 + = 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`. From c8f0abb51c59ee4c704eda1bb2a7e9e70173b074 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Fri, 7 Feb 2020 11:06:44 -0800 Subject: [PATCH 11/16] Add ignored test for associated types in const impl --- .../rfc-2632-const-trait-impl/assoc-type.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/assoc-type.rs 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() {} From 9a3682438d3104529d080050c6fd3744b2b4d3b6 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Fri, 7 Feb 2020 12:32:49 -0800 Subject: [PATCH 12/16] Remove outdated FIXME --- src/librustc_mir/const_eval/fn_queries.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs index b4bbcd1ca4f9c..1f7eb97690288 100644 --- a/src/librustc_mir/const_eval/fn_queries.rs +++ b/src/librustc_mir/const_eval/fn_queries.rs @@ -26,8 +26,6 @@ pub fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - // FIXME: check for `rustc_const_unstable` on the containing impl. This should be done by - // propagating it down so it is return by the `lookup_const_stability` query. if tcx.is_const_fn_raw(def_id) { let const_stab = tcx.lookup_const_stability(def_id)?; if const_stab.level.is_unstable() { Some(const_stab.feature) } else { None } From 160e6304e8e7971d38787e8ca81f154e31f5b772 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Mon, 10 Feb 2020 11:26:40 -0800 Subject: [PATCH 13/16] Add passing test for `Add` on generic struct --- .../generic-bound.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/generic-bound.rs 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::)); +} From cb81712d10f4af3a55dd2368d9714daa425fcf22 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 18 Feb 2020 15:45:14 -0800 Subject: [PATCH 14/16] Make `fn_queries` helpers module-private --- src/librustc_mir/const_eval/fn_queries.rs | 149 +++++++++++----------- 1 file changed, 74 insertions(+), 75 deletions(-) diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs index 1f7eb97690288..27efcd508414a 100644 --- a/src/librustc_mir/const_eval/fn_queries.rs +++ b/src/librustc_mir/const_eval/fn_queries.rs @@ -82,94 +82,93 @@ 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; - } - - match tcx.fn_sig(def_id).abi() { - Abi::RustIntrinsic | Abi::PlatformIntrinsic => { - Some(tcx.lookup_const_stability(def_id).is_some()) - } - _ => 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); +/// 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 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; - } + let node = tcx.hir().get(hir_id); - // If the function itself is not annotated with `const`, it may still be a `const fn` - // if it resides in a const trait impl. - let parent_id = tcx.hir().get_parent_did(hir_id); - if def_id != parent_id && !parent_id.is_top_level_module() { - is_const_impl_raw(tcx, LocalDefId::from_def_id(parent_id)) - } else { - false - } - } else if let hir::Node::Ctor(_) = node { - true - } else { - false + 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; } + + // 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 } +} - /// 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, .. }, - .. - }) - ) +/// 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; } - 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 + match tcx.fn_sig(def_id).abi() { + Abi::RustIntrinsic | Abi::PlatformIntrinsic => { + Some(tcx.lookup_const_stability(def_id).is_some()) + } + _ => None, + } +} + +/// 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)), From 5f06ce2c0f3938dd69a0676c7abe2243046c2f09 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 18 Feb 2020 15:45:39 -0800 Subject: [PATCH 15/16] Prevent const trait methods from being marked stable --- src/librustc_mir/transform/qualify_min_const_fn.rs | 8 ++++++++ 1 file changed, 8 insertions(+) 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); From 19801b12c950f727a4e6938a87b0b2fdece73c07 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 18 Feb 2020 15:45:54 -0800 Subject: [PATCH 16/16] Update tests --- .../const-check-fns-in-const-impl.rs | 2 +- .../const-check-fns-in-const-impl.stderr | 2 +- .../ui/rfc-2632-const-trait-impl/stability.rs | 12 +++++++++++- .../rfc-2632-const-trait-impl/stability.stderr | 18 +++++++++++++++--- 4 files changed, 28 insertions(+), 6 deletions(-) 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 index cad4f69033b2a..3278f35bad2b2 100644 --- 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 @@ -10,7 +10,7 @@ fn non_const() {} impl const T for S { fn foo() { non_const() } - //~^ ERROR + //~^ 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 index 8c220dc44ab47..7fe3a9fd8522c 100644 --- 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 @@ -4,7 +4,7 @@ error[E0723]: can only call other `const fn` within a `const fn`, but `const non LL | fn foo() { non_const() } | ^^^^^^^^^^^ | - = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 + = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable error: aborting due to previous error diff --git a/src/test/ui/rfc-2632-const-trait-impl/stability.rs b/src/test/ui/rfc-2632-const-trait-impl/stability.rs index 36cfaff2ffad4..03a6fb51503b5 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/stability.rs +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.rs @@ -6,6 +6,17 @@ 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; @@ -29,5 +40,4 @@ 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 index ce3bada01e853..1ecff62955b99 100644 --- a/src/test/ui/rfc-2632-const-trait-impl/stability.stderr +++ b/src/test/ui/rfc-2632-const-trait-impl/stability.stderr @@ -1,12 +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:21:5 + --> $DIR/stability.rs:32:5 | LL | Int(1i32) + Int(2i32) | ^^^^^^^^^^^^^^^^^^^^^ | - = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 + = note: see issue #57563 for more information = help: add `#![feature(const_fn)]` to the crate attributes to enable -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0723`.