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 fixed-length array length expressions lazily. #44275

Merged
merged 7 commits into from
Sep 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ pub enum BindingAnnotation {
RefMut,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum RangeEnd {
Included,
Excluded,
Expand Down
4 changes: 0 additions & 4 deletions src/librustc/ich/impls_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,10 +493,6 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for mir::L
hasher: &mut StableHasher<W>) {
mem::discriminant(self).hash_stable(hcx, hasher);
match *self {
mir::Literal::Item { def_id, substs } => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
}
mir::Literal::Value { ref value } => {
value.hash_stable(hcx, hasher);
}
Expand Down
51 changes: 32 additions & 19 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
StableHasherResult};
use std::hash as std_hash;
use std::mem;
use syntax_pos::symbol::InternedString;
use middle::region;
use ty;

Expand Down Expand Up @@ -236,6 +235,10 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for ty::Pr
def_id.hash_stable(hcx, hasher);
closure_kind.hash_stable(hcx, hasher);
}
ty::Predicate::ConstEvaluatable(def_id, substs) => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
}
}
}
}
Expand Down Expand Up @@ -272,59 +275,69 @@ for ::middle::const_val::ConstVal<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
hasher: &mut StableHasher<W>) {
use middle::const_val::ConstVal;
use middle::const_val::ConstVal::*;
use middle::const_val::ConstAggregate::*;

mem::discriminant(self).hash_stable(hcx, hasher);

match *self {
ConstVal::Float(ref value) => {
Integral(ref value) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Integral(ref value) => {
Float(ref value) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Str(ref value) => {
Str(ref value) => {
value.hash_stable(hcx, hasher);
}
ConstVal::ByteStr(ref value) => {
ByteStr(ref value) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Bool(value) => {
Bool(value) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Char(value) => {
Char(value) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Variant(def_id) => {
Variant(def_id) => {
def_id.hash_stable(hcx, hasher);
}
ConstVal::Function(def_id, substs) => {
Function(def_id, substs) => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
}
ConstVal::Struct(ref name_value_map) => {
let mut values: Vec<(InternedString, &ConstVal)> =
name_value_map.iter()
.map(|(name, val)| (name.as_str(), val))
.collect();

Aggregate(Struct(ref name_values)) => {
let mut values = name_values.to_vec();
values.sort_unstable_by_key(|&(ref name, _)| name.clone());
values.hash_stable(hcx, hasher);
}
ConstVal::Tuple(ref value) => {
Aggregate(Tuple(ref value)) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Array(ref value) => {
Aggregate(Array(ref value)) => {
value.hash_stable(hcx, hasher);
}
ConstVal::Repeat(ref value, times) => {
Aggregate(Repeat(ref value, times)) => {
value.hash_stable(hcx, hasher);
times.hash_stable(hcx, hasher);
}
Unevaluated(def_id, substs) => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
}
}
}
}

impl_stable_hash_for!(struct ::middle::const_val::ByteArray<'tcx> {
data
});

impl_stable_hash_for!(struct ty::Const<'tcx> {
ty,
val
});

impl_stable_hash_for!(struct ty::ClosureSubsts<'tcx> { substs });

impl_stable_hash_for!(struct ty::GeneratorInterior<'tcx> { witness });
Expand Down
5 changes: 3 additions & 2 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ macro_rules! impl_trans_normalize {

impl_trans_normalize!('gcx,
Ty<'gcx>,
&'gcx ty::Const<'gcx>,
&'gcx Substs<'gcx>,
ty::FnSig<'gcx>,
ty::PolyFnSig<'gcx>,
Expand Down Expand Up @@ -493,7 +494,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
let param_env = ty::ParamEnv::empty(Reveal::All);
let value = self.erase_regions(value);

if !value.has_projection_types() {
if !value.has_projections() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Idle speculation: I wonder if we should call this requires_normalization, at some point.

Copy link
Member Author

Choose a reason for hiding this comment

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

needs_normalizing?

Copy link
Contributor

Choose a reason for hiding this comment

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

also fine

return value;
}

Expand All @@ -515,7 +516,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {

let value = self.erase_regions(value);

if !value.has_projection_types() {
if !value.has_projections() {
return value;
}

Expand Down
103 changes: 33 additions & 70 deletions src/librustc/middle/const_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,66 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use self::ConstVal::*;
pub use rustc_const_math::ConstInt;

use hir;
use hir::def::Def;
use hir::def_id::DefId;
use traits::Reveal;
use ty::{self, TyCtxt, layout};
use ty::subst::Substs;
use util::common::ErrorReported;
use rustc_const_math::*;

use graphviz::IntoCow;
use errors::DiagnosticBuilder;
use serialize::{self, Encodable, Encoder, Decodable, Decoder};
use syntax::symbol::InternedString;
use syntax::ast;
use syntax_pos::Span;

use std::borrow::Cow;
use std::collections::BTreeMap;
use std::rc::Rc;

pub type EvalResult<'tcx> = Result<ConstVal<'tcx>, ConstEvalErr<'tcx>>;
pub type EvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ConstEvalErr<'tcx>>;

#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
pub enum ConstVal<'tcx> {
Float(ConstFloat),
Integral(ConstInt),
Float(ConstFloat),
Str(InternedString),
ByteStr(Rc<Vec<u8>>),
ByteStr(ByteArray<'tcx>),
Bool(bool),
Char(char),
Variant(DefId),
Function(DefId, &'tcx Substs<'tcx>),
Struct(BTreeMap<ast::Name, ConstVal<'tcx>>),
Tuple(Vec<ConstVal<'tcx>>),
Array(Vec<ConstVal<'tcx>>),
Repeat(Box<ConstVal<'tcx>>, u64),
Aggregate(ConstAggregate<'tcx>),
Unevaluated(DefId, &'tcx Substs<'tcx>),
}

impl<'tcx> ConstVal<'tcx> {
pub fn description(&self) -> &'static str {
match *self {
Float(f) => f.description(),
Integral(i) => i.description(),
Str(_) => "string literal",
ByteStr(_) => "byte string literal",
Bool(_) => "boolean",
Char(..) => "char",
Variant(_) => "enum variant",
Struct(_) => "struct",
Tuple(_) => "tuple",
Function(..) => "function definition",
Array(..) => "array",
Repeat(..) => "repeat",
}
#[derive(Copy, Clone, Debug, Hash, RustcEncodable, Eq, PartialEq)]
pub struct ByteArray<'tcx> {
pub data: &'tcx [u8],
Copy link
Member

Choose a reason for hiding this comment

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

Making this a Slice would seem like a good idea (and other similar cases below) but I guess it can be left for later; I suppose doing that now would possibly require more guarantees on the part of the code?

}

impl<'tcx> serialize::UseSpecializedDecodable for ByteArray<'tcx> {}

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum ConstAggregate<'tcx> {
Struct(&'tcx [(ast::Name, &'tcx ty::Const<'tcx>)]),
Tuple(&'tcx [&'tcx ty::Const<'tcx>]),
Array(&'tcx [&'tcx ty::Const<'tcx>]),
Repeat(&'tcx ty::Const<'tcx>, u64),
}

impl<'tcx> Encodable for ConstAggregate<'tcx> {
fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
bug!("should never encode ConstAggregate::{:?}", self)
}
}

impl<'tcx> Decodable for ConstAggregate<'tcx> {
fn decode<D: Decoder>(_: &mut D) -> Result<Self, D::Error> {
bug!("should never decode ConstAggregate")
}
}

impl<'tcx> ConstVal<'tcx> {
pub fn to_const_int(&self) -> Option<ConstInt> {
match *self {
ConstVal::Integral(i) => Some(i),
Expand All @@ -86,8 +88,6 @@ pub struct ConstEvalErr<'tcx> {
pub enum ErrKind<'tcx> {
CannotCast,
MissingStructField,
NegateOn(ConstVal<'tcx>),
NotOn(ConstVal<'tcx>),

NonConstPath,
UnimplementedConstVal(&'static str),
Expand Down Expand Up @@ -146,9 +146,6 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {

match self.kind {
CannotCast => simple!("can't cast this type"),
NegateOn(ref const_val) => simple!("negate on {}", const_val.description()),
NotOn(ref const_val) => simple!("not on {}", const_val.description()),

MissingStructField => simple!("nonexistent struct field"),
NonConstPath => simple!("non-constant path in constant expression"),
UnimplementedConstVal(what) =>
Expand Down Expand Up @@ -221,37 +218,3 @@ impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
self.struct_error(tcx, primary_span, primary_kind).emit();
}
}

/// Returns the value of the length-valued expression
pub fn eval_length(tcx: TyCtxt,
count: hir::BodyId,
reason: &str)
-> Result<usize, ErrorReported>
{
let count_expr = &tcx.hir.body(count).value;
let count_def_id = tcx.hir.body_owner_def_id(count);
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
let substs = Substs::identity_for_item(tcx.global_tcx(), count_def_id);
match tcx.at(count_expr.span).const_eval(param_env.and((count_def_id, substs))) {
Ok(Integral(Usize(count))) => {
let val = count.as_u64(tcx.sess.target.uint_type);
assert_eq!(val as usize as u64, val);
Ok(val as usize)
},
Ok(_) |
Err(ConstEvalErr { kind: ErrKind::TypeckError, .. }) => Err(ErrorReported),
Err(err) => {
let mut diag = err.struct_error(tcx, count_expr.span, reason);

if let hir::ExprPath(hir::QPath::Resolved(None, ref path)) = count_expr.node {
if let Def::Local(..) = path.def {
diag.note(&format!("`{}` is a variable",
tcx.hir.node_to_pretty_string(count_expr.id)));
}
}

diag.emit();
Err(ErrorReported)
}
}
}
3 changes: 2 additions & 1 deletion src/librustc/middle/free_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ impl<'tcx> FreeRegionMap<'tcx> {
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) => {
ty::Predicate::TypeOutlives(..) |
ty::Predicate::ConstEvaluatable(..) => {
// No region bounds here
}
ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => {
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,8 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {

// Always promote `[T; 0]` (even when e.g. borrowed mutably).
let promotable = match expr_ty.sty {
ty::TyArray(_, 0) => true,
ty::TyArray(_, len) if
len.val.to_const_int().and_then(|i| i.to_u64()) == Some(0) => true,
_ => promotable,
};

Expand Down
Loading