Skip to content

Commit 3a4da87

Browse files
committed
Auto merge of #77049 - lcnr:const-eval-function-signature, r=oli-obk
const_evaluatable_checked: extend predicate collection We now walk the hir instead of using `ty` so that we get better spans here, While I am still not completely sure if that's what we want in the end, it does seem a lot closer to the final goal than the previous version. We also look into type aliases (and use a `TypeVisitor` here), about which I am not completely sure, but we will see how well this works. We also look into fn decls, so the following should work now. ```rust fn test<T>() -> [u8; std::mem::size_of::<T>()] { [0; std::mem::size_of::<T>()] } ``` Additionally, we visit the optional trait and self type of impls. r? `@oli-obk`
2 parents 86b4172 + 21edd10 commit 3a4da87

File tree

9 files changed

+206
-81
lines changed

9 files changed

+206
-81
lines changed

compiler/rustc_trait_selection/src/traits/wf.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ use rustc_hir::lang_items::LangItem;
77
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
88
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
99
use rustc_span::Span;
10-
use std::rc::Rc;
1110

11+
use std::iter;
12+
use std::rc::Rc;
1213
/// Returns the set of obligations needed to make `arg` well-formed.
1314
/// If `arg` contains unresolved inference variables, this may include
1415
/// further WF obligations. However, if `arg` IS an unresolved
@@ -616,13 +617,24 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
616617
def_id: DefId,
617618
substs: SubstsRef<'tcx>,
618619
) -> Vec<traits::PredicateObligation<'tcx>> {
619-
let predicates = self.infcx.tcx.predicates_of(def_id).instantiate(self.infcx.tcx, substs);
620+
let predicates = self.infcx.tcx.predicates_of(def_id);
621+
let mut origins = vec![def_id; predicates.predicates.len()];
622+
let mut head = predicates;
623+
while let Some(parent) = head.parent {
624+
head = self.infcx.tcx.predicates_of(parent);
625+
origins.extend(iter::repeat(parent).take(head.predicates.len()));
626+
}
627+
628+
let predicates = predicates.instantiate(self.infcx.tcx, substs);
629+
debug_assert_eq!(predicates.predicates.len(), origins.len());
630+
620631
predicates
621632
.predicates
622633
.into_iter()
623634
.zip(predicates.spans.into_iter())
624-
.map(|(pred, span)| {
625-
let cause = self.cause(traits::BindingObligation(def_id, span));
635+
.zip(origins.into_iter().rev())
636+
.map(|((pred, span), origin_def_id)| {
637+
let cause = self.cause(traits::BindingObligation(origin_def_id, span));
626638
traits::Obligation::new(cause, self.param_env, pred)
627639
})
628640
.filter(|pred| !pred.has_escaping_bound_vars())

compiler/rustc_typeck/src/collect.rs

+106-71
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,11 @@ use rustc_middle::hir::map::Map;
3737
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
3838
use rustc_middle::mir::mono::Linkage;
3939
use rustc_middle::ty::query::Providers;
40-
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
40+
use rustc_middle::ty::subst::InternalSubsts;
4141
use rustc_middle::ty::util::Discr;
4242
use rustc_middle::ty::util::IntTypeExt;
4343
use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt};
4444
use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness};
45-
use rustc_middle::ty::{TypeFoldable, TypeVisitor};
4645
use rustc_session::config::SanitizerSet;
4746
use rustc_session::lint;
4847
use rustc_session::parse::feature_err;
@@ -51,8 +50,6 @@ use rustc_span::{Span, DUMMY_SP};
5150
use rustc_target::spec::abi;
5251
use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
5352

54-
use smallvec::SmallVec;
55-
5653
mod type_of;
5754

5855
struct OnlySelfBounds(bool);
@@ -1676,47 +1673,10 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate
16761673
}
16771674
}
16781675

1679-
if tcx.features().const_evaluatable_checked {
1680-
let const_evaluatable = const_evaluatable_predicates_of(tcx, def_id, &result);
1681-
result.predicates =
1682-
tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(const_evaluatable));
1683-
}
1684-
16851676
debug!("predicates_defined_on({:?}) = {:?}", def_id, result);
16861677
result
16871678
}
16881679

1689-
pub fn const_evaluatable_predicates_of<'tcx>(
1690-
tcx: TyCtxt<'tcx>,
1691-
def_id: DefId,
1692-
predicates: &ty::GenericPredicates<'tcx>,
1693-
) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> {
1694-
#[derive(Default)]
1695-
struct ConstCollector<'tcx> {
1696-
ct: SmallVec<[(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>, Span); 4]>,
1697-
curr_span: Span,
1698-
}
1699-
1700-
impl<'tcx> TypeVisitor<'tcx> for ConstCollector<'tcx> {
1701-
fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool {
1702-
if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
1703-
self.ct.push((def, substs, self.curr_span));
1704-
}
1705-
false
1706-
}
1707-
}
1708-
1709-
let mut collector = ConstCollector::default();
1710-
for &(pred, span) in predicates.predicates.iter() {
1711-
collector.curr_span = span;
1712-
pred.visit_with(&mut collector);
1713-
}
1714-
warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.ct);
1715-
collector.ct.into_iter().map(move |(def_id, subst, span)| {
1716-
(ty::PredicateAtom::ConstEvaluatable(def_id, subst).to_predicate(tcx), span)
1717-
})
1718-
}
1719-
17201680
/// Returns a list of all type predicates (explicit and implicit) for the definition with
17211681
/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
17221682
/// `Self: Trait` predicates for traits.
@@ -1754,29 +1714,6 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
17541714

