Skip to content

Commit

Permalink
Promote Refs to constants instead of static
Browse files Browse the repository at this point in the history
  • Loading branch information
spastorino authored and oli-obk committed Jan 10, 2020
1 parent a59abfa commit 1688719
Show file tree
Hide file tree
Showing 53 changed files with 447 additions and 223 deletions.
7 changes: 6 additions & 1 deletion src/librustc/mir/interpret/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,16 @@ impl<'tcx> TyCtxt<'tcx> {
param_env: ty::ParamEnv<'tcx>,
def_id: DefId,
substs: SubstsRef<'tcx>,
promoted: Option<mir::Promoted>,
span: Option<Span>,
) -> ConstEvalResult<'tcx> {
let instance = ty::Instance::resolve(self, param_env, def_id, substs);
if let Some(instance) = instance {
self.const_eval_instance(param_env, instance, span)
if let Some(promoted) = promoted {
self.const_eval_promoted(instance, promoted)
} else {
self.const_eval_instance(param_env, instance, span)
}
} else {
Err(ErrorHandled::TooGeneric)
}
Expand Down
11 changes: 11 additions & 0 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,16 @@ pub struct Body<'tcx> {

/// A span representing this MIR, for error reporting.
pub span: Span,

/// The user may be writing e.g. &[(SOME_CELL, 42)][i].1 and this would get promoted, because
/// we'd statically know that no thing with interior mutability will ever be available to the
/// user without some serious unsafe code. Now this means that our promoted is actually
/// &[(SOME_CELL, 42)] and the MIR using it will do the &promoted[i].1 projection because the
/// index may be a runtime value. Such a promoted value is illegal because it has reachable
/// interior mutability. This flag just makes this situation very obvious where the previous
/// implementation without the flag hid this situation silently.
/// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components.
pub ignore_interior_mut_in_const_validation: bool,
}

