Auto merge of #82936 - oli-obk:valtree, r=RalfJung,lcnr,matthewjasper

Implement (but don't use) valtree and refactor in preparation of use

This PR does not cause any functional change. It refactors various things that are needed to make valtrees possible. This refactoring got big enough that I decided I'd want it reviewed as a PR instead of trying to make one huge PR with all the changes.

cc `@rust-lang/wg-const-eval` on the following commits:

* 2027184 implement valtree
* eeecea9 fallible Scalar -> ScalarInt
* 042f663 ScalarInt convenience methods

cc `@eddyb` on ef04a6d

cc `@rust-lang/wg-mir-opt` for cf1700c (`mir::Constant` can now represent either a `ConstValue` or a `ty::Const`, and it is totally possible to have two different representations for the same value)
This commit is contained in:
bors
2021-03-16 22:42:56 +00:00
56 changed files with 783 additions and 331 deletions

View File

@@ -246,25 +246,27 @@ where
};
// Check the qualifs of the value of `const` items.
if let ty::ConstKind::Unevaluated(def, _, promoted) = constant.literal.val {
assert!(promoted.is_none());
// Don't peek inside trait associated constants.
if cx.tcx.trait_of_item(def.did).is_none() {
let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did))
} else {
cx.tcx.at(constant.span).mir_const_qualif(def.did)
};
if let Some(ct) = constant.literal.const_for_ty() {
if let ty::ConstKind::Unevaluated(def, _, promoted) = ct.val {
assert!(promoted.is_none());
// Don't peek inside trait associated constants.
if cx.tcx.trait_of_item(def.did).is_none() {
let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did))
} else {
cx.tcx.at(constant.span).mir_const_qualif(def.did)
};
if !Q::in_qualifs(&qualifs) {
return false;
if !Q::in_qualifs(&qualifs) {
return false;
}
// Just in case the type is more specific than
// the definition, e.g., impl associated const
// with type parameters, take it into account.
}
// Just in case the type is more specific than
// the definition, e.g., impl associated const
// with type parameters, take it into account.
}
}
// Otherwise use the qualifs of the type.
Q::in_any_value_of_ty(cx, constant.literal.ty)
Q::in_any_value_of_ty(cx, constant.literal.ty())
}

View File

@@ -13,9 +13,9 @@ use rustc_middle::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
use rustc_middle::mir::{
AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local, LocalDecl, LocalKind,
Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl,
LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData,
Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -482,18 +482,21 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None;
}
match self.ecx.const_to_op(c.literal, None) {
match self.ecx.mir_const_to_op(&c.literal, None) {
Ok(op) => Some(op),
Err(error) => {
let tcx = self.ecx.tcx.at(c.span);
let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
if let Some(lint_root) = self.lint_root(source_info) {
let lint_only = match c.literal.val {
// Promoteds must lint and not error as the user didn't ask for them
ConstKind::Unevaluated(_, _, Some(_)) => true,
// Out of backwards compatibility we cannot report hard errors in unused
// generic functions using associated constants of the generic parameters.
_ => c.literal.needs_subst(),
let lint_only = match c.literal {
ConstantKind::Ty(ct) => match ct.val {
// Promoteds must lint and not error as the user didn't ask for them
ConstKind::Unevaluated(_, _, Some(_)) => true,
// Out of backwards compatibility we cannot report hard errors in unused
// generic functions using associated constants of the generic parameters.
_ => c.literal.needs_subst(),
},
ConstantKind::Val(_, ty) => ty.needs_subst(),
};
if lint_only {
// Out of backwards compatibility we cannot report hard errors in unused
@@ -803,7 +806,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
Operand::Constant(Box::new(Constant {
span,
user_ty: None,
literal: ty::Const::from_scalar(self.tcx, scalar, ty),
literal: ty::Const::from_scalar(self.tcx, scalar, ty).into(),
}))
}
@@ -814,9 +817,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
source_info: SourceInfo,
) {
if let Rvalue::Use(Operand::Constant(c)) = rval {
if !matches!(c.literal.val, ConstKind::Unevaluated(..)) {
trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
return;
match c.literal {
ConstantKind::Ty(c) if matches!(c.val, ConstKind::Unevaluated(..)) => {}
_ => {
trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
return;
}
}
}
@@ -883,13 +889,17 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
*rval = Rvalue::Use(Operand::Constant(Box::new(Constant {
span: source_info.span,
user_ty: None,
literal: self.ecx.tcx.mk_const(ty::Const {
ty,
val: ty::ConstKind::Value(ConstValue::ByRef {
alloc,
offset: Size::ZERO,
}),
}),
literal: self
.ecx
.tcx
.mk_const(ty::Const {
ty,
val: ty::ConstKind::Value(ConstValue::ByRef {
alloc,
offset: Size::ZERO,
}),
})
.into(),
})));
}
}

View File

@@ -471,7 +471,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
Rvalue::Use(Operand::Constant(Box::new(Constant {
span,
user_ty: None,
literal: ty::Const::from_bool(self.tcx, val),
literal: ty::Const::from_bool(self.tcx, val).into(),
})))
}

View File

@@ -989,7 +989,7 @@ fn insert_panic_block<'tcx>(
cond: Operand::Constant(box Constant {
span: body.span,
user_ty: None,
literal: ty::Const::from_bool(tcx, false),
literal: ty::Const::from_bool(tcx, false).into(),
}),
expected: true,
msg: message,

View File

@@ -416,7 +416,7 @@ impl Inliner<'tcx> {
TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => {
if let ty::FnDef(def_id, substs) =
*callsite.callee.subst_mir(self.tcx, &f.literal.ty).kind()
*callsite.callee.subst_mir(self.tcx, &f.literal.ty()).kind()
{
let substs = self.tcx.normalize_erasing_regions(self.param_env, substs);
if let Ok(Some(instance)) =
@@ -637,8 +637,11 @@ impl Inliner<'tcx> {
// `required_consts`, here we may not only have `ConstKind::Unevaluated`
// because we are calling `subst_and_normalize_erasing_regions`.
caller_body.required_consts.extend(
callee_body.required_consts.iter().copied().filter(|&constant| {
matches!(constant.literal.val, ConstKind::Unevaluated(_, _, _))
callee_body.required_consts.iter().copied().filter(|&ct| {
match ct.literal.const_for_ty() {
Some(ct) => matches!(ct.val, ConstKind::Unevaluated(_, _, _)),
None => true,
}
}),
);
}

View File

@@ -79,7 +79,7 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {
fn try_eval_bool(&self, a: &Operand<'_>) -> Option<bool> {
let a = a.constant()?;
if a.literal.ty.is_bool() { a.literal.val.try_to_bool() } else { None }
if a.literal.ty().is_bool() { a.literal.try_to_bool() } else { None }
}
/// Transform "&(*a)" ==> "a".
@@ -110,12 +110,13 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {
fn combine_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
if let Rvalue::Len(ref place) = *rvalue {
let place_ty = place.ty(self.local_decls, self.tcx).ty;
if let ty::Array(_, len) = place_ty.kind() {
if let ty::Array(_, len) = *place_ty.kind() {
if !self.should_combine(source_info, rvalue) {
return;
}
let constant = Constant { span: source_info.span, literal: len, user_ty: None };
let constant =
Constant { span: source_info.span, literal: len.into(), user_ty: None };
*rvalue = Rvalue::Use(Operand::Constant(box constant));
}
}

View File

@@ -33,7 +33,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
Rvalue::Use(Operand::Constant(box Constant {
span: terminator.source_info.span,
user_ty: None,
literal: ty::Const::zero_sized(tcx, tcx.types.unit),
literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
})),
)),
});

View File

@@ -93,8 +93,8 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))),
StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))),
) if lhs_f == lhs_s
&& f_c.literal.ty.is_bool()
&& s_c.literal.ty.is_bool()
&& f_c.literal.ty().is_bool()
&& s_c.literal.ty().is_bool()
&& f_c.literal.try_eval_bool(tcx, param_env).is_some()
&& s_c.literal.try_eval_bool(tcx, param_env).is_some() => {}

