diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 09fe7b14c7973..bcee6d75b5a4a 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -895,145 +895,160 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { let mut is_shuffle = false; let mut is_const_fn = false; let mut is_promotable_const_fn = false; - if let ty::FnDef(def_id, _) = fn_ty.sty { - callee_def_id = Some(def_id); - match self.tcx.fn_sig(def_id).abi() { - Abi::RustIntrinsic | - Abi::PlatformIntrinsic => { - assert!(!self.tcx.is_const_fn(def_id)); - match &self.tcx.item_name(def_id).as_str()[..] { - | "size_of" - | "min_align_of" - | "needs_drop" - | "type_id" - | "bswap" - | "bitreverse" - | "ctpop" - | "cttz" - | "cttz_nonzero" - | "ctlz" - | "ctlz_nonzero" - | "overflowing_add" - | "overflowing_sub" - | "overflowing_mul" - | "unchecked_shl" - | "unchecked_shr" - | "rotate_left" - | "rotate_right" - | "add_with_overflow" - | "sub_with_overflow" - | "mul_with_overflow" - // no need to check feature gates, intrinsics are only callable from the - // libstd or with forever unstable feature gates - => is_const_fn = true, - // special intrinsic that can be called diretly without an intrinsic - // feature gate needs a language feature gate - "transmute" => { - // never promote transmute calls - if self.mode != Mode::Fn { - is_const_fn = true; - // const eval transmute calls only with the feature gate - if !self.tcx.features().const_transmute { - emit_feature_err( - &self.tcx.sess.parse_sess, "const_transmute", - self.span, GateIssue::Language, - &format!("The use of std::mem::transmute() \ - is gated in {}s", self.mode)); + match fn_ty.sty { + ty::FnDef(def_id, _) => { + callee_def_id = Some(def_id); + match self.tcx.fn_sig(def_id).abi() { + Abi::RustIntrinsic | + Abi::PlatformIntrinsic => { + assert!(!self.tcx.is_const_fn(def_id)); + match &self.tcx.item_name(def_id).as_str()[..] { + | "size_of" + | "min_align_of" + | "needs_drop" + | "type_id" + | "bswap" + | "bitreverse" + | "ctpop" + | "cttz" + | "cttz_nonzero" + | "ctlz" + | "ctlz_nonzero" + | "overflowing_add" + | "overflowing_sub" + | "overflowing_mul" + | "unchecked_shl" + | "unchecked_shr" + | "rotate_left" + | "rotate_right" + | "add_with_overflow" + | "sub_with_overflow" + | "mul_with_overflow" + // no need to check feature gates, intrinsics are only callable + // from the libstd or with forever unstable feature gates + => is_const_fn = true, + // special intrinsic that can be called diretly without an intrinsic + // feature gate needs a language feature gate + "transmute" => { + // never promote transmute calls + if self.mode != Mode::Fn { + is_const_fn = true; + // const eval transmute calls only with the feature gate + if !self.tcx.features().const_transmute { + emit_feature_err( + &self.tcx.sess.parse_sess, "const_transmute", + self.span, GateIssue::Language, + &format!("The use of std::mem::transmute() \ + is gated in {}s", self.mode)); + } } } - } - name if name.starts_with("simd_shuffle") => { - is_shuffle = true; - } + name if name.starts_with("simd_shuffle") => { + is_shuffle = true; + } - _ => {} - } - } - _ => { - // in normal functions we only care about promotion - if self.mode == Mode::Fn { - // never promote const fn calls of - // functions without #[rustc_promotable] - if self.tcx.is_promotable_const_fn(def_id) { - is_const_fn = true; - is_promotable_const_fn = true; - } else if self.tcx.is_const_fn(def_id) { - is_const_fn = true; + _ => {} } - } else { - // stable const fn or unstable const fns with their feature gate - // active - if self.tcx.is_const_fn(def_id) { - is_const_fn = true; - } else if self.is_const_panic_fn(def_id) { - // check the const_panic feature gate - // FIXME: cannot allow this inside `allow_internal_unstable` because - // that would make `panic!` insta stable in constants, since the - // macro is marked with the attr - if self.tcx.features().const_panic { + } + _ => { + // in normal functions we only care about promotion + if self.mode == Mode::Fn { + // never promote const fn calls of + // functions without #[rustc_promotable] + if self.tcx.is_promotable_const_fn(def_id) { is_const_fn = true; - } else { - // don't allow panics in constants without the feature gate - emit_feature_err( - &self.tcx.sess.parse_sess, - "const_panic", - self.span, - GateIssue::Language, - &format!("panicking in {}s is unstable", self.mode), - ); - } - } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) { - // check `#[unstable]` const fns or `#[rustc_const_unstable]` - // functions without the feature gate active in this crate to report - // a better error message than the one below - if self.span.allows_unstable() { - // `allow_internal_unstable` can make such calls stable + is_promotable_const_fn = true; + } else if self.tcx.is_const_fn(def_id) { is_const_fn = true; - } else { - let mut err = self.tcx.sess.struct_span_err(self.span, - &format!("`{}` is not yet stable as a const fn", - self.tcx.item_path_str(def_id))); - help!(&mut err, - "in Nightly builds, add `#![feature({})]` \ - to the crate attributes to enable", - feature); - err.emit(); } } else { - // FIXME(#24111) Remove this check when const fn stabilizes - let (msg, note) = if let UnstableFeatures::Disallow = - self.tcx.sess.opts.unstable_features { - (format!("calls in {}s are limited to \ - tuple structs and tuple variants", - self.mode), - Some("a limited form of compile-time function \ - evaluation is available on a nightly \ - compiler via `const fn`")) + // stable const fn or unstable const fns with their feature gate + // active + if self.tcx.is_const_fn(def_id) { + is_const_fn = true; + } else if self.is_const_panic_fn(def_id) { + // check the const_panic feature gate + // FIXME: cannot allow this inside `allow_internal_unstable` + // because that would make `panic!` insta stable in constants, + // since the macro is marked with the attr + if self.tcx.features().const_panic { + is_const_fn = true; + } else { + // don't allow panics in constants without the feature gate + emit_feature_err( + &self.tcx.sess.parse_sess, + "const_panic", + self.span, + GateIssue::Language, + &format!("panicking in {}s is unstable", self.mode), + ); + } + } else if let Some(feat) = self.tcx.is_unstable_const_fn(def_id) { + // check `#[unstable]` const fns or `#[rustc_const_unstable]` + // functions without the feature gate active in this crate to + // report a better error message than the one below + if self.span.allows_unstable() { + // `allow_internal_unstable` can make such calls stable + is_const_fn = true; + } else { + let mut err = self.tcx.sess.struct_span_err(self.span, + &format!("`{}` is not yet stable as a const fn", + self.tcx.item_path_str(def_id))); + help!(&mut err, + "in Nightly builds, add `#![feature({})]` \ + to the crate attributes to enable", + feat); + err.emit(); + } } else { - (format!("calls in {}s are limited \ - to constant functions, \ - tuple structs and tuple variants", - self.mode), - None) - }; - let mut err = struct_span_err!( - self.tcx.sess, - self.span, - E0015, - "{}", - msg, - ); - if let Some(note) = note { - err.span_note(self.span, note); + // FIXME(#24111) Remove this check when const fn stabilizes + let (msg, note) = if let UnstableFeatures::Disallow = + self.tcx.sess.opts.unstable_features { + (format!("calls in {}s are limited to \ + tuple structs and tuple variants", + self.mode), + Some("a limited form of compile-time function \ + evaluation is available on a nightly \ + compiler via `const fn`")) + } else { + (format!("calls in {}s are limited \ + to constant functions, \ + tuple structs and tuple variants", + self.mode), + None) + }; + let mut err = struct_span_err!( + self.tcx.sess, + self.span, + E0015, + "{}", + msg, + ); + if let Some(note) = note { + err.span_note(self.span, note); + } + err.emit(); } - err.emit(); } } } + }, + ty::FnPtr(_) => { + if self.mode != Mode::Fn { + let mut err = self.tcx.sess.struct_span_err( + self.span, + &format!("function pointers are not allowed in const fn")); + err.emit(); + } + }, + _ => { + self.not_const(); + return } } + let constant_arguments = callee_def_id.and_then(|id| { args_required_const(self.tcx, id) }); diff --git a/src/test/ui/consts/issue-56164.rs b/src/test/ui/consts/issue-56164.rs new file mode 100644 index 0000000000000..9d1a8b59463e7 --- /dev/null +++ b/src/test/ui/consts/issue-56164.rs @@ -0,0 +1,13 @@ +#![feature(const_fn)] + +const fn foo() { (||{})() } +//~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple +// variants + +const fn bad(input: fn()) { + input() + //~^ ERROR function pointers are not allowed in const fn +} + +fn main() { +} diff --git a/src/test/ui/consts/issue-56164.stderr b/src/test/ui/consts/issue-56164.stderr new file mode 100644 index 0000000000000..d3e9ce379aed7 --- /dev/null +++ b/src/test/ui/consts/issue-56164.stderr @@ -0,0 +1,15 @@ +error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants + --> $DIR/issue-56164.rs:3:18 + | +LL | const fn foo() { (||{})() } + | ^^^^^^^^ + +error: function pointers are not allowed in const fn + --> $DIR/issue-56164.rs:8:5 + | +LL | input() + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0015`.