1
Fork 0

Rollup merge of #88963 - fee1-dead:const-iterator, r=oli-obk

Coerce const FnDefs to implement const Fn traits

You can now pass a FnDef to a function expecting `F` where `F: ~const FnTrait`.

r? ``@oli-obk``

``@rustbot`` label T-compiler F-const_trait_impl
This commit is contained in:
Manish Goregaokar 2021-10-01 14:46:48 -07:00 committed by GitHub
commit 743e842afb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 79 additions and 37 deletions

View file

@ -6,23 +6,6 @@ use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
/// Whether the `def_id` counts as const fn in your current crate, considering all active
/// feature gates
pub fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
tcx.is_const_fn_raw(def_id)
&& match is_unstable_const_fn(tcx, def_id) {
Some(feature_name) => {
// has a `rustc_const_unstable` attribute, check whether the user enabled the
// corresponding feature gate.
tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == feature_name)
}
// functions without const stability are either stable user written
// const fn or the user is using feature gates and we thus don't
// care what they do
None => true,
}
}
/// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it /// 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<Symbol> { pub fn is_unstable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
if tcx.is_const_fn_raw(def_id) { if tcx.is_const_fn_raw(def_id) {
@ -77,7 +60,7 @@ fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
} }
fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
is_const_fn(tcx, def_id) tcx.is_const_fn(def_id)
&& match tcx.lookup_const_stability(def_id) { && match tcx.lookup_const_stability(def_id) {
Some(stab) => { Some(stab) => {
if cfg!(debug_assertions) && stab.promotable { if cfg!(debug_assertions) && stab.promotable {

View file

@ -26,7 +26,6 @@ use rustc_index::vec::{Idx, IndexVec};
use std::cell::Cell; use std::cell::Cell;
use std::{cmp, iter, mem}; use std::{cmp, iter, mem};
use crate::const_eval::{is_const_fn, is_unstable_const_fn};
use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx}; use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx};
use crate::transform::MirPass; use crate::transform::MirPass;
@ -658,9 +657,7 @@ impl<'tcx> Validator<'_, 'tcx> {
let is_const_fn = match *fn_ty.kind() { let is_const_fn = match *fn_ty.kind() {
ty::FnDef(def_id, _) => { ty::FnDef(def_id, _) => {
is_const_fn(self.tcx, def_id) self.tcx.is_const_fn_raw(def_id) || is_lang_panic_fn(self.tcx, def_id)
|| is_unstable_const_fn(self.tcx, def_id).is_some()
|| is_lang_panic_fn(self.tcx, def_id)
} }
_ => false, _ => false,
}; };
@ -1081,7 +1078,7 @@ pub fn is_const_fn_in_array_repeat_expression<'tcx>(
if let ty::FnDef(def_id, _) = *literal.ty().kind() { if let ty::FnDef(def_id, _) = *literal.ty().kind() {
if let Some((destination_place, _)) = destination { if let Some((destination_place, _)) = destination {
if destination_place == place { if destination_place == place {
if is_const_fn(ccx.tcx, def_id) { if ccx.tcx.is_const_fn(def_id) {
return true; return true;
} }
} }

View file

@ -120,7 +120,9 @@ pub enum SelectionCandidate<'tcx> {
/// Implementation of a `Fn`-family trait by one of the anonymous /// Implementation of a `Fn`-family trait by one of the anonymous
/// types generated for a fn pointer type (e.g., `fn(int) -> int`) /// types generated for a fn pointer type (e.g., `fn(int) -> int`)
FnPointerCandidate, FnPointerCandidate {
is_const: bool,
},
/// Builtin implementation of `DiscriminantKind`. /// Builtin implementation of `DiscriminantKind`.
DiscriminantKindCandidate, DiscriminantKindCandidate,

View file

@ -2701,6 +2701,29 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn lifetime_scope(self, id: HirId) -> Option<LifetimeScopeForPath> { pub fn lifetime_scope(self, id: HirId) -> Option<LifetimeScopeForPath> {
self.lifetime_scope_map(id.owner).and_then(|mut map| map.remove(&id.local_id)) self.lifetime_scope_map(id.owner).and_then(|mut map| map.remove(&id.local_id))
} }
/// Whether the `def_id` counts as const fn in the current crate, considering all active
/// feature gates
pub fn is_const_fn(self, def_id: DefId) -> bool {
if self.is_const_fn_raw(def_id) {
match self.lookup_const_stability(def_id) {
Some(stability) if stability.level.is_unstable() => {
// has a `rustc_const_unstable` attribute, check whether the user enabled the
// corresponding feature gate.
self.features()
.declared_lib_features
.iter()
.any(|&(sym, _)| sym == stability.feature)
}
// functions without const stability are either stable user written
// const fn or the user is using feature gates and we thus don't
// care what they do
_ => true,
}
} else {
false
}
}
} }
impl TyCtxtAt<'tcx> { impl TyCtxtAt<'tcx> {

View file

@ -476,7 +476,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.. ..
} = self_ty.fn_sig(self.tcx()).skip_binder() } = self_ty.fn_sig(self.tcx()).skip_binder()
{ {
candidates.vec.push(FnPointerCandidate); candidates.vec.push(FnPointerCandidate { is_const: false });
} }
} }
// Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
@ -489,7 +489,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
} = self_ty.fn_sig(self.tcx()).skip_binder() } = self_ty.fn_sig(self.tcx()).skip_binder()
{ {
if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() { if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() {
candidates.vec.push(FnPointerCandidate); candidates
.vec
.push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) });
} }
} }
} }

