Skip to content

Commit

Permalink
rustc: evaluate fixed-length array length expressions lazily.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Sep 11, 2017
1 parent 8821761 commit 74349fa
Show file tree
Hide file tree
Showing 48 changed files with 517 additions and 122 deletions.
8 changes: 8 additions & 0 deletions src/librustc/ich/impls_ty.rs
Expand Up @@ -235,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 @@ -317,6 +321,10 @@ for ::middle::const_val::ConstVal<'gcx> {
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);
}
}
}
}
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
39 changes: 1 addition & 38 deletions src/librustc/middle/const_val.rs
Expand Up @@ -10,13 +10,9 @@

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;
Expand All @@ -41,6 +37,7 @@ pub enum ConstVal<'tcx> {
Variant(DefId),
Function(DefId, &'tcx Substs<'tcx>),
Aggregate(ConstAggregate<'tcx>),
Unevaluated(DefId, &'tcx Substs<'tcx>),
}

#[derive(Copy, Clone, Debug, Hash, RustcEncodable, Eq, PartialEq)]
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<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
count: hir::BodyId,
reason: &str)
-> Result<&'gcx ty::Const<'gcx>, 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(count) => {
// Elsewhere in the compiler this is enforced even in the presence
// of erroneous code (type mismatch error has already been emitted).
assert_eq!(count.ty, tcx.types.usize);
Ok(count)
}
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(_, len) if len.val.to_const_int().unwrap().to_u64().unwrap() == 0 => true,
ty::TyArray(_, len) if
len.val.to_const_int().and_then(|i| i.to_u64()) == Some(0) => true,
_ => promotable,
};

Expand Down
1 change: 1 addition & 0 deletions src/librustc/mir/mod.rs
Expand Up @@ -1535,6 +1535,7 @@ fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
Variant(def_id) |
Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)),
Aggregate(_) => bug!("`ConstVal::{:?}` should not be in MIR", const_val),
Unevaluated(..) => write!(fmt, "{:?}", const_val)
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/librustc/traits/error_reporting.rs
Expand Up @@ -19,6 +19,7 @@ use super::{
OnUnimplementedNote,
OutputTypeParameterMismatch,
TraitNotObjectSafe,
ConstEvalFailure,
PredicateObligation,
Reveal,
SelectionContext,
Expand All @@ -31,6 +32,7 @@ use hir;
use hir::def_id::DefId;
use infer::{self, InferCtxt};
use infer::type_variable::TypeVariableOrigin;
use middle::const_val;
use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
use std::fmt;
use syntax::ast;
Expand Down Expand Up @@ -698,6 +700,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// (which may fail).
span_bug!(span, "WF predicate not satisfied for {:?}", ty);
}

ty::Predicate::ConstEvaluatable(..) => {
// Errors for `ConstEvaluatable` predicates show up as
// `SelectionError::ConstEvalFailure`,
// not `Unimplemented`.
span_bug!(span,
"const-evaluatable requirement gave wrong error: `{:?}`", obligation)
}
}
}

Expand Down Expand Up @@ -762,6 +772,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.tcx.report_object_safety_error(span, did,
violations)
}

ConstEvalFailure(ref err) => {
if let const_val::ErrKind::TypeckError = err.kind {
return;
}
err.struct_error(self.tcx, span, "constant expression")
}
};
self.note_obligation_cause(&mut err, obligation);
err.emit();
Expand Down
25 changes: 24 additions & 1 deletion src/librustc/traits/fulfill.rs
Expand Up @@ -25,7 +25,7 @@ use super::{FulfillmentError, FulfillmentErrorCode};
use super::{ObligationCause, PredicateObligation, Obligation};
use super::project;
use super::select::SelectionContext;
use super::Unimplemented;
use super::{Unimplemented, ConstEvalFailure};

impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
type Predicate = ty::Predicate<'tcx>;
Expand Down Expand Up @@ -540,6 +540,29 @@ fn process_predicate<'a, 'gcx, 'tcx>(
}
}
}