17551715
debug!("explicit_predicates_of(def_id={:?})", def_id);
17561716

1757-
/// A data structure with unique elements, which preserves order of insertion.
1758-
/// Preserving the order of insertion is important here so as not to break
1759-
/// compile-fail UI tests.
1760-
struct UniquePredicates<'tcx> {
1761-
predicates: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
1762-
}
1763-
1764-
impl<'tcx> UniquePredicates<'tcx> {
1765-
fn new() -> Self {
1766-
UniquePredicates { predicates: FxIndexSet::default() }
1767-
}
1768-
1769-
fn push(&mut self, value: (ty::Predicate<'tcx>, Span)) {
1770-
self.predicates.insert(value);
1771-
}
1772-
1773-
fn extend<I: IntoIterator<Item = (ty::Predicate<'tcx>, Span)>>(&mut self, iter: I) {
1774-
for value in iter {
1775-
self.push(value);
1776-
}
1777-
}
1778-
}
1779-
17801717
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
17811718
let node = tcx.hir().get(hir_id);
17821719

@@ -1789,7 +1726,10 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
17891726

17901727
const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty();
17911728

1792-
let mut predicates = UniquePredicates::new();
1729+
// We use an `IndexSet` to preserves order of insertion.
1730+
// Preserving the order of insertion is important here so as not to break
1731+
// compile-fail UI tests.
1732+
let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default();
17931733

17941734
let ast_generics = match node {
17951735
Node::TraitItem(item) => {
@@ -1891,7 +1831,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
18911831
// (see below). Recall that a default impl is not itself an impl, but rather a
18921832
// set of defaults that can be incorporated into another impl.
18931833
if let Some(trait_ref) = is_default_impl_trait {
1894-
predicates.push((
1834+
predicates.insert((
18951835
trait_ref.to_poly_trait_ref().without_const().to_predicate(tcx),
18961836
tcx.def_span(def_id),
18971837
));
@@ -1915,7 +1855,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
19151855
hir::GenericBound::Outlives(lt) => {
19161856
let bound = AstConv::ast_region_to_region(&icx, &lt, None);
19171857
let outlives = ty::Binder::bind(ty::OutlivesPredicate(region, bound));
1918-
predicates.push((outlives.to_predicate(tcx), lt.span));
1858+
predicates.insert((outlives.to_predicate(tcx), lt.span));
19191859
}
19201860
_ => bug!(),
19211861
});
@@ -1970,7 +1910,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
19701910
let span = bound_pred.bounded_ty.span;
19711911
let re_root_empty = tcx.lifetimes.re_root_empty;
19721912
let predicate = ty::OutlivesPredicate(ty, re_root_empty);
1973-
predicates.push((
1913+
predicates.insert((
19741914
ty::PredicateAtom::TypeOutlives(predicate)
19751915
.potentially_quantified(tcx, ty::PredicateKind::ForAll),
19761916
span,
@@ -2014,11 +1954,11 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
20141954

20151955
&hir::GenericBound::Outlives(ref lifetime) => {
20161956
let region = AstConv::ast_region_to_region(&icx, lifetime, None);
2017-
predicates.push((
1957+
predicates.insert((
20181958
ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, region))
20191959
.potentially_quantified(tcx, ty::PredicateKind::ForAll),
20201960
lifetime.span,
2021-
))
1961+
));
20221962
}
20231963
}
20241964
}
@@ -2063,7 +2003,11 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
20632003
}))
20642004
}
20652005

2066-
let mut predicates: Vec<_> = predicates.predicates.into_iter().collect();
2006+
if tcx.features().const_evaluatable_checked {
2007+
predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local()));
2008+
}
2009+
2010+
let mut predicates: Vec<_> = predicates.into_iter().collect();
20672011

20682012
// Subtle: before we store the predicates into the tcx, we
20692013
// sort them so that predicates like `T: Foo<Item=U>` come
@@ -2089,6 +2033,97 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
20892033
result
20902034
}
20912035

