Fix const_fn ICE with non-const function pointer

This commit is contained in:
Philipp Hansch 2018-11-30 08:43:50 +01:00
parent 3e90a12a8a
commit 9b847f0f96
No known key found for this signature in database
GPG Key ID: B6FA06A6E0E2665B
3 changed files with 167 additions and 124 deletions

View File

@ -895,145 +895,160 @@ fn visit_terminator_kind(&mut self,
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;
}
_ => {}
}
}
_ => {
// 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 {
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),
);
name if name.starts_with("simd_shuffle") => {
is_shuffle = true;
}
} 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
_ => {}
}
}
_ => {
// 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 {
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(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_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 {
(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)
});

View File

@ -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() {
}

View File

@ -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`.