attempt to re-add ty::Unevaluated visitor and friends

This commit is contained in:
Ellen 2022-01-12 23:29:10 +00:00
parent 71bbb603f4
commit dec8ed438c
21 changed files with 174 additions and 121 deletions

View file

@ -19,7 +19,7 @@ use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
use rustc_session::lint;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use std::cmp;
@ -29,26 +29,20 @@ use std::ops::ControlFlow;
/// Check if a given constant can be evaluated.
pub fn is_const_evaluatable<'cx, 'tcx>(
infcx: &InferCtxt<'cx, 'tcx>,
def: ty::WithOptConstParam<DefId>,
substs: SubstsRef<'tcx>,
uv: ty::Unevaluated<'tcx, ()>,
param_env: ty::ParamEnv<'tcx>,
span: Span,
) -> Result<(), NotConstEvaluatable> {
debug!("is_const_evaluatable({:?}, {:?})", def, substs);
debug!("is_const_evaluatable({:?})", uv);
if infcx.tcx.features().generic_const_exprs {
let tcx = infcx.tcx;
match AbstractConst::new(tcx, def, substs)? {
match AbstractConst::new(tcx, uv)? {
// We are looking at a generic abstract constant.
Some(ct) => {
for pred in param_env.caller_bounds() {
match pred.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(b_def, b_substs) => {
if b_def == def && b_substs == substs {
debug!("is_const_evaluatable: caller_bound ~~> ok");
return Ok(());
}
if let Some(b_ct) = AbstractConst::new(tcx, b_def, b_substs)? {
ty::PredicateKind::ConstEvaluatable(uv) => {
if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
// Try to unify with each subtree in the AbstractConst to allow for
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
// predicate for `(N + 1) * 2`
@ -132,7 +126,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
}
let future_compat_lint = || {
if let Some(local_def_id) = def.did.as_local() {
if let Some(local_def_id) = uv.def.did.as_local() {
infcx.tcx.struct_span_lint_hir(
lint::builtin::CONST_EVALUATABLE_UNCHECKED,
infcx.tcx.hir().local_def_id_to_hir_id(local_def_id),
@ -153,16 +147,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
// and hopefully soon change this to an error.
//
// See #74595 for more details about this.
let concrete = infcx.const_eval_resolve(
param_env,
ty::Unevaluated { def, substs, promoted: None },
Some(span),
);
let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
if concrete.is_ok() && substs.has_param_types_or_consts() {
match infcx.tcx.def_kind(def.did) {
if concrete.is_ok() && uv.substs.has_param_types_or_consts() {
match infcx.tcx.def_kind(uv.def.did) {
DefKind::AnonConst | DefKind::InlineConst => {
let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(def);
let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def);
if mir_body.is_polymorphic {
future_compat_lint();
@ -174,7 +164,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
debug!(?concrete, "is_const_evaluatable");
match concrete {
Err(ErrorHandled::TooGeneric) => Err(match substs.has_infer_types_or_consts() {
Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
true => NotConstEvaluatable::MentionsInfer,
false => NotConstEvaluatable::MentionsParam,
}),
@ -202,12 +192,11 @@ pub struct AbstractConst<'tcx> {
impl<'tcx> AbstractConst<'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<DefId>,
substs: SubstsRef<'tcx>,
uv: ty::Unevaluated<'tcx, ()>,
) -> Result<Option<AbstractConst<'tcx>>, ErrorReported> {
let inner = tcx.thir_abstract_const_opt_const_arg(def)?;
debug!("AbstractConst::new({:?}, {:?}) = {:?}", def, substs, inner);
Ok(inner.map(|inner| AbstractConst { inner, substs }))
let inner = tcx.thir_abstract_const_opt_const_arg(uv.def)?;
debug!("AbstractConst::new({:?}) = {:?}", uv, inner);
Ok(inner.map(|inner| AbstractConst { inner, substs: uv.substs }))
}
pub fn from_const(
@ -215,9 +204,7 @@ impl<'tcx> AbstractConst<'tcx> {
ct: &ty::Const<'tcx>,
) -> Result<Option<AbstractConst<'tcx>>, ErrorReported> {
match ct.val {
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: _ }) => {
AbstractConst::new(tcx, def, substs)
}
ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()),
ty::ConstKind::Error(_) => Err(ErrorReported),
_ => Ok(None),
}
@ -539,14 +526,11 @@ pub(super) fn thir_abstract_const<'tcx>(
pub(super) fn try_unify_abstract_consts<'tcx>(
tcx: TyCtxt<'tcx>,
((a, a_substs), (b, b_substs)): (
(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
(ty::WithOptConstParam<DefId>, SubstsRef<'tcx>),
),
(a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
) -> bool {
(|| {
if let Some(a) = AbstractConst::new(tcx, a, a_substs)? {
if let Some(b) = AbstractConst::new(tcx, b, b_substs)? {
if let Some(a) = AbstractConst::new(tcx, a)? {
if let Some(b) = AbstractConst::new(tcx, b)? {
return Ok(try_unify(tcx, a, b));
}
}

View file

@ -819,10 +819,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}
match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::ConstEvaluatable(def, _) => {
ty::PredicateKind::ConstEvaluatable(uv) => {
let mut err =
self.tcx.sess.struct_span_err(span, "unconstrained generic constant");
let const_span = self.tcx.def_span(def.did);
let const_span = self.tcx.def_span(uv.def.did);
match self.tcx.sess.source_map().span_to_snippet(const_span) {
Ok(snippet) => err.help(&format!(
"try adding a `where` bound using this expression: `where [(); {}]:`",

View file

@ -527,11 +527,10 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
}
}
ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
ty::PredicateKind::ConstEvaluatable(uv) => {
match const_evaluatable::is_const_evaluatable(
self.selcx.infcx(),
def_id,
substs,
uv,
obligation.param_env,
obligation.cause.span,
) {
@ -539,7 +538,9 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
Err(NotConstEvaluatable::MentionsInfer) => {
pending_obligation.stalled_on.clear();
pending_obligation.stalled_on.extend(
substs.iter().filter_map(TyOrConstInferVar::maybe_from_generic_arg),
uv.substs
.iter()
.filter_map(TyOrConstInferVar::maybe_from_generic_arg),
);
ProcessResult::Unchanged
}
@ -563,7 +564,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
(c1.val, c2.val)
{
if infcx.try_unify_abstract_consts(a, b) {
if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
return ProcessResult::Changed(vec![]);
}
}

View file

@ -845,12 +845,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>(
}
fn visit_predicate(&mut self, pred: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::PredicateKind::ConstEvaluatable(def, substs) = pred.kind().skip_binder() {
if let ty::PredicateKind::ConstEvaluatable(uv) = pred.kind().skip_binder() {
// FIXME(generic_const_exprs): We should probably deduplicate the logic for
// `AbstractConst`s here, it might make sense to change `ConstEvaluatable` to
// take a `ty::Const` instead.
use rustc_middle::thir::abstract_const::Node;
if let Ok(Some(ct)) = AbstractConst::new(self.tcx, def, substs) {
if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv) {
const_evaluatable::walk_abstract_const(self.tcx, ct, |node| {
match node.root(self.tcx) {
Node::Leaf(leaf) => self.visit_const(leaf),

View file

@ -619,11 +619,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
ty::PredicateKind::ConstEvaluatable(def_id, substs) => {
ty::PredicateKind::ConstEvaluatable(uv) => {
match const_evaluatable::is_const_evaluatable(
self.infcx,
def_id,
substs,
uv,
obligation.param_env,
obligation.cause.span,
) {
@ -645,7 +644,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
(c1.val, c2.val)
{
if self.infcx.try_unify_abstract_consts(a, b) {
if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
return Ok(EvaluatedToOk);
}
}

View file

@ -131,11 +131,11 @@ pub fn predicate_obligations<'a, 'tcx>(
wf.compute(a.into());
wf.compute(b.into());
}
ty::PredicateKind::ConstEvaluatable(def, substs) => {
let obligations = wf.nominal_obligations(def.did, substs);
ty::PredicateKind::ConstEvaluatable(uv) => {
let obligations = wf.nominal_obligations(uv.def.did, uv.substs);
wf.out.extend(obligations);
for arg in substs.iter() {
for arg in uv.substs.iter() {
wf.compute(arg);
}
}
@ -441,14 +441,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
GenericArgKind::Const(constant) => {
match constant.val {
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => {
assert!(promoted.is_none());
ty::ConstKind::Unevaluated(uv) => {
assert!(uv.promoted.is_none());
let obligations = self.nominal_obligations(def.did, substs);
let obligations = self.nominal_obligations(uv.def.did, uv.substs);
self.out.extend(obligations);
let predicate =
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(def, substs))
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(uv.shrink()))
.to_predicate(self.tcx());
let cause = self.cause(traits::MiscObligation);
self.out.push(traits::Obligation::with_depth(