2036+
fn const_evaluatable_predicates_of<'tcx>(
2037+
tcx: TyCtxt<'tcx>,
2038+
def_id: LocalDefId,
2039+
) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> {
2040+
struct ConstCollector<'tcx> {
2041+
tcx: TyCtxt<'tcx>,
2042+
preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
2043+
}
2044+
2045+
impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> {
2046+
type Map = Map<'tcx>;
2047+
2048+
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
2049+
intravisit::NestedVisitorMap::None
2050+
}
2051+
2052+
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
2053+
let def_id = self.tcx.hir().local_def_id(c.hir_id);
2054+
let ct = ty::Const::from_anon_const(self.tcx, def_id);
2055+
if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
2056+
let span = self.tcx.hir().span(c.hir_id);
2057+
self.preds.insert((
2058+
ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx),
2059+
span,
2060+
));
2061+
}
2062+
}
2063+
2064+
// Look into `TyAlias`.
2065+
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
2066+
use ty::fold::{TypeFoldable, TypeVisitor};
2067+
struct TyAliasVisitor<'a, 'tcx> {
2068+
tcx: TyCtxt<'tcx>,
2069+
preds: &'a mut FxIndexSet<(ty::Predicate<'tcx>, Span)>,
2070+
span: Span,
2071+
}
2072+
2073+
impl<'a, 'tcx> TypeVisitor<'tcx> for TyAliasVisitor<'a, 'tcx> {
2074+
fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool {
2075+
if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val {
2076+
self.preds.insert((
2077+
ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx),
2078+
self.span,
2079+
));
2080+
}
2081+
false
2082+
}
2083+
}
2084+
2085+
if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind {
2086+
if let Res::Def(DefKind::TyAlias, def_id) = path.res {
2087+
let mut visitor =
2088+
TyAliasVisitor { tcx: self.tcx, preds: &mut self.preds, span: path.span };
2089+
self.tcx.type_of(def_id).visit_with(&mut visitor);
2090+
}
2091+
}
2092+
2093+
intravisit::walk_ty(self, ty)
2094+
}
2095+
}
2096+
2097+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
2098+
let node = tcx.hir().get(hir_id);
2099+
2100+
let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() };
2101+
if let hir::Node::Item(item) = node {
2102+
if let hir::ItemKind::Impl { ref of_trait, ref self_ty, .. } = item.kind {
2103+
if let Some(of_trait) = of_trait {
2104+
warn!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id);
2105+
collector.visit_trait_ref(of_trait);
2106+
}
2107+
2108+
warn!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id);
2109+
collector.visit_ty(self_ty);
2110+
}
2111+
}
2112+
2113+
if let Some(generics) = node.generics() {
2114+
warn!("const_evaluatable_predicates_of({:?}): visit_generics", def_id);
2115+
collector.visit_generics(generics);
2116+
}
2117+
2118+
if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) {
2119+
warn!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id);
2120+
collector.visit_fn_decl(fn_sig.decl);
2121+
}
2122+
warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds);
2123+
2124+
collector.preds
2125+
}
2126+
20922127
fn projection_ty_from_predicates(
20932128
tcx: TyCtxt<'tcx>,
20942129
key: (

src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ fn user<T>() {
88
//~^ ERROR constant expression depends
99
//~| ERROR constant expression depends
1010
//~| ERROR constant expression depends
11+
//~| ERROR constant expression depends
1112
}
1213

1314
fn main() {}

src/test/ui/const-generics/const_evaluatable_checked/cross_crate_predicate.stderr

+23-5
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,23 @@ error: constant expression depends on a generic parameter
44
LL | let _ = const_evaluatable_lib::test1::<T>();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7-
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:41
7+
::: $DIR/auxiliary/const_evaluatable_lib.rs:6:10
88
|
99
LL | [u8; std::mem::size_of::<T>() - 1]: Sized,
10-
| ----- required by this bound in `test1`
10+
| ---------------------------- required by this bound in `test1`
11+
|
12+
= note: this may fail depending on what value the parameter takes
13+
14+
error: constant expression depends on a generic parameter
15+
--> $DIR/cross_crate_predicate.rs:7:13
16+
|
17+
LL | let _ = const_evaluatable_lib::test1::<T>();
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
|
20+
::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27
21+
|
22+
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
23+
| ---------------------------- required by this bound in `test1`
1124
|
1225
= note: this may fail depending on what value the parameter takes
1326

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

@@ -29,8 +42,13 @@ error: constant expression depends on a generic parameter
2942
|
3043
LL | let _ = const_evaluatable_lib::test1::<T>();
3144
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
45+
|
46+
::: $DIR/auxiliary/const_evaluatable_lib.rs:4:27
47+
|
48+
LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
49+
| ---------------------------- required by this bound in `test1`
3250
|
3351
= note: this may fail depending on what value the parameter takes
3452

35-
error: aborting due to 3 previous errors
53+
error: aborting due to 4 previous errors
3654

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(const_generics, const_evaluatable_checked)]
2+
#![allow(incomplete_features)]
3+
4+
fn test<const N: usize>() -> [u8; N - 1] {
5+
//~^ ERROR evaluation of constant
6+
todo!()
7+
}
8+
9+
fn main() {
10+
test::<0>();
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/from-sig-fail.rs:4:35
3+
|
4+
LL | fn test<const N: usize>() -> [u8; N - 1] {
5+
| ^^^^^ attempt to compute `0_usize - 1_usize` which would overflow
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)