ty::Predicate::ConstEvaluatable(def_id, substs) => {
match selcx.tcx().lift_to_global(&obligation.param_env) {
None => {
Ok(None)
}
Some(param_env) => {
match selcx.tcx().lift_to_global(&substs) {
None => {
pending_obligation.stalled_on = substs.types().collect();
Ok(None)
}
Some(substs) => {
match selcx.tcx().at(obligation.cause.span)
.const_eval(param_env.and((def_id, substs))) {
Ok(_) => Ok(Some(vec![])),
Err(e) => Err(CodeSelectionError(ConstEvalFailure(e)))
}
}
}
}
}
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/librustc/traits/mod.rs
Expand Up @@ -17,6 +17,7 @@ pub use self::ObligationCauseCode::*;

use hir;
use hir::def_id::DefId;
use middle::const_val::ConstEvalErr;
use middle::region;
use middle::free_region::FreeRegionMap;
use ty::subst::Substs;
Expand Down Expand Up @@ -218,6 +219,7 @@ pub enum SelectionError<'tcx> {
ty::PolyTraitRef<'tcx>,
ty::error::TypeError<'tcx>),
TraitNotObjectSafe(DefId),
ConstEvalFailure(ConstEvalErr<'tcx>),
}

pub struct FulfillmentError<'tcx> {
Expand Down
6 changes: 4 additions & 2 deletions src/librustc/traits/object_safety.rs
Expand Up @@ -169,7 +169,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
ty::Predicate::RegionOutlives(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::Subtype(..) |
ty::Predicate::Equate(..) => {
ty::Predicate::Equate(..) |
ty::Predicate::ConstEvaluatable(..) => {
false
}
}
Expand Down Expand Up @@ -203,7 +204,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) => {
ty::Predicate::TypeOutlives(..) |
ty::Predicate::ConstEvaluatable(..) => {
false
}
}
Expand Down
40 changes: 37 additions & 3 deletions src/librustc/traits/project.rs
Expand Up @@ -27,10 +27,11 @@ use super::util;
use hir::def_id::DefId;
use infer::{InferCtxt, InferOk};
use infer::type_variable::TypeVariableOrigin;
use middle::const_val::ConstVal;
use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
use syntax::ast;
use syntax::symbol::Symbol;
use ty::subst::Subst;
use ty::subst::{Subst, Substs};
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
use ty::fold::{TypeFoldable, TypeFolder};
use util::common::FN_OUTPUT_NAME;
Expand Down Expand Up @@ -260,7 +261,7 @@ impl<'a, 'b, 'gcx, 'tcx> AssociatedTypeNormalizer<'a, 'b, 'gcx, 'tcx> {
fn fold<T:TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
let value = self.selcx.infcx().resolve_type_vars_if_possible(value);

if !value.has_projection_types() {
if !value.has_projections() {
value.clone()
} else {
value.fold_with(self)
Expand Down Expand Up @@ -332,6 +333,39 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
}
}
}

fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
if let ConstVal::Unevaluated(def_id, substs) = constant.val {
if substs.needs_infer() {
let identity_substs = Substs::identity_for_item(self.tcx(), def_id);
let data = self.param_env.and((def_id, identity_substs));
match self.tcx().lift_to_global(&data) {
Some(data) => {
match self.tcx().const_eval(data) {
Ok(evaluated) => {
let evaluated = evaluated.subst(self.tcx(), substs);
return self.fold_const(evaluated);
}
Err(_) => {}
}
}
None => {}
}
} else {
let data = self.param_env.and((def_id, substs));
match self.tcx().lift_to_global(&data) {
Some(data) => {
match self.tcx().const_eval(data) {
Ok(evaluated) => return self.fold_const(evaluated),
Err(_) => {}
}
}
None => {}
}
}
}
constant
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -520,7 +554,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
depth,
obligations);

let result = if projected_ty.has_projection_types() {
let result = if projected_ty.has_projections() {
let mut normalizer = AssociatedTypeNormalizer::new(selcx,
param_env,
cause,
Expand Down
15 changes: 15 additions & 0 deletions src/librustc/traits/select.rs
Expand Up @@ -732,6 +732,21 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}
}
}

ty::Predicate::ConstEvaluatable(def_id, substs) => {
match self.tcx().lift_to_global(&(obligation.param_env, substs)) {
Some((param_env, substs)) => {
match self.tcx().const_eval(param_env.and((def_id, substs))) {
Ok(_) => EvaluatedToOk,
Err(_) => EvaluatedToErr
}
}
None => {
// Inference variables still left in param_env or substs.
EvaluatedToAmbig
}
}
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/librustc/traits/structural_impls.rs
Expand Up @@ -173,6 +173,9 @@ impl<'a, 'tcx> Lift<'tcx> for traits::SelectionError<'a> {
super::TraitNotObjectSafe(def_id) => {
Some(super::TraitNotObjectSafe(def_id))
}
super::ConstEvalFailure(ref err) => {
tcx.lift(err).map(super::ConstEvalFailure)
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/traits/trans/mod.rs
Expand Up @@ -120,7 +120,7 @@ impl<'a, 'gcx> AssociatedTypeNormalizer<'a, 'gcx> {
}

fn fold<T:TypeFoldable<'gcx>>(&mut self, value: &T) -> T {
if !value.has_projection_types() {
if !value.has_projections() {
value.clone()
} else {
value.fold_with(self)
Expand All @@ -134,7 +134,7 @@ impl<'a, 'gcx> TypeFolder<'gcx, 'gcx> for AssociatedTypeNormalizer<'a, 'gcx> {
}

fn fold_ty(&mut self, ty: Ty<'gcx>) -> Ty<'gcx> {
if !ty.has_projection_types() {
if !ty.has_projections() {
ty
} else {
self.tcx.trans_trait_caches.project_cache.memoize(ty, || {
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/traits/util.rs
Expand Up @@ -48,6 +48,9 @@ fn anonymize_predicate<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,

ty::Predicate::Subtype(ref data) =>
ty::Predicate::Subtype(tcx.anonymize_late_bound_regions(data)),

ty::Predicate::ConstEvaluatable(def_id, substs) =>
ty::Predicate::ConstEvaluatable(def_id, substs),
}
}

Expand Down Expand Up @@ -175,6 +178,10 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
ty::Predicate::ClosureKind(..) => {
// Nothing to elaborate when waiting for a closure's kind to be inferred.
}
ty::Predicate::ConstEvaluatable(..) => {
// Currently, we do not elaborate const-evaluatable
// predicates.
}

ty::Predicate::RegionOutlives(..) => {
// Nothing to elaborate from `'a: 'b`.
Expand Down

0 comments on commit 74349fa

Please sign in to comment.