Skip to content

Commit

Permalink
walk hir to get const evaluatable predicates
Browse files Browse the repository at this point in the history
  • Loading branch information
lcnr committed Sep 24, 2020
1 parent ac1d0d8 commit f8d3f40
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 73 deletions.
143 changes: 84 additions & 59 deletions compiler/rustc_typeck/src/collect.rs
Expand Up @@ -37,12 +37,11 @@ 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, SubstsRef};
use rustc_middle::ty::subst::InternalSubsts;
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 @@ -51,8 +50,6 @@ 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 @@ -1676,65 +1673,10 @@ 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 !const_evaluatable.is_empty() {
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>,
) -> Vec<(ty::Predicate<'tcx>, Span)> {
#[derive(Default)]
struct ConstCollector<'tcx> {
ct: SmallVec<[(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>, Span); 4]>,
curr_span: Span,
}

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, self.curr_span));
}
false
}
}

let mut collector = ConstCollector::default();
for &(pred, span) in predicates.predicates.iter() {
collector.curr_span = span;
pred.visit_with(&mut collector);
}

match tcx.def_kind(def_id) {
DefKind::Fn | DefKind::AssocFn => {
tcx.fn_sig(def_id).visit_with(&mut collector);
}
_ => (),
}
debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.ct);

// We only want unique const evaluatable predicates.
collector.ct.sort();
collector.ct.dedup();
collector
.ct
.into_iter()
.map(move |(def_id, subst, span)| {
(ty::PredicateAtom::ConstEvaluatable(def_id, subst).to_predicate(tcx), span)
})
.collect()
}

/// 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 Expand Up @@ -2061,6 +2003,10 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
}))
}

if tcx.features().const_evaluatable_checked {
predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local()));
}

let mut predicates: Vec<_> = predicates.into_iter().collect();

// Subtle: before we store the predicates into the tcx, we
Expand All @@ -2087,6 +2033,85 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
result
}

fn const_evaluatable_predicates_of<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> {
struct ConstCollector<'tcx> {
tcx: TyCtxt<'tcx>,
preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
}

impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> {
type Map = Map<'tcx>;

fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
intravisit::NestedVisitorMap::None
}

fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
let def_id = self.tcx.hir().local_def_id(c.hir_id);
let ct = ty::Const::from_anon_const(self.tcx, def_id);
if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
let span = self.tcx.hir().span(c.hir_id);
self.preds.insert((
ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx),
span,
));
}
}

// Look into `TyAlias`.
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
use ty::fold::{TypeFoldable, TypeVisitor};
struct TyAliasVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
preds: &'a mut FxIndexSet<(ty::Predicate<'tcx>, Span)>,
span: Span,
}

impl<'a, 'tcx> TypeVisitor<'tcx> for TyAliasVisitor<'a, 'tcx> {
fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool {
if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
self.preds.insert((
ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx),
self.span,
));
}
false
}
}

if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind {
if let Res::Def(DefKind::TyAlias, def_id) = path.res {
let mut visitor =
TyAliasVisitor { tcx: self.tcx, preds: &mut self.preds, span: path.span };
self.tcx.type_of(def_id).visit_with(&mut visitor);
}
}

intravisit::walk_ty(self, ty)
}
}

let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let node = tcx.hir().get(hir_id);

let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() };
if let Some(generics) = node.generics() {
warn!("const_evaluatable_predicates_of({:?}): visit_generics", def_id);
collector.visit_generics(generics);
}

if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) {
warn!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id);
collector.visit_fn_decl(fn_sig.decl);
}
warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds);

collector.preds
}

fn projection_ty_from_predicates(
tcx: TyCtxt<'tcx>,
key: (
Expand Down
Expand Up @@ -4,10 +4,10 @@ error: constant expression depends on a generic parameter
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:41
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10
|
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
| ----- required by this bound in `test1`
| ---------------------------- required by this bound in `test1`
|
= note: this may fail depending on what value the parameter takes

Expand All @@ -17,10 +17,10 @@ error: constant expression depends on a generic parameter
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:41
::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27
|
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
| ----- required by this bound in `test1`
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
| ---------------------------- required by this bound in `test1`
|
= note: this may fail depending on what value the parameter takes

Expand All @@ -30,10 +30,10 @@ error: constant expression depends on a generic parameter
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:41
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10
|
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
| ----- required by this bound in `test1::{{constant}}#1`
| ---------------------------- required by this bound in `test1::{{constant}}#1`
|
= note: this may fail depending on what value the parameter takes

Expand All @@ -43,10 +43,10 @@ error: constant expression depends on a generic parameter
LL | let _ = const_evaluatable_lib::test1::<T>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:41
::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27
|
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
| ----- required by this bound in `test1::{{constant}}#1`
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
| ---------------------------- required by this bound in `test1::{{constant}}#1`
|
= note: this may fail depending on what value the parameter takes

Expand Down
@@ -1,10 +1,8 @@
error: overly complex generic constant
error: constant expression depends on a generic parameter
--> $DIR/let-bindings.rs:6:68
|
LL | fn test<const N: usize>() -> [u8; { let x = N; N + 1 }] where [u8; { let x = N; N + 1 }]: Default {
| ^^^^^^-^^^^^^^^^^^^^
| |
| unsupported statement
| ^^^^^^^^^^^^^^^^^^^^ required by this bound in `test::{{constant}}#0`
|
= help: consider moving this anonymous constant into a `const` function

Expand Down

0 comments on commit f8d3f40

Please sign in to comment.