Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add the const_evaluatable_checked feature #76559

Merged
merged 2 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,9 @@ declare_features! (
/// Allows `if let` guard in match arms.
(active, if_let_guard, "1.47.0", Some(51114), None),

/// Allows non trivial generic constants which have to be manually propageted upwards.
(active, const_evaluatable_checked, "1.48.0", Some(76560), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand All @@ -600,13 +603,14 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
sym::const_generics,
sym::let_chains,
sym::raw_dylib,
sym::const_evaluatable_checked,
sym::const_trait_impl,
sym::const_trait_bound_opt_out,
sym::lazy_normalization_consts,
sym::specialization,
];

/// Some features are not allowed to be used together at the same time, if
/// the two are present, produce an error
/// the two are present, produce an error.
pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] =
&[(sym::const_generics, sym::min_const_generics)];
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ symbols! {
const_compare_raw_pointers,
const_constructor,
const_eval_limit,
const_evaluatable_checked,
const_extern_fn,
const_fn,
const_fn_transmute,
Expand Down
47 changes: 32 additions & 15 deletions compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
param_env: ty::ParamEnv<'tcx>,
span: Span,
) -> Result<(), ErrorHandled> {
debug!("is_const_evaluatable({:?}, {:?})", def, substs);
if infcx.tcx.features().const_evaluatable_checked {
// FIXME(const_evaluatable_checked): Actually look into generic constants to
// implement const equality.
for pred in param_env.caller_bounds() {
match pred.skip_binders() {
ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => {
debug!("is_const_evaluatable: caller_bound={:?}, {:?}", b_def, b_substs);
if b_def == def && b_substs == substs {
debug!("is_const_evaluatable: caller_bound ~~> ok");
return Ok(());
}
}
_ => {} // don't care
}
}
}

let future_compat_lint = || {
if let Some(local_def_id) = def.did.as_local() {
infcx.tcx.struct_span_lint_hir(
Expand All @@ -38,24 +56,23 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
// See #74595 for more details about this.
let concrete = infcx.const_eval_resolve(param_env, def, substs, None, Some(span));

let def_kind = infcx.tcx.def_kind(def.did);
match def_kind {
DefKind::AnonConst => {
let mir_body = if let Some(def) = def.as_const_arg() {
infcx.tcx.optimized_mir_of_const_arg(def)
} else {
infcx.tcx.optimized_mir(def.did)
};
if mir_body.is_polymorphic && concrete.is_ok() {
future_compat_lint();
}
}
_ => {
if substs.has_param_types_or_consts() && concrete.is_ok() {
future_compat_lint();
if concrete.is_ok() && substs.has_param_types_or_consts() {
match infcx.tcx.def_kind(def.did) {
DefKind::AnonConst => {
let mir_body = if let Some(def) = def.as_const_arg() {
infcx.tcx.optimized_mir_of_const_arg(def)
} else {
infcx.tcx.optimized_mir(def.did)
};

if mir_body.is_polymorphic {
future_compat_lint();
}
}
_ => future_compat_lint(),
}
}

debug!(?concrete, "is_const_evaluatable");
concrete.map(drop)
}
41 changes: 40 additions & 1 deletion compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ use rustc_middle::hir::map::Map;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::mono::Linkage;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
use rustc_middle::ty::util::Discr;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt};
use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness};
use rustc_middle::ty::{TypeFoldable, TypeVisitor};
use rustc_session::config::SanitizerSet;
use rustc_session::lint;
use rustc_session::parse::feature_err;
Expand All @@ -50,6 +51,8 @@ use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi;
use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;

use smallvec::SmallVec;

mod type_of;

struct OnlySelfBounds(bool);
Expand Down Expand Up @@ -1672,10 +1675,46 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate
.alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied());
}
}

if tcx.features().const_evaluatable_checked {
let const_evaluatable = const_evaluatable_predicates_of(tcx, def_id, &result);
result.predicates =
tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(const_evaluatable));
}

debug!("predicates_defined_on({:?}) = {:?}", def_id, result);
result
}

pub fn const_evaluatable_predicates_of<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: DefId,
predicates: &ty::GenericPredicates<'tcx>,
) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> {
#[derive(Default)]
struct ConstCollector<'tcx> {
ct: SmallVec<[(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>); 4]>,
}

impl<'tcx> TypeVisitor<'tcx> for ConstCollector<'tcx> {
fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool {
if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
self.ct.push((def, substs));
}
false
}
}

let mut collector = ConstCollector::default();
for (pred, _span) in predicates.predicates.iter() {
pred.visit_with(&mut collector);
}
warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.ct);
collector.ct.into_iter().map(move |(def_id, subst)| {
(ty::PredicateAtom::ConstEvaluatable(def_id, subst).to_predicate(tcx), DUMMY_SP)
})
}

/// Returns a list of all type predicates (explicit and implicit) for the definition with
/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
/// `Self: Trait` predicates for traits.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![feature(const_generics)]
#![allow(incomplete_features)]

type Arr<const N: usize> = [u8; N - 1];

fn test<const N: usize>() -> Arr<N> where Arr<N>: Default {
//~^ ERROR constant expression depends
Default::default()
}

fn main() {
let x = test::<33>();
assert_eq!(x, [0; 32]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: constant expression depends on a generic parameter
--> $DIR/feature-gate-const_evaluatable_checked.rs:6:30
|
LL | fn test<const N: usize>() -> Arr<N> where Arr<N>: Default {
| ^^^^^^
|
= note: this may fail depending on what value the parameter takes

error: aborting due to previous error

14 changes: 14 additions & 0 deletions src/test/ui/const-generics/const_evaluatable_checked/simple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// run-pass
#![feature(const_generics, const_evaluatable_checked)]
#![allow(incomplete_features)]

type Arr<const N: usize> = [u8; N - 1];

fn test<const N: usize>() -> Arr<N> where Arr<N>: Default {
Default::default()
}

fn main() {
let x = test::<33>();
assert_eq!(x, [0; 32]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(const_generics, const_evaluatable_checked)]
#![allow(incomplete_features)]

type Arr<const N: usize> = [u8; N - 1]; //~ ERROR evaluation of constant

fn test<const N: usize>() -> Arr<N> where Arr<N>: Sized {
todo!()
}

fn main() {
test::<0>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0080]: evaluation of constant value failed
--> $DIR/simple_fail.rs:4:33
|
LL | type Arr<const N: usize> = [u8; N - 1];
| ^^^^^ attempt to compute `0_usize - 1_usize` which would overflow

error: aborting due to previous error

For more information about this error, try `rustc --explain E0080`.