Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evaluate repeat expression lengths as late as possible #69981

Merged
merged 9 commits into from
Mar 24, 2020
32 changes: 22 additions & 10 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2066,7 +2066,7 @@ pub enum Rvalue<'tcx> {
Use(Operand<'tcx>),

/// [x; 32]
Repeat(Operand<'tcx>, u64),
Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>),
eddyb marked this conversation as resolved.
Show resolved Hide resolved

/// &x or &mut x
Ref(Region<'tcx>, BorrowKind, Place<'tcx>),
Expand Down Expand Up @@ -2194,7 +2194,11 @@ impl<'tcx> Debug for Rvalue<'tcx> {

match *self {
Use(ref place) => write!(fmt, "{:?}", place),
Repeat(ref a, ref b) => write!(fmt, "[{:?}; {:?}]", a, b),
Repeat(ref a, ref b) => {
write!(fmt, "[{:?}; ", a)?;
pretty_print_const(b, fmt, false)?;
write!(fmt, "]")
}
Len(ref a) => write!(fmt, "Len({:?})", a),
Cast(ref kind, ref place, ref ty) => {
write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind)
Expand Down Expand Up @@ -2562,18 +2566,26 @@ impl<'tcx> Debug for Constant<'tcx> {

impl<'tcx> Display for Constant<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
use crate::ty::print::PrettyPrinter;
write!(fmt, "const ")?;
ty::tls::with(|tcx| {
let literal = tcx.lift(&self.literal).unwrap();
let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS);
cx.print_alloc_ids = true;
cx.pretty_print_const(literal, true)?;
Ok(())
})
pretty_print_const(self.literal, fmt, true)
}
}

fn pretty_print_const(
c: &ty::Const<'tcx>,
fmt: &mut Formatter<'_>,
print_types: bool,
) -> fmt::Result {
use crate::ty::print::PrettyPrinter;
ty::tls::with(|tcx| {
let literal = tcx.lift(&c).unwrap();
let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS);
cx.print_alloc_ids = true;
cx.pretty_print_const(literal, print_types)?;
Ok(())
})
}

impl<'tcx> graph::DirectedGraph for Body<'tcx> {
type Node = BasicBlock;
}
Expand Down
4 changes: 3 additions & 1 deletion src/librustc/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ impl<'tcx> Rvalue<'tcx> {
{
match *self {
Rvalue::Use(ref operand) => operand.ty(local_decls, tcx),
Rvalue::Repeat(ref operand, count) => tcx.mk_array(operand.ty(local_decls, tcx), count),
Rvalue::Repeat(ref operand, count) => {
tcx.mk_ty(ty::Array(operand.ty(local_decls, tcx), count))
Comment on lines -152 to +153
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder when we can have mk_array take a ty::Const.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely never, we have 14 uses in the code base, and half of them are ProjectionKind::SubSlice related.

}
Rvalue::Ref(reg, bk, ref place) => {
let place_ty = place.ty(local_decls, tcx).ty;
tcx.mk_ref(reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() })
Expand Down
86 changes: 84 additions & 2 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use self::TyKind::*;
use crate::infer::canonical::Canonical;
use crate::middle::region;
use crate::mir::interpret::ConstValue;
use crate::mir::interpret::Scalar;
use crate::mir::interpret::{LitToConstInput, Scalar};
use crate::mir::Promoted;
use crate::ty::layout::VariantIdx;
use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef};
Expand All @@ -20,7 +20,7 @@ use polonius_engine::Atom;
use rustc_ast::ast::{self, Ident};
use rustc_data_structures::captures::Captures;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_index::vec::Idx;
use rustc_macros::HashStable;
use rustc_span::symbol::{kw, Symbol};
Expand Down Expand Up @@ -2402,17 +2402,89 @@ pub struct Const<'tcx> {
static_assert_size!(Const<'_>, 48);

impl<'tcx> Const<'tcx> {
/// Literals and const generic parameters are eagerly converted to a constant, everything else
/// becomes `Unevaluated`.
pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self {
debug!("Const::from_anon_const(id={:?})", def_id);

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

let body_id = match tcx.hir().get(hir_id) {
hir::Node::AnonConst(ac) => ac.body,
_ => span_bug!(tcx.def_span(def_id.to_def_id()), "from_anon_const can only process anonymous constants"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My kingdom for a custom coercion LocalDefId -> DefId.
(impl Into<DefId> is bad for your fn bodies, sadly, if we could even use it for queries)

};

let expr = &tcx.hir().body(body_id).value;

let ty = tcx.type_of(def_id.to_def_id());

let lit_input = match expr.kind {
hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => match expr.kind {
hir::ExprKind::Lit(ref lit) => {
Some(LitToConstInput { lit: &lit.node, ty, neg: true })
}
_ => None,
},
_ => None,
};

if let Some(lit_input) = lit_input {
// If an error occurred, ignore that it's a literal and leave reporting the error up to
// mir.
if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) {
return c;
} else {
tcx.sess.delay_span_bug(expr.span, "ast_const_to_const: couldn't lit_to_const");
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
}
}

// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = match &expr.kind {
hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
block.expr.as_ref().unwrap()
}
_ => expr,
};

use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
let val = match expr.kind {
ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
// Find the name and index of the const parameter by indexing the generics of
// the parent item and construct a `ParamConst`.
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
let item_id = tcx.hir().get_parent_node(hir_id);
let item_def_id = tcx.hir().local_def_id(item_id);
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id)];
let name = tcx.hir().name(hir_id);
ty::ConstKind::Param(ty::ParamConst::new(index, name))
}
_ => ty::ConstKind::Unevaluated(
def_id.to_def_id(),
InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
None,
),
};