impl<'tcx> Body<'tcx> {
Expand Down Expand Up @@ -202,6 +212,7 @@ impl<'tcx> Body<'tcx> {
spread_arg: None,
var_debug_info,
span,
ignore_interior_mut_in_const_validation: false,
control_flow_destroyed,
}
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
obligation.param_env,
def_id,
substs,
None,
Some(obligation.cause.span),
) {
Ok(_) => ProcessResult::Changed(vec![]),
Expand Down
9 changes: 7 additions & 2 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -802,8 +802,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

ty::Predicate::ConstEvaluatable(def_id, substs) => {
if !(obligation.param_env, substs).has_local_value() {
match self.tcx().const_eval_resolve(obligation.param_env, def_id, substs, None)
{
match self.tcx().const_eval_resolve(
obligation.param_env,
def_id,
substs,
None,
None,
) {
Ok(_) => Ok(EvaluatedToOk),
Err(_) => Ok(EvaluatedToErr),
}
Expand Down
4 changes: 3 additions & 1 deletion src/librustc/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
/// Pushes the obligations required for an array length to be WF
/// into `self.out`.
fn compute_array_len(&mut self, constant: ty::Const<'tcx>) {
if let ty::ConstKind::Unevaluated(def_id, substs) = constant.val {
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.val {
assert!(promoted.is_none());

let obligations = self.nominal_obligations(def_id, substs);
self.out.extend(obligations);

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ impl FlagComputation {
fn add_const(&mut self, c: &ty::Const<'_>) {
self.add_ty(c.ty);
match c.val {
ty::ConstKind::Unevaluated(_, substs) => {
ty::ConstKind::Unevaluated(_, substs, _) => {
self.add_substs(substs);
self.add_flags(TypeFlags::HAS_PROJECTION);
}
Expand Down
36 changes: 22 additions & 14 deletions src/librustc/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,23 +841,31 @@ pub trait PrettyPrinter<'tcx>:

match (ct.val, &ct.ty.kind) {
(_, ty::FnDef(did, substs)) => p!(print_value_path(*did, substs)),
(ty::ConstKind::Unevaluated(did, substs), _) => match self.tcx().def_kind(did) {
Some(DefKind::Static) | Some(DefKind::Const) | Some(DefKind::AssocConst) => {
p!(print_value_path(did, substs))
}
_ => {
if did.is_local() {
let span = self.tcx().def_span(did);
if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) {
p!(write("{}", snip))
} else {
p!(write("_: "), print(ct.ty))
(ty::ConstKind::Unevaluated(did, substs, promoted), _) => {
if let Some(promoted) = promoted {
p!(print_value_path(did, substs));
p!(write("::{:?}", promoted));
} else {
match self.tcx().def_kind(did) {
Some(DefKind::Static)
| Some(DefKind::Const)
| Some(DefKind::AssocConst) => p!(print_value_path(did, substs)),
_ => {
if did.is_local() {
let span = self.tcx().def_span(did);
if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span)
{
p!(write("{}", snip))
} else {
p!(write("_: "), print(ct.ty))
}
} else {
p!(write("_: "), print(ct.ty))
}
}
} else {
p!(write("_: "), print(ct.ty))
}
}
},
}
(ty::ConstKind::Infer(..), _) => p!(write("_: "), print(ct.ty)),
(ty::ConstKind::Param(ParamConst { name, .. }), _) => p!(write("{}", name)),
(ty::ConstKind::Value(value), _) => return self.pretty_print_const_value(value, ct.ty),
Expand Down
8 changes: 4 additions & 4 deletions src/librustc/ty/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,12 +568,12 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(

// FIXME(const_generics): this is wrong, as it is a projection
(
ty::ConstKind::Unevaluated(a_def_id, a_substs),
ty::ConstKind::Unevaluated(b_def_id, b_substs),
) if a_def_id == b_def_id => {
ty::ConstKind::Unevaluated(a_def_id, a_substs, a_promoted),
ty::ConstKind::Unevaluated(b_def_id, b_substs, b_promoted),
) if a_def_id == b_def_id && a_promoted == b_promoted => {
let substs =
relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?;
Ok(ty::ConstKind::Unevaluated(a_def_id, &substs))
Ok(ty::ConstKind::Unevaluated(a_def_id, &substs, a_promoted))
}
_ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))),
};
Expand Down
6 changes: 3 additions & 3 deletions src/librustc/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1037,8 +1037,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> {
match *self {
ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)),
ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)),
ty::ConstKind::Unevaluated(did, substs) => {
ty::ConstKind::Unevaluated(did, substs.fold_with(folder))
ty::ConstKind::Unevaluated(did, substs, promoted) => {
ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted)
}
ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(..) => {
*self
Expand All @@ -1050,7 +1050,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> {
match *self {
ty::ConstKind::Infer(ic) => ic.visit_with(visitor),
ty::ConstKind::Param(p) => p.visit_with(visitor),
ty::ConstKind::Unevaluated(_, substs) => substs.visit_with(visitor),
ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor),
ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
false
}
Expand Down
13 changes: 7 additions & 6 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::infer::canonical::Canonical;
use crate::middle::region;
use crate::mir::interpret::ConstValue;
use crate::mir::interpret::Scalar;
use crate::mir::Promoted;
use crate::ty::layout::VariantIdx;
use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef};
use crate::ty::{self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable};
Expand Down Expand Up @@ -2375,7 +2376,7 @@ impl<'tcx> Const<'tcx> {

#[inline]
pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> {
let try_const_eval = |did, param_env: ParamEnv<'tcx>, substs| {
let try_const_eval = |did, param_env: ParamEnv<'tcx>, substs, promoted| {
let param_env_and_substs = param_env.with_reveal_all().and(substs);

// Avoid querying `tcx.const_eval(...)` with any e.g. inference vars.
Expand All @@ -2387,11 +2388,11 @@ impl<'tcx> Const<'tcx> {

// try to resolve e.g. associated constants to their definition on an impl, and then
// evaluate the const.
tcx.const_eval_resolve(param_env, did, substs, None).ok()
tcx.const_eval_resolve(param_env, did, substs, promoted, None).ok()
};

match self.val {
ConstKind::Unevaluated(did, substs) => {
ConstKind::Unevaluated(did, substs, promoted) => {
// HACK(eddyb) when substs contain e.g. inference variables,
// attempt using identity substs instead, that will succeed
// when the expression doesn't depend on any parameters.
Expand All @@ -2401,12 +2402,12 @@ impl<'tcx> Const<'tcx> {
let identity_substs = InternalSubsts::identity_for_item(tcx, did);
// The `ParamEnv` needs to match the `identity_substs`.
let identity_param_env = tcx.param_env(did);
match try_const_eval(did, identity_param_env, identity_substs) {
match try_const_eval(did, identity_param_env, identity_substs, promoted) {
Some(ct) => ct.subst(tcx, substs),
None => self,
}
} else {
try_const_eval(did, param_env, substs).unwrap_or(self)
try_const_eval(did, param_env, substs, promoted).unwrap_or(self)
}
}
_ => self,
Expand Down Expand Up @@ -2470,7 +2471,7 @@ pub enum ConstKind<'tcx> {

/// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
/// variants when the code is monomorphic enough for that.
Unevaluated(DefId, SubstsRef<'tcx>),
Unevaluated(DefId, SubstsRef<'tcx>, Option<Promoted>),

/// Used to hold computed value.
Value(ConstValue<'tcx>),
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/ty/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) {
| ty::Bound(..)
| ty::Foreign(..) => {}
ty::Array(ty, len) => {
if let ty::ConstKind::Unevaluated(_, substs) = len.val {
if let ty::ConstKind::Unevaluated(_, substs, promoted) = len.val {
assert!(promoted.is_none());
stack.extend(substs.types().rev());
}
stack.push(len.ty);
Expand Down
9 changes: 6 additions & 3 deletions src/librustc_codegen_ssa/mir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// use `get_static` to get at their id.
// FIXME(oli-obk): can we unify this somehow, maybe by making const eval of statics
// always produce `&STATIC`. This may also simplify how const eval works with statics.
ty::ConstKind::Unevaluated(def_id, substs) if self.cx.tcx().is_static(def_id) => {
ty::ConstKind::Unevaluated(def_id, substs, promoted)
if self.cx.tcx().is_static(def_id) =>
{
assert!(promoted.is_none());
assert!(substs.is_empty(), "we don't support generic statics yet");
let static_ = bx.get_static(def_id);
// we treat operands referring to statics as if they were `&STATIC` instead
Expand All @@ -40,11 +43,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
constant: &mir::Constant<'tcx>,
) -> Result<&'tcx ty::Const<'tcx>, ErrorHandled> {
match constant.literal.val {
ty::ConstKind::Unevaluated(def_id, substs) => {
ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
let substs = self.monomorphize(&substs);
self.cx
.tcx()
.const_eval_resolve(ty::ParamEnv::reveal_all(), def_id, substs, None)
.const_eval_resolve(ty::ParamEnv::reveal_all(), def_id, substs, promoted, None)
.map_err(|err| {
self.cx
.tcx()
Expand Down
59 changes: 48 additions & 11 deletions src/librustc_mir/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,17 +310,54 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
);
}
} else {
if let ty::ConstKind::Unevaluated(def_id, substs) = constant.literal.val {
if let Err(terr) = self.cx.fully_perform_op(
location.to_locations(),
ConstraintCategory::Boring,
self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
constant.literal.ty,
def_id,
UserSubsts { substs, user_self_ty: None },
)),
) {
span_mirbug!(self, constant, "bad constant type {:?} ({:?})", constant, terr);
if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.literal.val {
if let Some(promoted) = promoted {
let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>,
promoted: &ReadOnlyBodyAndCache<'_, 'tcx>,
ty,
san_ty| {
if let Err(terr) = verifier.cx.eq_types(
san_ty,
ty,
location.to_locations(),
ConstraintCategory::Boring,
) {
span_mirbug!(
verifier,
promoted,
"bad promoted type ({:?}: {:?}): {:?}",
ty,
san_ty,
terr
);
};
};

if !self.errors_reported {
let promoted_body = self.promoted[promoted];
self.sanitize_promoted(promoted_body, location);

let promoted_ty = promoted_body.return_ty();
check_err(self, &promoted_body, ty, promoted_ty);
}
} else {
if let Err(terr) = self.cx.fully_perform_op(
location.to_locations(),
ConstraintCategory::Boring,
self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
constant.literal.ty,
def_id,
UserSubsts { substs, user_self_ty: None },
)),
) {
span_mirbug!(
self,
constant,
"bad constant type {:?} ({:?})",
constant,
terr
);
}
}
}
if let ty::FnDef(def_id, substs) = constant.literal.ty.kind {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub(crate) fn const_caller_location<'tcx>(

let loc_ty = tcx.caller_location_ty();
let loc_place = ecx.alloc_caller_location(file, line, col);
intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
intern_const_alloc_recursive(&mut ecx, None, loc_place, false).unwrap();
let loc_const = ty::Const {
ty: loc_ty,
val: ty::ConstKind::Value(ConstValue::Scalar(loc_place.ptr.into())),
Expand Down
18 changes: 14 additions & 4 deletions src/librustc_mir/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ fn eval_body_using_ecx<'mir, 'tcx>(
ecx.run()?;

// Intern the result
intern_const_alloc_recursive(ecx, tcx.static_mutability(cid.instance.def_id()), ret)?;
intern_const_alloc_recursive(
ecx,
tcx.static_mutability(cid.instance.def_id()),
ret,
body.ignore_interior_mut_in_const_validation,
)?;

debug!("eval_body_using_ecx done: {:?}", *ret);
Ok(ret)
Expand Down Expand Up @@ -171,9 +176,14 @@ fn validate_and_turn_into_const<'tcx>(
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
let val = (|| {
let mplace = ecx.raw_const_to_mplace(constant)?;
let mut ref_tracking = RefTracking::new(mplace);
while let Some((mplace, path)) = ref_tracking.todo.pop() {
ecx.validate_operand(mplace.into(), path, Some(&mut ref_tracking))?;

// FIXME do not validate promoteds until a decision on
// https://github.com/rust-lang/rust/issues/67465 is made
if cid.promoted.is_none() {
let mut ref_tracking = RefTracking::new(mplace);
while let Some((mplace, path)) = ref_tracking.todo.pop() {
ecx.validate_operand(mplace.into(), path, Some(&mut ref_tracking))?;
}
}
// Now that we validated, turn this into a proper constant.
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
Expand Down
Loading

0 comments on commit 1688719

Please sign in to comment.