Skip to content

Commit

Permalink
Auto merge of #44275 - eddyb:deferred-ctfe, r=nikomatsakis
Browse files Browse the repository at this point in the history
Evaluate fixed-length array length expressions lazily.

This is in preparation for polymorphic array lengths (aka `[T; T::A]`) and const generics.
We need deferred const-evaluation to break cycles when array types show up in positions which require knowing the array type to typeck the array length, e.g. the array type is in a `where` clause.

The final step - actually passing bounds in scope to array length expressions from the parent - is not done because it still produces cycles when *normalizing* `ParamEnv`s, and @nikomatsakis' in-progress lazy normalization work is needed to deal with that uniformly.

However, the changes here are still useful to unlock work on const generics, which @EpicatSupercell manifested interest in, and I might be mentoring them for that, but we need this baseline first.

r? @nikomatsakis cc @oli-obk
  • Loading branch information
bors committed Sep 12, 2017
2 parents 11f64d8 + 57ebd28 commit 3cb24bd
Show file tree
Hide file tree
Showing 104 changed files with 1,605 additions and 785 deletions.
2 changes: 1 addition & 1 deletion src/librustc/hir/mod.rs
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
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
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
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() {
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
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],
}

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
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
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

0 comments on commit 3cb24bd

Please sign in to comment.