tcx.mk_const(ty::Const { val, ty })
}

#[inline]
/// Interns the given value as a constant.
pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self {
tcx.mk_const(Self { val: ConstKind::Value(val), ty })
}

#[inline]
/// Interns the given scalar as a constant.
pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self {
Self::from_value(tcx, ConstValue::Scalar(val), ty)
}

#[inline]
/// Creates a constant with the given integer value and interns it.
pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self {
let size = tcx
.layout_of(ty)
Expand All @@ -2422,21 +2494,27 @@ impl<'tcx> Const<'tcx> {
}

#[inline]
/// Creates an interned zst constant.
pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self {
Self::from_scalar(tcx, Scalar::zst(), ty)
}

#[inline]
/// Creates an interned bool constant.
pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self {
Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool))
}

#[inline]
/// Creates an interned usize constant.
pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self {
Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize))
}

#[inline]
/// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
/// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
/// contains const generic parameters or pointers).
pub fn try_eval_bits(
&self,
tcx: TyCtxt<'tcx>,
Expand All @@ -2450,6 +2528,8 @@ impl<'tcx> Const<'tcx> {
}

#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
/// unevaluated constant.
pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> {
let try_const_eval = |did, param_env: ParamEnv<'tcx>, substs, promoted| {
let param_env_and_substs = param_env.with_reveal_all().and(substs);
Expand Down Expand Up @@ -2506,12 +2586,14 @@ impl<'tcx> Const<'tcx> {
}

#[inline]
/// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
self.try_eval_bits(tcx, param_env, ty)
.unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
}

#[inline]
/// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 {
self.eval_bits(tcx, param_env, tcx.types.usize) as u64
}
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_codegen_ssa/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}

let count =
self.monomorphize(&count).eval_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all());

bx.write_operand_repeatedly(cg_elem, count, dest)
}

Expand Down
6 changes: 5 additions & 1 deletion src/librustc_mir/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1986,7 +1986,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}

Rvalue::Repeat(operand, len) => {
if *len > 1 {
// If the length cannot be evaluated we must assume that the length can be larger
// than 1.
// If the length is larger than 1, the repeat expression will need to copy the
// element, so we require the `Copy` trait.
if len.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
if let Operand::Move(_) = operand {
// While this is located in `nll::typeck` this error is not an NLL error, it's
// a required check to make sure that repeated elements implement `Copy`.
Expand Down
32 changes: 3 additions & 29 deletions src/librustc_mir_build/hair/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::hair::cx::to_ref::ToRef;
use crate::hair::cx::Cx;
use crate::hair::util::UserAnnotatedTyHelpers;
use crate::hair::*;
use rustc::mir::interpret::{ErrorHandled, Scalar};
use rustc::mir::interpret::Scalar;
use rustc::mir::BorrowKind;
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast};
use rustc::ty::subst::{InternalSubsts, SubstsRef};
Expand Down Expand Up @@ -406,34 +406,8 @@ fn make_mirror_unadjusted<'a, 'tcx>(

// Now comes the rote stuff:
hir::ExprKind::Repeat(ref v, ref count) => {
let def_id = cx.tcx.hir().local_def_id(count.hir_id);
let substs = InternalSubsts::identity_for_item(cx.tcx, def_id);
let span = cx.tcx.def_span(def_id);
let count = match cx.tcx.const_eval_resolve(
ty::ParamEnv::reveal_all(),
def_id,
substs,
None,
Some(span),
) {
Ok(cv) => {
if let Some(count) = cv.try_to_bits_for_ty(
cx.tcx,
ty::ParamEnv::reveal_all(),
cx.tcx.types.usize,
) {
count as u64
} else {
bug!("repeat count constant value can't be converted to usize");
}
}
Err(ErrorHandled::Reported) => 0,
Err(ErrorHandled::TooGeneric) => {
let span = cx.tcx.def_span(def_id);
cx.tcx.sess.span_err(span, "array lengths can't depend on generic parameters");
0
}
};
let count_def_id = cx.tcx.hir().local_def_id(count.hir_id).expect_local();
let count = ty::Const::from_anon_const(cx.tcx, count_def_id);

ExprKind::Repeat { value: v.to_ref(), count }
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir_build/hair/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ crate enum ExprKind<'tcx> {
},
Repeat {
value: ExprRef<'tcx>,
count: u64,
count: &'tcx Const<'tcx>,
},
Array {
fields: Vec<ExprRef<'tcx>>,
Expand Down
Loading