View File

@@ -921,7 +921,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
let unit = Rvalue::Use(Operand::Constant(box Constant {
span: statement.source_info.span,
user_ty: None,
literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit),
literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit).into(),
}));
mem::replace(rhs, unit)
},
@@ -998,20 +998,22 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
Operand::Constant(Box::new(Constant {
span,
user_ty: None,
literal: tcx.mk_const(ty::Const {
ty,
val: ty::ConstKind::Unevaluated(
def,
InternalSubsts::for_item(tcx, def.did, |param, _| {
if let ty::GenericParamDefKind::Lifetime = param.kind {
tcx.lifetimes.re_erased.into()
} else {
tcx.mk_param_from_def(param)
}
}),
Some(promoted_id),
),
}),
literal: tcx
.mk_const(ty::Const {
ty,
val: ty::ConstKind::Unevaluated(
def,
InternalSubsts::for_item(tcx, def.did, |param, _| {
if let ty::GenericParamDefKind::Lifetime = param.kind {
tcx.lifetimes.re_erased.into()
} else {
tcx.mk_param_from_def(param)
}
}),
Some(promoted_id),
),
})
.into(),
}))
};
let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
@@ -1250,8 +1252,8 @@ crate fn is_const_fn_in_array_repeat_expression<'tcx>(
if let Some(Terminator { kind: TerminatorKind::Call { func, destination, .. }, .. }) =
&block.terminator
{
if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
if let ty::FnDef(def_id, _) = *ty.kind() {
if let Operand::Constant(box Constant { literal, .. }) = func {
if let ty::FnDef(def_id, _) = *literal.ty().kind() {
if let Some((destination_place, _)) = destination {
if destination_place == place {
if is_const_fn(ccx.tcx, def_id) {

View File

@@ -14,10 +14,10 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) {
let const_kind = constant.literal.val;
if let ConstKind::Unevaluated(_, _, _) = const_kind {
self.required_consts.push(*constant);
if let Some(ct) = constant.literal.const_for_ty() {
if let ConstKind::Unevaluated(_, _, _) = ct.val {
self.required_consts.push(*constant);
}
}
}
}

View File

@@ -205,7 +205,7 @@ impl PeekCall {
if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
&terminator.kind
{
if let ty::FnDef(def_id, substs) = *func.literal.ty.kind() {
if let ty::FnDef(def_id, substs) = *func.literal.ty().kind() {
let sig = tcx.fn_sig(def_id);
let name = tcx.item_name(def_id);
if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {

View File

@@ -205,12 +205,12 @@ fn find_branch_value_info<'tcx>(
match (left, right) {
(Constant(branch_value), Copy(to_switch_on) | Move(to_switch_on))
| (Copy(to_switch_on) | Move(to_switch_on), Constant(branch_value)) => {
let branch_value_ty = branch_value.literal.ty;
let branch_value_ty = branch_value.literal.ty();
// we only want to apply this optimization if we are matching on integrals (and chars), as it is not possible to switch on floats
if !branch_value_ty.is_integral() && !branch_value_ty.is_char() {
return None;
};
let branch_value_scalar = branch_value.literal.val.try_to_scalar()?;
let branch_value_scalar = branch_value.literal.try_to_scalar()?;
Some((branch_value_scalar, branch_value_ty, *to_switch_on))
}
_ => None,