View file

@ -92,7 +92,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(ImplSource::Generator(vtable_generator)) Ok(ImplSource::Generator(vtable_generator))
} }
FnPointerCandidate => { FnPointerCandidate { .. } => {
let data = self.confirm_fn_pointer_candidate(obligation)?; let data = self.confirm_fn_pointer_candidate(obligation)?;
Ok(ImplSource::FnPointer(data)) Ok(ImplSource::FnPointer(data))
} }

View file

@ -1112,6 +1112,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// generator, this will raise error in other places // generator, this will raise error in other places
// or ignore error with const_async_blocks feature // or ignore error with const_async_blocks feature
GeneratorCandidate => {} GeneratorCandidate => {}
// FnDef where the function is const
FnPointerCandidate { is_const: true } => {}
ConstDropCandidate => {} ConstDropCandidate => {}
_ => { _ => {
// reject all other types of candidates // reject all other types of candidates
@ -1539,6 +1541,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
} }
} }
// Drop otherwise equivalent non-const fn pointer candidates
(FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true,
// Global bounds from the where clause should be ignored // Global bounds from the where clause should be ignored
// here (see issue #50825). Otherwise, we have a where // here (see issue #50825). Otherwise, we have a where
// clause so don't go around looking for impls. // clause so don't go around looking for impls.
@ -1549,7 +1554,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(..) ImplCandidate(..)
| ClosureCandidate | ClosureCandidate
| GeneratorCandidate | GeneratorCandidate
| FnPointerCandidate | FnPointerCandidate { .. }
| BuiltinObjectCandidate | BuiltinObjectCandidate
| BuiltinUnsizeCandidate | BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_) | TraitUpcastingUnsizeCandidate(_)
@ -1567,7 +1572,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(_) ImplCandidate(_)
| ClosureCandidate | ClosureCandidate
| GeneratorCandidate | GeneratorCandidate
| FnPointerCandidate | FnPointerCandidate { .. }
| BuiltinObjectCandidate | BuiltinObjectCandidate
| BuiltinUnsizeCandidate | BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_) | TraitUpcastingUnsizeCandidate(_)
@ -1597,7 +1602,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(..) ImplCandidate(..)
| ClosureCandidate | ClosureCandidate
| GeneratorCandidate | GeneratorCandidate
| FnPointerCandidate | FnPointerCandidate { .. }
| BuiltinObjectCandidate | BuiltinObjectCandidate
| BuiltinUnsizeCandidate | BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_) | TraitUpcastingUnsizeCandidate(_)
@ -1609,7 +1614,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(..) ImplCandidate(..)
| ClosureCandidate | ClosureCandidate
| GeneratorCandidate | GeneratorCandidate
| FnPointerCandidate | FnPointerCandidate { .. }
| BuiltinObjectCandidate | BuiltinObjectCandidate
| BuiltinUnsizeCandidate | BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_) | TraitUpcastingUnsizeCandidate(_)
@ -1690,7 +1695,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(_) ImplCandidate(_)
| ClosureCandidate | ClosureCandidate
| GeneratorCandidate | GeneratorCandidate
| FnPointerCandidate | FnPointerCandidate { .. }
| BuiltinObjectCandidate | BuiltinObjectCandidate
| BuiltinUnsizeCandidate | BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_) | TraitUpcastingUnsizeCandidate(_)
@ -1699,7 +1704,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(_) ImplCandidate(_)
| ClosureCandidate | ClosureCandidate
| GeneratorCandidate | GeneratorCandidate
| FnPointerCandidate | FnPointerCandidate { .. }
| BuiltinObjectCandidate | BuiltinObjectCandidate
| BuiltinUnsizeCandidate | BuiltinUnsizeCandidate
| TraitUpcastingUnsizeCandidate(_) | TraitUpcastingUnsizeCandidate(_)

View file

@ -11,7 +11,7 @@ crate mod utils;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_attr as attr; use rustc_attr as attr;
use rustc_const_eval::const_eval::{is_const_fn, is_unstable_const_fn}; use rustc_const_eval::const_eval::is_unstable_const_fn;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def::{CtorKind, DefKind, Res};
@ -787,7 +787,7 @@ fn clean_fn_or_proc_macro(
let mut func = (sig, generics, body_id).clean(cx); let mut func = (sig, generics, body_id).clean(cx);
let def_id = item.def_id.to_def_id(); let def_id = item.def_id.to_def_id();
func.header.constness = func.header.constness =
if is_const_fn(cx.tcx, def_id) && is_unstable_const_fn(cx.tcx, def_id).is_none() { if cx.tcx.is_const_fn(def_id) && is_unstable_const_fn(cx.tcx, def_id).is_none() {
hir::Constness::Const hir::Constness::Const
} else { } else {
hir::Constness::NotConst hir::Constness::NotConst

View file

@ -0,0 +1,31 @@
// run-pass
#![feature(const_trait_impl)]
#![feature(const_fn_trait_bound)]
const fn answer_p1<F>(f: &F) -> u8
where
F: ~const FnOnce() -> u8,
F: ~const FnMut() -> u8,
F: ~const Fn() -> u8,
{
f() * 7
}
const fn three() -> u8 {
3
}
const fn answer_p2() -> u8 {
answer_p1(&three)
}
const fn answer<F: ~const Fn() -> u8>(f: &F) -> u8 {
f() + f()
}
const ANSWER: u8 = answer(&answer_p2);
fn main() {
assert_eq!(ANSWER, 42)
}

View file

@ -18,7 +18,6 @@
extern crate rustc_ast; extern crate rustc_ast;
extern crate rustc_ast_pretty; extern crate rustc_ast_pretty;
extern crate rustc_attr; extern crate rustc_attr;
extern crate rustc_const_eval;
extern crate rustc_data_structures; extern crate rustc_data_structures;
extern crate rustc_errors; extern crate rustc_errors;
extern crate rustc_hir; extern crate rustc_hir;

View file

@ -364,7 +364,7 @@ fn check_terminator(
} }
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
rustc_const_eval::const_eval::is_const_fn(tcx, def_id) tcx.is_const_fn(def_id)
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
// Checking MSRV is manually necessary because `rustc` has no such concept. This entire // Checking MSRV is manually necessary because `rustc` has no such concept. This entire