try to evaluate in try_unify
This commit is contained in:
parent
d8e564715e
commit
47f78a2487
4 changed files with 68 additions and 37 deletions
|
@ -691,11 +691,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
&self,
|
&self,
|
||||||
a: ty::Unevaluated<'tcx, ()>,
|
a: ty::Unevaluated<'tcx, ()>,
|
||||||
b: ty::Unevaluated<'tcx, ()>,
|
b: ty::Unevaluated<'tcx, ()>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default());
|
let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default());
|
||||||
debug!("canonical consts: {:?}", &canonical.value);
|
debug!("canonical consts: {:?}", &canonical.value);
|
||||||
|
|
||||||
self.tcx.try_unify_abstract_consts(canonical.value)
|
self.tcx.try_unify_abstract_consts(param_env.and(canonical.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_in_snapshot(&self) -> bool {
|
pub fn is_in_snapshot(&self) -> bool {
|
||||||
|
|
|
@ -329,12 +329,12 @@ rustc_queries! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query try_unify_abstract_consts(key: (
|
query try_unify_abstract_consts(key:
|
||||||
ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>
|
ty::ParamEnvAnd<'tcx, (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>
|
||||||
)) -> bool {
|
)>) -> bool {
|
||||||
desc {
|
desc {
|
||||||
|tcx| "trying to unify the generic constants {} and {}",
|
|tcx| "trying to unify the generic constants {} and {}",
|
||||||
tcx.def_path_str(key.0.def.did), tcx.def_path_str(key.1.def.did)
|
tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -585,7 +585,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
||||||
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
|
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
|
||||||
if tcx.features().generic_const_exprs =>
|
if tcx.features().generic_const_exprs =>
|
||||||
{
|
{
|
||||||
tcx.try_unify_abstract_consts((au.shrink(), bu.shrink()))
|
tcx.try_unify_abstract_consts(relation.param_env().and((au.shrink(), bu.shrink())))
|
||||||
}
|
}
|
||||||
|
|
||||||
// While this is slightly incorrect, it shouldn't matter for `min_const_generics`
|
// While this is slightly incorrect, it shouldn't matter for `min_const_generics`
|
||||||
|
|
|
@ -28,13 +28,13 @@ use std::iter;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
/// Check if a given constant can be evaluated.
|
/// Check if a given constant can be evaluated.
|
||||||
|
#[instrument(skip(infcx), level = "debug")]
|
||||||
pub fn is_const_evaluatable<'cx, 'tcx>(
|
pub fn is_const_evaluatable<'cx, 'tcx>(
|
||||||
infcx: &InferCtxt<'cx, 'tcx>,
|
infcx: &InferCtxt<'cx, 'tcx>,
|
||||||
uv: ty::Unevaluated<'tcx, ()>,
|
uv: ty::Unevaluated<'tcx, ()>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<(), NotConstEvaluatable> {
|
) -> Result<(), NotConstEvaluatable> {
|
||||||
debug!("is_const_evaluatable({:?})", uv);
|
|
||||||
let tcx = infcx.tcx;
|
let tcx = infcx.tcx;
|
||||||
|
|
||||||
if tcx.features().generic_const_exprs {
|
if tcx.features().generic_const_exprs {
|
||||||
|
@ -185,6 +185,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(tcx), level = "debug")]
|
||||||
fn satisfied_from_param_env<'tcx>(
|
fn satisfied_from_param_env<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
ct: AbstractConst<'tcx>,
|
ct: AbstractConst<'tcx>,
|
||||||
|
@ -197,10 +198,11 @@ fn satisfied_from_param_env<'tcx>(
|
||||||
// Try to unify with each subtree in the AbstractConst to allow for
|
// Try to unify with each subtree in the AbstractConst to allow for
|
||||||
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
|
// `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
|
||||||
// predicate for `(N + 1) * 2`
|
// predicate for `(N + 1) * 2`
|
||||||
let result =
|
let result = walk_abstract_const(tcx, b_ct, |b_ct| {
|
||||||
walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) {
|
match try_unify(tcx, ct, b_ct, param_env) {
|
||||||
true => ControlFlow::BREAK,
|
true => ControlFlow::BREAK,
|
||||||
false => ControlFlow::CONTINUE,
|
false => ControlFlow::CONTINUE,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let ControlFlow::Break(()) = result {
|
if let ControlFlow::Break(()) = result {
|
||||||
|
@ -570,11 +572,12 @@ pub(super) fn thir_abstract_const<'tcx>(
|
||||||
pub(super) fn try_unify_abstract_consts<'tcx>(
|
pub(super) fn try_unify_abstract_consts<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
(a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
|
(a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
(|| {
|
(|| {
|
||||||
if let Some(a) = AbstractConst::new(tcx, a)? {
|
if let Some(a) = AbstractConst::new(tcx, a)? {
|
||||||
if let Some(b) = AbstractConst::new(tcx, b)? {
|
if let Some(b) = AbstractConst::new(tcx, b)? {
|
||||||
return Ok(try_unify(tcx, a, b));
|
return Ok(try_unify(tcx, a, b, param_env));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,32 +622,59 @@ where
|
||||||
recurse(tcx, ct, &mut f)
|
recurse(tcx, ct, &mut f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to unify two abstract constants using structural equality.
|
// Substitutes generics repeatedly to allow AbstractConsts to unify where a
|
||||||
pub(super) fn try_unify<'tcx>(
|
// ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g.
|
||||||
|
// Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
|
||||||
|
#[inline]
|
||||||
|
#[instrument(skip(tcx), level = "debug")]
|
||||||
|
fn try_replace_substs_in_root<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
mut a: AbstractConst<'tcx>,
|
mut abstr_const: AbstractConst<'tcx>,
|
||||||
mut b: AbstractConst<'tcx>,
|
) -> Option<AbstractConst<'tcx>> {
|
||||||
) -> bool {
|
while let Node::Leaf(ct) = abstr_const.root(tcx) {
|
||||||
// We substitute generics repeatedly to allow AbstractConsts to unify where a
|
match AbstractConst::from_const(tcx, ct) {
|
||||||
// ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g.
|
Ok(Some(act)) => abstr_const = act,
|
||||||
// Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
|
|
||||||
while let Node::Leaf(a_ct) = a.root(tcx) {
|
|
||||||
match AbstractConst::from_const(tcx, a_ct) {
|
|
||||||
Ok(Some(a_act)) => a = a_act,
|
|
||||||
Ok(None) => break,
|
Ok(None) => break,
|
||||||
Err(_) => return true,
|
Err(_) => return None,
|
||||||
}
|
|
||||||
}
|
|
||||||
while let Node::Leaf(b_ct) = b.root(tcx) {
|
|
||||||
match AbstractConst::from_const(tcx, b_ct) {
|
|
||||||
Ok(Some(b_act)) => b = b_act,
|
|
||||||
Ok(None) => break,
|
|
||||||
Err(_) => return true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match (a.root(tcx), b.root(tcx)) {
|
Some(abstr_const)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to unify two abstract constants using structural equality.
|
||||||
|
#[instrument(skip(tcx), level = "debug")]
|
||||||
|
pub(super) fn try_unify<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
a: AbstractConst<'tcx>,
|
||||||
|
b: AbstractConst<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
let a = match try_replace_substs_in_root(tcx, a) {
|
||||||
|
Some(a) => a,
|
||||||
|
None => {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let b = match try_replace_substs_in_root(tcx, b) {
|
||||||
|
Some(b) => b,
|
||||||
|
None => {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let a_root = a.root(tcx);
|
||||||
|
let b_root = b.root(tcx);
|
||||||
|
debug!(?a_root, ?b_root);
|
||||||
|
|
||||||
|
match (a_root, b_root) {
|
||||||
(Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
|
(Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
|
||||||
|
let a_ct = a_ct.eval(tcx, param_env);
|
||||||
|
debug!("a_ct evaluated: {:?}", a_ct);
|
||||||
|
let b_ct = b_ct.eval(tcx, param_env);
|
||||||
|
debug!("b_ct evaluated: {:?}", b_ct);
|
||||||
|
|
||||||
if a_ct.ty() != b_ct.ty() {
|
if a_ct.ty() != b_ct.ty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -678,23 +708,23 @@ pub(super) fn try_unify<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
|
(Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
|
||||||
try_unify(tcx, a.subtree(al), b.subtree(bl))
|
try_unify(tcx, a.subtree(al), b.subtree(bl), param_env)
|
||||||
&& try_unify(tcx, a.subtree(ar), b.subtree(br))
|
&& try_unify(tcx, a.subtree(ar), b.subtree(br), param_env)
|
||||||
}
|
}
|
||||||
(Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
|
(Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
|
||||||
try_unify(tcx, a.subtree(av), b.subtree(bv))
|
try_unify(tcx, a.subtree(av), b.subtree(bv), param_env)
|
||||||
}
|
}
|
||||||
(Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
|
(Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
|
||||||
if a_args.len() == b_args.len() =>
|
if a_args.len() == b_args.len() =>
|
||||||
{
|
{
|
||||||
try_unify(tcx, a.subtree(a_f), b.subtree(b_f))
|
try_unify(tcx, a.subtree(a_f), b.subtree(b_f), param_env)
|
||||||
&& iter::zip(a_args, b_args)
|
&& iter::zip(a_args, b_args)
|
||||||
.all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn)))
|
.all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn), param_env))
|
||||||
}
|
}
|
||||||
(Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
|
(Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
|
||||||
if (a_ty == b_ty) && (a_kind == b_kind) =>
|
if (a_ty == b_ty) && (a_kind == b_kind) =>
|
||||||
{
|
{
|
||||||
try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand))
|
try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand), param_env)
|
||||||
}
|
}
|
||||||
// use this over `_ => false` to make adding variants to `Node` less error prone
|
// use this over `_ => false` to make adding variants to `Node` less error prone
|
||||||
(Node::Cast(..), _)
|
(Node::Cast(..), _)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue