Skip to content

Commit

Permalink
Auto merge of rust-lang#74949 - oli-obk:validate_const_eval_raw, r=Ra…
Browse files Browse the repository at this point in the history
…lfJung

Validate constants during `const_eval_raw`

This PR implements the groundwork for rust-lang#72396

* constants are now validated during `const_eval_raw`
* to prevent cycle errors, we do not validate references to statics anymore beyond the fact that they are not dangling
* the `const_eval` query ICEs if used on `static` items
* as a side effect promoteds are now evaluated to `ConstValue::Scalar` again (since they are just a reference to the actual promoted allocation in most cases).
  • Loading branch information
bors committed Sep 20, 2020
2 parents 10b3595 + 34785fc commit 5e449b9
Show file tree
Hide file tree
Showing 52 changed files with 296 additions and 369 deletions.
7 changes: 2 additions & 5 deletions compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::Node;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
read_target_uint, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer,
read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer,
};
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::{self, Instance, Ty};
Expand Down Expand Up @@ -85,10 +85,7 @@ pub fn codegen_static_initializer(
cx: &CodegenCx<'ll, 'tcx>,
def_id: DefId,
) -> Result<(&'ll Value, &'tcx Allocation), ErrorHandled> {
let alloc = match cx.tcx.const_eval_poly(def_id)? {
ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => alloc,
val => bug!("static const eval returned {:#?}", val),
};
let alloc = cx.tcx.eval_static_initializer(def_id)?;
Ok((const_alloc_to_llvm(cx, alloc), alloc))
}

Expand Down
26 changes: 7 additions & 19 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rustc_ast as ast;
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{AllocId, ConstValue, Pointer, Scalar};
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::AssertKind;
use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt};
use rustc_middle::ty::print::with_no_trimmed_paths;
Expand Down Expand Up @@ -867,24 +867,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let ty = constant.literal.ty;
let size = bx.layout_of(ty).size;
let scalar = match const_value {
// Promoted constants are evaluated into a ByRef instead of a Scalar,
// but we want the scalar value here.
ConstValue::ByRef { alloc, offset } => {
let ptr = Pointer::new(AllocId(0), offset);
alloc
.read_scalar(&bx, ptr, size)
.and_then(|s| s.check_init())
.unwrap_or_else(|e| {
bx.tcx().sess.span_err(
span,
&format!("Could not evaluate asm const: {}", e),
);

// We are erroring out, just emit a dummy constant.
Scalar::from_u64(0)
})
}
_ => span_bug!(span, "expected ByRef for promoted asm const"),
ConstValue::Scalar(s) => s,
_ => span_bug!(
span,
"expected Scalar for promoted asm const, but got {:#?}",
const_value
),
};
let value = scalar.assert_bits(size);
let string = match ty.kind() {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
use rustc_middle::mir;
use rustc_middle::mir::interpret::ConstEvalResult;
use rustc_middle::mir::interpret::EvalToConstValueResult;
use rustc_middle::traits::select;
use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric};
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
Expand Down Expand Up @@ -1542,7 +1542,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
substs: SubstsRef<'tcx>,
promoted: Option<mir::Promoted>,
span: Option<Span>,
) -> ConstEvalResult<'tcx> {
) -> EvalToConstValueResult<'tcx> {
let mut original_values = OriginalQueryValues::default();
let canonical = self.canonicalize_query(&(param_env, substs), &mut original_values);

Expand Down
16 changes: 7 additions & 9 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1474,21 +1474,19 @@ declare_lint_pass!(
UnusedBrokenConst => []
);

fn check_const(cx: &LateContext<'_>, body_id: hir::BodyId) {
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
// trigger the query once for all constants since that will already report the errors
// FIXME: Use ensure here
let _ = cx.tcx.const_eval_poly(def_id);
}

impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
match it.kind {
hir::ItemKind::Const(_, body_id) => {
check_const(cx, body_id);
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
// trigger the query once for all constants since that will already report the errors
// FIXME: Use ensure here
let _ = cx.tcx.const_eval_poly(def_id);
}
hir::ItemKind::Static(_, _, body_id) => {
check_const(cx, body_id);
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
// FIXME: Use ensure here
let _ = cx.tcx.eval_static_initializer(def_id);
}
_ => {}
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/mir/interpret/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{AllocId, Pointer, RawConst, Scalar};
use super::{AllocId, ConstAlloc, Pointer, Scalar};

use crate::mir::interpret::ConstValue;
use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
Expand Down Expand Up @@ -27,8 +27,8 @@ CloneTypeFoldableAndLiftImpls! {
ErrorHandled,
}

pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;

pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_middle/src/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,12 @@ use crate::ty::subst::GenericArgKind;
use crate::ty::{self, Instance, Ty, TyCtxt};

pub use self::error::{
struct_error, CheckInAllocMsg, ConstEvalRawResult, ConstEvalResult, ErrorHandled, InterpError,
InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, ResourceExhaustionInfo,
UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult,
InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType,
ResourceExhaustionInfo, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
};

pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit};
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMaybeUninit};

pub use self::allocation::{Allocation, AllocationExtra, InitMask, Relocations};

Expand Down
16 changes: 8 additions & 8 deletions compiler/rustc_middle/src/mir/interpret/queries.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{ConstEvalResult, ErrorHandled, GlobalId};
use super::{ErrorHandled, EvalToConstValueResult, GlobalId};

use crate::mir;
use crate::ty::subst::{InternalSubsts, SubstsRef};
Expand All @@ -10,7 +10,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Evaluates a constant without providing any substitutions. This is useful to evaluate consts
/// that can't take any generic arguments like statics, const items or enum discriminants. If a
/// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned.
pub fn const_eval_poly(self, def_id: DefId) -> ConstEvalResult<'tcx> {
pub fn const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx> {
// In some situations def_id will have substitutions within scope, but they aren't allowed
// to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions
// into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are
Expand Down Expand Up @@ -38,7 +38,7 @@ impl<'tcx> TyCtxt<'tcx> {
substs: SubstsRef<'tcx>,
promoted: Option<mir::Promoted>,
span: Option<Span>,
) -> ConstEvalResult<'tcx> {
) -> EvalToConstValueResult<'tcx> {
match ty::Instance::resolve_opt_const_arg(self, param_env, def, substs) {
Ok(Some(instance)) => {
let cid = GlobalId { instance, promoted };
Expand All @@ -54,7 +54,7 @@ impl<'tcx> TyCtxt<'tcx> {
param_env: ty::ParamEnv<'tcx>,
instance: ty::Instance<'tcx>,
span: Option<Span>,
) -> ConstEvalResult<'tcx> {
) -> EvalToConstValueResult<'tcx> {
self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span)
}

Expand All @@ -64,14 +64,14 @@ impl<'tcx> TyCtxt<'tcx> {
param_env: ty::ParamEnv<'tcx>,
cid: GlobalId<'tcx>,
span: Option<Span>,
) -> ConstEvalResult<'tcx> {
) -> EvalToConstValueResult<'tcx> {
// Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
// improve caching of queries.
let inputs = self.erase_regions(&param_env.and(cid));
if let Some(span) = span {
self.at(span).const_eval_validated(inputs)
self.at(span).eval_to_const_value_raw(inputs)
} else {
self.const_eval_validated(inputs)
self.eval_to_const_value_raw(inputs)
}
}

Expand All @@ -94,7 +94,7 @@ impl<'tcx> TyCtxt<'tcx> {
param_env: ty::ParamEnv<'tcx>,
) -> Result<&'tcx mir::Allocation, ErrorHandled> {
trace!("eval_to_allocation: Need to compute {:?}", gid);
let raw_const = self.const_eval_raw(param_env.and(gid))?;
let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
}
}
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/mir/interpret/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ use crate::ty::{ParamEnv, Ty, TyCtxt};

use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};

/// Represents the result of a raw const operation, pre-validation.
/// Represents the result of const evaluation via the `eval_to_allocation` query.
#[derive(Clone, HashStable)]
pub struct RawConst<'tcx> {
pub struct ConstAlloc<'tcx> {
// the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
// (so you can use `AllocMap::unwrap_memory`).
pub alloc_id: AllocId,
Expand Down
27 changes: 11 additions & 16 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,32 +707,27 @@ rustc_queries! {
}

Other {
/// Evaluates a constant without running sanity checks.
/// Evaluates a constant and returns the computed allocation.
///
/// **Do not use this** outside const eval. Const eval uses this to break query cycles
/// during validation. Please add a comment to every use site explaining why using
/// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable
/// form to be used outside of const eval.
query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
-> ConstEvalRawResult<'tcx> {
/// **Do not use this** directly, use the `tcx.eval_static_initializer` wrapper.
query eval_to_allocation_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
-> EvalToAllocationRawResult<'tcx> {
desc { |tcx|
"const-evaluating `{}`",
"const-evaluating + checking `{}`",
key.value.display(tcx)
}
}

/// Results of evaluating const items or constants embedded in
/// other items (such as enum variant explicit discriminants).
///
/// In contrast to `const_eval_raw` this performs some validation on the constant, and
/// returns a proper constant that is usable by the rest of the compiler.
/// Evaluates const items or anonymous constants
/// (such as enum variant explicit discriminants or array lengths)
/// into a representation suitable for the type system and const generics.
///
/// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`,
/// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`.
query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
-> ConstEvalResult<'tcx> {
query eval_to_const_value_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
-> EvalToConstValueResult<'tcx> {
desc { |tcx|
"const-evaluating + checking `{}`",
"simplifying constant for the type system `{}`",
key.value.display(tcx)
}
cache_on_disk_if(_, opt_result) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLife
use crate::middle::stability::{self, DeprecationEntry};
use crate::mir;
use crate::mir::interpret::GlobalId;
use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue};
use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult};
use crate::mir::interpret::{LitToConstError, LitToConstInput};
use crate::mir::mono::CodegenUnit;
use crate::traits::query::{
Expand Down
Loading

0 comments on commit 5e449b9

Please sign in to comment.