Skip to content

Commit

Permalink
Evaluate repeat expression lengths as late as possible
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Mar 23, 2020
1 parent 8ff7850 commit 799b15e
Show file tree
Hide file tree
Showing 46 changed files with 279 additions and 264 deletions.
32 changes: 22 additions & 10 deletions src/librustc/mir/mod.rs
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>),

/// &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
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))
}
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
70 changes: 69 additions & 1 deletion src/librustc/ty/sty.rs
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 Down Expand Up @@ -2401,7 +2401,75 @@ pub struct Const<'tcx> {
#[cfg(target_arch = "x86_64")]
static_assert_size!(Const<'_>, 48);

/// Returns the `DefId` of the constant parameter that the provided expression is a path to.
fn const_param_def_id(expr: &hir::Expr<'_>) -> Option<DefId> {
// 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,
};

match &expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => match path.res {
hir::def::Res::Def(hir::def::DefKind::ConstParam, did) => Some(did),
_ => None,
},
_ => None,
}
}

impl<'tcx> Const<'tcx> {
pub fn from_hir_anon_const(
tcx: TyCtxt<'tcx>,
ast_const: &hir::AnonConst,
ty: Ty<'tcx>,
) -> &'tcx Self {
debug!("Const::from_hir_anon_const(id={:?}, ast_const={:?})", ast_const.hir_id, ast_const);

let def_id = tcx.hir().local_def_id(ast_const.hir_id);

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

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");
}
}

let kind = if let Some(def_id) = const_param_def_id(expr) {
// 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))
} else {
ty::ConstKind::Unevaluated(def_id, InternalSubsts::identity_for_item(tcx, def_id), None)
};
tcx.mk_const(ty::Const { val: kind, ty })
}

#[inline]
pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self {
tcx.mk_const(Self { val: ConstKind::Value(val), ty })
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_codegen_ssa/mir/rvalue.rs
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
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
31 changes: 2 additions & 29 deletions src/librustc_mir_build/hair/cx/expr.rs
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,7 @@ 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 = ty::Const::from_hir_anon_const(cx.tcx, count, cx.tcx.types.usize);

ExprKind::Repeat { value: v.to_ref(), count }
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir_build/hair/mod.rs
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
77 changes: 3 additions & 74 deletions src/librustc_typeck/astconv.rs
Expand Up @@ -22,7 +22,7 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
use rustc_hir::print;
use rustc_hir::{Constness, ExprKind, GenericArg, GenericArgs};
use rustc_hir::{Constness, GenericArg, GenericArgs};
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, LATE_BOUND_LIFETIME_ARGUMENTS};
use rustc_session::parse::feature_err;
use rustc_session::Session;
Expand All @@ -39,8 +39,6 @@ use std::collections::BTreeSet;
use std::iter;
use std::slice;

use rustc::mir::interpret::LitToConstInput;

#[derive(Debug)]
pub struct PathSeg(pub DefId, pub usize);

Expand Down Expand Up @@ -782,7 +780,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}
(GenericParamDefKind::Const, GenericArg::Const(ct)) => {
self.ast_const_to_const(&ct.value, tcx.type_of(param.def_id)).into()
ty::Const::from_hir_anon_const(tcx, &ct.value, tcx.type_of(param.def_id)).into()
}
_ => unreachable!(),
},
Expand Down Expand Up @@ -2766,7 +2764,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.unwrap_or(tcx.types.err)
}
hir::TyKind::Array(ref ty, ref length) => {
let length = self.ast_const_to_const(length, tcx.types.usize);
let length = ty::Const::from_hir_anon_const(tcx, length, tcx.types.usize);
let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(&ty), length));
self.normalize_ty(ast_ty.span, array_ty)
}
Expand Down Expand Up @@ -2798,75 +2796,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
result_ty
}

/// Returns the `DefId` of the constant parameter that the provided expression is a path to.
pub fn const_param_def_id(&self, expr: &hir::Expr<'_>) -> Option<DefId> {
// 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 {
ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
block.expr.as_ref().unwrap()
}
_ => expr,
};

match &expr.kind {
ExprKind::Path(hir::QPath::Resolved(_, path)) => match path.res {
Res::Def(DefKind::ConstParam, did) => Some(did),
_ => None,
},
_ => None,
}
}

pub fn ast_const_to_const(
&self,
ast_const: &hir::AnonConst,
ty: Ty<'tcx>,
) -> &'tcx ty::Const<'tcx> {
debug!("ast_const_to_const(id={:?}, ast_const={:?})", ast_const.hir_id, ast_const);

let tcx = self.tcx();
let def_id = tcx.hir().local_def_id(ast_const.hir_id);

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

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");
}
}

let kind = if let Some(def_id) = self.const_param_def_id(expr) {
// 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))
} else {
ty::ConstKind::Unevaluated(def_id, InternalSubsts::identity_for_item(tcx, def_id), None)
};
tcx.mk_const(ty::Const { val: kind, ty })
}

pub fn impl_trait_ty_to_ty(
&self,
def_id: DefId,
Expand Down
21 changes: 3 additions & 18 deletions src/librustc_typeck/check/expr.rs
Expand Up @@ -18,7 +18,6 @@ use crate::type_error_struct;
use crate::util::common::ErrorReported;

use rustc::middle::lang_items;
use rustc::mir::interpret::ErrorHandled;
use rustc::ty;
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::Ty;
Expand Down Expand Up @@ -1009,12 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
let tcx = self.tcx;
let count_def_id = tcx.hir().local_def_id(count.hir_id);
let count = if self.const_param_def_id(count).is_some() {
Ok(self.to_const(count, tcx.type_of(count_def_id)))
} else {
tcx.const_eval_poly(count_def_id)
.map(|val| ty::Const::from_value(tcx, val, tcx.type_of(count_def_id)))
};
let count = self.to_const(count, tcx.type_of(count_def_id));

let uty = match expected {
ExpectHasType(uty) => match uty.kind {
Expand Down Expand Up @@ -1042,17 +1036,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if element_ty.references_error() {
return tcx.types.err;
}
match count {
Ok(count) => tcx.mk_ty(ty::Array(t, count)),
Err(ErrorHandled::TooGeneric) => {
self.tcx.sess.span_err(
tcx.def_span(count_def_id),
"array lengths can't depend on generic parameters",
);
tcx.types.err
}
Err(ErrorHandled::Reported) => tcx.types.err,
}

tcx.mk_ty(ty::Array(t, count))
}

fn check_expr_tuple(
Expand Down
7 changes: 1 addition & 6 deletions src/librustc_typeck/check/mod.rs
Expand Up @@ -3279,13 +3279,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty
}

/// Returns the `DefId` of the constant parameter that the provided expression is a path to.
pub fn const_param_def_id(&self, hir_c: &hir::AnonConst) -> Option<DefId> {
AstConv::const_param_def_id(self, &self.tcx.hir().body(hir_c.body).value)
}

pub fn to_const(&self, ast_c: &hir::AnonConst, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> {
AstConv::ast_const_to_const(self, ast_c, ty)
ty::Const::from_hir_anon_const(self.tcx, ast_c, ty)
}

// If the type given by the user has free regions, save it for later, since
Expand Down

0 comments on commit 799b15e

Please sign in to comment.