diff --git a/compiler/rustc_error_codes/src/error_codes/E0542.md b/compiler/rustc_error_codes/src/error_codes/E0542.md index 7cb58f9d0cb..7fecfeaa57c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0542.md +++ b/compiler/rustc_error_codes/src/error_codes/E0542.md @@ -10,7 +10,7 @@ Erroneous code example: fn _stable_fn() {} #[rustc_const_stable(feature = "_stable_const_fn")] // invalid -fn _stable_const_fn() {} +const fn _stable_const_fn() {} #[stable(feature = "_deprecated_fn", since = "0.1.0")] #[rustc_deprecated( @@ -29,7 +29,7 @@ To fix this issue, you need to provide the `since` field. Example: fn _stable_fn() {} #[rustc_const_stable(feature = "_stable_const_fn", since = "1.0.0")] // ok! -fn _stable_const_fn() {} +const fn _stable_const_fn() {} #[stable(feature = "_deprecated_fn", since = "0.1.0")] #[rustc_deprecated( diff --git a/compiler/rustc_error_codes/src/error_codes/E0545.md b/compiler/rustc_error_codes/src/error_codes/E0545.md index 9fb935a3ab1..7aba084f4d3 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0545.md +++ b/compiler/rustc_error_codes/src/error_codes/E0545.md @@ -10,7 +10,7 @@ Erroneous code example: fn _unstable_fn() {} #[rustc_const_unstable(feature = "_unstable_const_fn", issue = "0")] // invalid -fn _unstable_const_fn() {} +const fn _unstable_const_fn() {} ``` To fix this issue, you need to provide a correct value in the `issue` field. @@ -24,7 +24,7 @@ Example: fn _unstable_fn() {} #[rustc_const_unstable(feature = "_unstable_const_fn", issue = "1")] // ok! -fn _unstable_const_fn() {} +const fn _unstable_const_fn() {} ``` See the [How Rust is Made and “Nightly Rust”][how-rust-made-nightly] appendix diff --git a/compiler/rustc_error_codes/src/error_codes/E0547.md b/compiler/rustc_error_codes/src/error_codes/E0547.md index 1aa4b354248..4950325df64 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0547.md +++ b/compiler/rustc_error_codes/src/error_codes/E0547.md @@ -10,7 +10,7 @@ Erroneous code example: fn _unstable_fn() {} #[rustc_const_unstable(feature = "_unstable_const_fn")] // invalid -fn _unstable_const_fn() {} +const fn _unstable_const_fn() {} ``` To fix this issue, you need to provide the `issue` field. Example: @@ -26,7 +26,7 @@ fn _unstable_fn() {} feature = "_unstable_const_fn", issue = "none" )] // ok! -fn _unstable_const_fn() {} +const fn _unstable_const_fn() {} ``` See the [How Rust is Made and “Nightly Rust”][how-rust-made-nightly] appendix diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 5830245e837..cd8dd6984d5 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -20,6 +20,7 @@ use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::spec::abi::Abi; use std::cmp::Ordering; use std::iter; @@ -95,10 +96,12 @@ struct Annotator<'a, 'tcx> { impl<'a, 'tcx> Annotator<'a, 'tcx> { // Determine the stability for a node based on its attributes and inherited // stability. The stability is recorded in the index and used as the parent. + // If the node is a function, `fn_sig` is its signature fn annotate( &mut self, hir_id: HirId, item_sp: Span, + fn_sig: Option<&'tcx hir::FnSig<'tcx>>, kind: AnnotationKind, inherit_deprecation: InheritDeprecation, inherit_const_stability: InheritConstStability, @@ -163,13 +166,30 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp); + let mut const_span = None; - let const_stab = const_stab.map(|(const_stab, _)| { + let const_stab = const_stab.map(|(const_stab, const_span_node)| { let const_stab = self.tcx.intern_const_stability(const_stab); self.index.const_stab_map.insert(hir_id, const_stab); + const_span = Some(const_span_node); const_stab }); + // If the current node is a function, has const stability attributes and if it doesn not have an intrinsic ABI, + // check if the function/method is const or the parent impl block is const + if let (Some(const_span), Some(fn_sig)) = (const_span, fn_sig) { + if fn_sig.header.abi != Abi::RustIntrinsic + && fn_sig.header.abi != Abi::PlatformIntrinsic + && !fn_sig.header.is_const() + { + if !self.in_trait_impl + || (self.in_trait_impl && !self.tcx.is_const_fn_raw(hir_id.owner.to_def_id())) + { + missing_const_err(&self.tcx.sess, fn_sig.span, const_span); + } + } + } + // `impl const Trait for Type` items forward their const stability to their // immediate children. if const_stab.is_none() { @@ -367,6 +387,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { let orig_in_trait_impl = self.in_trait_impl; let mut kind = AnnotationKind::Required; let mut const_stab_inherit = InheritConstStability::No; + let mut fn_sig = None; + match i.kind { // Inherent impls and foreign modules serve only as containers for other items, // they don't have their own stability. They still can be annotated as unstable @@ -387,6 +409,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { self.annotate( ctor_hir_id, i.span, + None, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -395,12 +418,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ) } } + hir::ItemKind::Fn(ref item_fn_sig, _, _) => { + fn_sig = Some(item_fn_sig); + } _ => {} } self.annotate( i.hir_id(), i.span, + fn_sig, kind, InheritDeprecation::Yes, const_stab_inherit, @@ -411,9 +438,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { } fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { + let fn_sig = match ti.kind { + hir::TraitItemKind::Fn(ref fn_sig, _) => Some(fn_sig), + _ => None, + }; + self.annotate( ti.hir_id(), ti.span, + fn_sig, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -427,9 +460,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { let kind = if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required }; + + let fn_sig = match ii.kind { + hir::ImplItemKind::Fn(ref fn_sig, _) => Some(fn_sig), + _ => None, + }; + self.annotate( ii.hir_id(), ii.span, + fn_sig, kind, InheritDeprecation::Yes, InheritConstStability::No, @@ -444,6 +484,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { self.annotate( var.id, var.span, + None, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -453,6 +494,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { v.annotate( ctor_hir_id, var.span, + None, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -470,6 +512,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { self.annotate( s.hir_id, s.span, + None, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -484,6 +527,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { self.annotate( i.hir_id(), i.span, + None, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -498,6 +542,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { self.annotate( md.hir_id(), md.span, + None, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -517,6 +562,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { self.annotate( p.hir_id, p.span, + None, kind, InheritDeprecation::No, InheritConstStability::No, @@ -687,6 +733,7 @@ fn stability_index(tcx: TyCtxt<'tcx>, (): ()) -> Index<'tcx> { annotator.annotate( hir::CRATE_HIR_ID, krate.item.inner, + None, AnnotationKind::Required, InheritDeprecation::Yes, InheritConstStability::No, @@ -969,3 +1016,15 @@ fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) { struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature) .emit(); } + +fn missing_const_err(session: &Session, fn_sig_span: Span, const_span: Span) { + const ERROR_MSG: &'static str = "attributes `#[rustc_const_unstable]` \ + and `#[rustc_const_stable]` require \ + the function or method to be `const`"; + + session + .struct_span_err(fn_sig_span, ERROR_MSG) + .span_help(fn_sig_span, "make the function or method const") + .span_label(const_span, "attribute specified here") + .emit(); +} diff --git a/src/test/ui/consts/rustc-const-stability-require-const.rs b/src/test/ui/consts/rustc-const-stability-require-const.rs new file mode 100644 index 00000000000..4fb259b335c --- /dev/null +++ b/src/test/ui/consts/rustc-const-stability-require-const.rs @@ -0,0 +1,47 @@ +#![crate_type = "lib"] +#![feature(staged_api)] +#![stable(feature = "foo", since = "1.0.0")] + +#[stable(feature = "foo", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_foo", issue = "none")] +pub fn foo() {} +//~^ ERROR attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + +#[stable(feature = "bar", since = "1.0.0")] +#[rustc_const_stable(feature = "const_bar", since = "1.0.0")] +pub fn bar() {} +//~^ ERROR attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + +#[stable(feature = "potato", since = "1.0.0")] +pub struct Potato; + +impl Potato { + #[stable(feature = "salad", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_salad", issue = "none")] + pub fn salad(&self) -> &'static str { "mmmmmm" } + //~^ ERROR attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + + #[stable(feature = "roasted", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_roasted", issue = "none")] + pub fn roasted(&self) -> &'static str { "mmmmmmmmmm" } + //~^ ERROR attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` +} + +#[stable(feature = "bar", since = "1.0.0")] +#[rustc_const_stable(feature = "const_bar", since = "1.0.0")] +pub extern "C" fn bar_c() {} +//~^ ERROR attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + +#[stable(feature = "foo", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_foo", issue = "none")] +pub extern "C" fn foo_c() {} +//~^ ERROR attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + + +#[stable(feature = "foobar", since = "1.0.0")] +#[rustc_const_unstable(feature = "foobar_const", issue = "none")] +pub const fn foobar() {} + +#[stable(feature = "barfoo", since = "1.0.0")] +#[rustc_const_stable(feature = "barfoo_const", since = "1.0.0")] +pub const fn barfoo() {} diff --git a/src/test/ui/consts/rustc-const-stability-require-const.stderr b/src/test/ui/consts/rustc-const-stability-require-const.stderr new file mode 100644 index 00000000000..1027b9311b7 --- /dev/null +++ b/src/test/ui/consts/rustc-const-stability-require-const.stderr @@ -0,0 +1,86 @@ +error: attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + --> $DIR/rustc-const-stability-require-const.rs:7:1 + | +LL | #[rustc_const_unstable(feature = "const_foo", issue = "none")] + | -------------------------------------------------------------- attribute specified here +LL | pub fn foo() {} + | ^^^^^^^^^^^^ + | +help: make the function or method const + --> $DIR/rustc-const-stability-require-const.rs:7:1 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^ + +error: attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + --> $DIR/rustc-const-stability-require-const.rs:12:1 + | +LL | #[rustc_const_stable(feature = "const_bar", since = "1.0.0")] + | ------------------------------------------------------------- attribute specified here +LL | pub fn bar() {} + | ^^^^^^^^^^^^ + | +help: make the function or method const + --> $DIR/rustc-const-stability-require-const.rs:12:1 + | +LL | pub fn bar() {} + | ^^^^^^^^^^^^ + +error: attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + --> $DIR/rustc-const-stability-require-const.rs:21:5 + | +LL | #[rustc_const_unstable(feature = "const_salad", issue = "none")] + | ---------------------------------------------------------------- attribute specified here +LL | pub fn salad(&self) -> &'static str { "mmmmmm" } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function or method const + --> $DIR/rustc-const-stability-require-const.rs:21:5 + | +LL | pub fn salad(&self) -> &'static str { "mmmmmm" } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + --> $DIR/rustc-const-stability-require-const.rs:26:5 + | +LL | #[rustc_const_unstable(feature = "const_roasted", issue = "none")] + | ------------------------------------------------------------------ attribute specified here +LL | pub fn roasted(&self) -> &'static str { "mmmmmmmmmm" } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function or method const + --> $DIR/rustc-const-stability-require-const.rs:26:5 + | +LL | pub fn roasted(&self) -> &'static str { "mmmmmmmmmm" } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + --> $DIR/rustc-const-stability-require-const.rs:32:1 + | +LL | #[rustc_const_stable(feature = "const_bar", since = "1.0.0")] + | ------------------------------------------------------------- attribute specified here +LL | pub extern "C" fn bar_c() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function or method const + --> $DIR/rustc-const-stability-require-const.rs:32:1 + | +LL | pub extern "C" fn bar_c() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: attributes `#[rustc_const_unstable]` and `#[rustc_const_stable]` require the function or method to be `const` + --> $DIR/rustc-const-stability-require-const.rs:37:1 + | +LL | #[rustc_const_unstable(feature = "const_foo", issue = "none")] + | -------------------------------------------------------------- attribute specified here +LL | pub extern "C" fn foo_c() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function or method const + --> $DIR/rustc-const-stability-require-const.rs:37:1 + | +LL | pub extern "C" fn foo_c() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/consts/rustc-impl-const-stability.rs b/src/test/ui/consts/rustc-impl-const-stability.rs new file mode 100644 index 00000000000..01daa1c9cb4 --- /dev/null +++ b/src/test/ui/consts/rustc-impl-const-stability.rs @@ -0,0 +1,21 @@ +// build-pass + +#![crate_type = "lib"] +#![allow(incomplete_features)] +#![feature(staged_api)] +#![feature(const_trait_impl)] +#![stable(feature = "foo", since = "1.0.0")] + + +#[stable(feature = "potato", since = "1.27.0")] +pub struct Data { + _data: u128 +} + +#[stable(feature = "potato", since = "1.27.0")] +impl const Default for Data { + #[rustc_const_unstable(feature = "data_foo", issue = "none")] + fn default() -> Data { + Data { _data: 42 } + } +}