From 8667f93040cf539c469e3a64b9ed82c5f13bf938 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 10 Sep 2020 08:52:02 +0200 Subject: [PATCH 1/2] implement `const_evaluatable_checked` feature MVP --- compiler/rustc_feature/src/active.rs | 6 ++- compiler/rustc_span/src/symbol.rs | 1 + .../src/traits/const_evaluatable.rs | 51 +++++++++++++------ compiler/rustc_typeck/src/collect.rs | 46 ++++++++++++++++- .../const_evaluatable_checked/simple.rs | 14 +++++ .../const_evaluatable_checked/simple_fail.rs | 12 +++++ .../simple_fail.stderr | 9 ++++ 7 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/simple.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/simple_fail.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 3d7b3da45ccb9..d7adf1cdb6d24 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -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(0), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -600,6 +603,7 @@ 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, @@ -607,6 +611,6 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ ]; /// 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)]; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5092b945f72c4..407663e57577a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -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, diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 013cd71ea305d..74f7b1b352caf 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -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( @@ -38,24 +56,27 @@ 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(), } } + if concrete.is_ok() { + debug!("is_const_evaluatable: concrete ~~> ok"); + } else { + debug!("is_const_evaluatable: concrete ~~> err"); + } concrete.map(drop) } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index ea59375bad736..0bba8b821a70c 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -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; @@ -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); @@ -1672,10 +1675,51 @@ 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); + if result.predicates.is_empty() { + result.predicates = tcx.arena.alloc_from_iter(const_evaluatable); + } else { + 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, Span)> { + #[derive(Default)] + struct ConstCollector<'tcx> { + ct: SmallVec<[(ty::WithOptConstParam, 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. diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple.rs b/src/test/ui/const-generics/const_evaluatable_checked/simple.rs new file mode 100644 index 0000000000000..a7ead78b97bae --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple.rs @@ -0,0 +1,14 @@ +// run-pass +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +type Arr = [u8; N - 1]; + +fn test() -> Arr where Arr: Default { + Default::default() +} + +fn main() { + let x = test::<33>(); + assert_eq!(x, [0; 32]); +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs new file mode 100644 index 0000000000000..1edf1885dd281 --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.rs @@ -0,0 +1,12 @@ +#![feature(const_generics, const_evaluatable_checked)] +#![allow(incomplete_features)] + +type Arr = [u8; N - 1]; //~ ERROR evaluation of constant + +fn test() -> Arr where Arr: Sized { + todo!() +} + +fn main() { + test::<0>(); +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.stderr b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.stderr new file mode 100644 index 0000000000000..1ac5e1d95537a --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/simple_fail.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/simple_fail.rs:4:33 + | +LL | type Arr = [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`. From 300b0acb85e41a19b518715147968f177679ebc1 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Thu, 10 Sep 2020 09:48:02 +0200 Subject: [PATCH 2/2] fix tidy, small cleanup --- compiler/rustc_feature/src/active.rs | 2 +- .../src/traits/const_evaluatable.rs | 6 +----- compiler/rustc_typeck/src/collect.rs | 9 ++------- .../feature-gate-const_evaluatable_checked.rs | 14 ++++++++++++++ .../feature-gate-const_evaluatable_checked.stderr | 10 ++++++++++ 5 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index d7adf1cdb6d24..1aeb0bd5ad9aa 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -586,7 +586,7 @@ declare_features! ( (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(0), None), + (active, const_evaluatable_checked, "1.48.0", Some(76560), None), // ------------------------------------------------------------------------- // feature-group-end: actual feature gates diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 74f7b1b352caf..fdb87c085b54e 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -73,10 +73,6 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } } - if concrete.is_ok() { - debug!("is_const_evaluatable: concrete ~~> ok"); - } else { - debug!("is_const_evaluatable: concrete ~~> err"); - } + debug!(?concrete, "is_const_evaluatable"); concrete.map(drop) } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 0bba8b821a70c..7d6b3df03b064 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1678,13 +1678,8 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate if tcx.features().const_evaluatable_checked { let const_evaluatable = const_evaluatable_predicates_of(tcx, def_id, &result); - if result.predicates.is_empty() { - result.predicates = tcx.arena.alloc_from_iter(const_evaluatable); - } else { - result.predicates = tcx - .arena - .alloc_from_iter(result.predicates.iter().copied().chain(const_evaluatable)); - } + result.predicates = + tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(const_evaluatable)); } debug!("predicates_defined_on({:?}) = {:?}", def_id, result); diff --git a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs new file mode 100644 index 0000000000000..941bd5e9e5d0a --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.rs @@ -0,0 +1,14 @@ +#![feature(const_generics)] +#![allow(incomplete_features)] + +type Arr = [u8; N - 1]; + +fn test() -> Arr where Arr: Default { + //~^ ERROR constant expression depends + Default::default() +} + +fn main() { + let x = test::<33>(); + assert_eq!(x, [0; 32]); +} diff --git a/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.stderr b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.stderr new file mode 100644 index 0000000000000..6e4a22a38b17c --- /dev/null +++ b/src/test/ui/const-generics/const_evaluatable_checked/feature-gate-const_evaluatable_checked.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/feature-gate-const_evaluatable_checked.rs:6:30 + | +LL | fn test() -> Arr where Arr: Default { + | ^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error +