Skip to content

Implement (but don't use) valtree and refactor in preparation of use #82936

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

Merged
merged 27 commits into from
Mar 17, 2021
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a4fbac1
Implement valtree
oli-obk Feb 22, 2021
0fe4f38
Intern valtree field vector
oli-obk Feb 22, 2021
858216c
Add fallible Scalar to ScalarInt conversion method
oli-obk Feb 23, 2021
09f7f91
Add convenience conversion methods for ScalarInt
oli-obk Feb 23, 2021
019dba0
Resolve a FIXME around type equality checks in Relate for constants
oli-obk Mar 2, 2021
4d917fa
Reduce destructuring and re-interning where possible
oli-obk Mar 2, 2021
5e8a89b
Reduce the duplication in the relation logic for constants
oli-obk Mar 2, 2021
b729cc9
Pull out ConstValue relating into its own function
oli-obk Mar 2, 2021
0bb367e
Split pretty printer logic for scalar int and scalar ptr
oli-obk Mar 2, 2021
d5eec65
Use the explicit error constant instead of fake dummies
oli-obk Mar 8, 2021
914df2a
Add `ty` helper function for mir constants
oli-obk Mar 8, 2021
3ecde6f
Directly intern allocations so that we can write a `Lift` impl for them
oli-obk Mar 8, 2021
3127a9c
Prepare mir::Constant for ty::Const only supporting valtrees
oli-obk Mar 8, 2021
20f7379
Replace a custom lift method with a Lift impl
oli-obk Mar 10, 2021
6ca1d87
Visit `mir::Constant::user_ty` for completeness.
oli-obk Mar 12, 2021
c51749a
We won't support trait object constants in type level constants for t…
oli-obk Mar 12, 2021
11ddd22
Run rustfmt
oli-obk Mar 12, 2021
c30c1be
s/ConstantSource/ConstantKind/
oli-obk Mar 15, 2021
bc8641a
Document valtree
oli-obk Mar 15, 2021
f646c1e
Explain why we do not allow const_to_valtree to read from statics
oli-obk Mar 15, 2021
c01c494
Explain how we encode enums at the encoding site
oli-obk Mar 15, 2021
0dd5a1b
Explain pointer and dyn Trait handling in const_to_valtree
oli-obk Mar 15, 2021
9f407ae
Do not expose fallible `to_int` operation on `Scalar`.
oli-obk Mar 15, 2021
1ffd21a
Pacify tidy
oli-obk Mar 15, 2021
f0997fa
Update compiler/rustc_mir/src/const_eval/mod.rs
oli-obk Mar 16, 2021
5b9bd90
Update compiler/rustc_middle/src/ty/consts/valtree.rs
oli-obk Mar 16, 2021
c4d564c
Update compiler/rustc_middle/src/ty/consts/valtree.rs
oli-obk Mar 16, 2021
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
30 changes: 17 additions & 13 deletions compiler/rustc_codegen_cranelift/src/constant.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::interpret::{
read_target_uint, AllocId, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer, Scalar,
};
use rustc_middle::ty::{Const, ConstKind};
use rustc_middle::ty::ConstKind;

use cranelift_codegen::ir::GlobalValueData;
use cranelift_module::*;
@@ -39,7 +39,10 @@ impl ConstantCx {
pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool {
let mut all_constants_ok = true;
for constant in &fx.mir.required_consts {
let const_ = fx.monomorphize(constant.literal);
let const_ = match fx.monomorphize(constant.literal) {
ConstantKind::Ty(ct) => ct,
ConstantKind::Val(..) => continue,
};
match const_.val {
ConstKind::Value(_) => {}
ConstKind::Unevaluated(def, ref substs, promoted) => {
@@ -113,19 +116,17 @@ pub(crate) fn codegen_constant<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
constant: &Constant<'tcx>,
) -> CValue<'tcx> {
let const_ = fx.monomorphize(constant.literal);
let const_ = match fx.monomorphize(constant.literal) {
ConstantKind::Ty(ct) => ct,
ConstantKind::Val(val, ty) => return codegen_const_value(fx, val, ty),
};
let const_val = match const_.val {
ConstKind::Value(const_val) => const_val,
ConstKind::Unevaluated(def, ref substs, promoted) if fx.tcx.is_static(def.did) => {
assert!(substs.is_empty());
assert!(promoted.is_none());

return codegen_static_ref(
fx,
def.did,
fx.layout_of(fx.monomorphize(&constant.literal.ty)),
)
.to_cvalue(fx);
return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty)).to_cvalue(fx);
}
ConstKind::Unevaluated(def, ref substs, promoted) => {
match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None) {
@@ -422,11 +423,14 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
pub(crate) fn mir_operand_get_const_val<'tcx>(
fx: &FunctionCx<'_, '_, 'tcx>,
operand: &Operand<'tcx>,
) -> Option<&'tcx Const<'tcx>> {
) -> Option<ConstValue<'tcx>> {
match operand {
Operand::Copy(_) | Operand::Move(_) => None,
Operand::Constant(const_) => {
Some(fx.monomorphize(const_.literal).eval(fx.tcx, ParamEnv::reveal_all()))
}
Operand::Constant(const_) => match const_.literal {
ConstantKind::Ty(const_) => {
fx.monomorphize(const_).eval(fx.tcx, ParamEnv::reveal_all()).val.try_to_value()
}
ConstantKind::Val(val, _) => Some(val),
},
}
}
6 changes: 3 additions & 3 deletions compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
};
llvm.x86.sse2.cmp.ps | llvm.x86.sse2.cmp.pd, (c x, c y, o kind) {
let kind_const = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const");
let flt_cc = match kind_const.val.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind_const)) {
let flt_cc = match kind_const.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind_const)) {
0 => FloatCC::Equal,
1 => FloatCC::LessThan,
2 => FloatCC::LessThanOrEqual,
@@ -84,7 +84,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
llvm.x86.sse2.psrli.d, (c a, o imm8) {
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const");
simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| {
let res_lane = match imm8.val.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
let res_lane = match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
_ => fx.bcx.ins().iconst(types::I32, 0),
};
@@ -94,7 +94,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>(
llvm.x86.sse2.pslli.d, (c a, o imm8) {
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const");
simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| {
let res_lane = match imm8.val.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
let res_lane = match imm8.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) {
imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
_ => fx.bcx.ins().iconst(types::I32, 0),
};
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
Original file line number Diff line number Diff line change
@@ -85,8 +85,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
use rustc_middle::mir::interpret::*;
let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");

let idx_bytes = match idx_const.val {
ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) => {
let idx_bytes = match idx_const {
ConstValue::ByRef { alloc, offset } => {
let ptr = Pointer::new(AllocId(0 /* dummy */), offset);
let size = Size::from_bytes(4 * u64::from(ret_lane_count) /* size_of([u32; ret_lane_count]) */);
alloc.get_bytes(fx, ptr, size).unwrap()
@@ -130,7 +130,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
);
};

let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
if idx >= lane_count.into() {
fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
@@ -159,7 +159,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
return;
};

let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
if idx >= lane_count.into() {
fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/analyze.rs
Original file line number Diff line number Diff line change
@@ -231,7 +231,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
let check = match terminator.kind {
mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => {
match *c.literal.ty.kind() {
match *c.ty().kind() {
ty::FnDef(did, _) => Some((did, args)),
_ => None,
}
12 changes: 4 additions & 8 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
@@ -635,12 +635,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") {
if let mir::Operand::Constant(constant) = arg {
let c = self.eval_mir_constant(constant);
let (llval, ty) = self.simd_shuffle_indices(
&bx,
constant.span,
constant.literal.ty,
c,
);
let (llval, ty) =
self.simd_shuffle_indices(&bx, constant.span, constant.ty(), c);
return OperandRef {
val: Immediate(llval),
layout: bx.layout_of(ty),
@@ -830,7 +826,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let const_value = self
.eval_mir_constant(constant)
.unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved"));
let ty = constant.literal.ty;
let ty = constant.ty();
let size = bx.layout_of(ty).size;
let scalar = match const_value {
ConstValue::Scalar(s) => s,
@@ -864,7 +860,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
mir::InlineAsmOperand::SymFn { ref value } => {
let literal = self.monomorphize(value.literal);
if let ty::FnDef(def_id, substs) = *literal.ty.kind() {
if let ty::FnDef(def_id, substs) = *literal.ty().kind() {
let instance = ty::Instance::resolve_for_fn_ptr(
bx.tcx(),
ty::ParamEnv::reveal_all(),
9 changes: 7 additions & 2 deletions compiler/rustc_codegen_ssa/src/mir/constant.rs
Original file line number Diff line number Diff line change
@@ -16,15 +16,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
constant: &mir::Constant<'tcx>,
) -> Result<OperandRef<'tcx, Bx::Value>, ErrorHandled> {
let val = self.eval_mir_constant(constant)?;
let ty = self.monomorphize(constant.literal.ty);
let ty = self.monomorphize(constant.ty());
Ok(OperandRef::from_const(bx, val, ty))
}

pub fn eval_mir_constant(
&self,
constant: &mir::Constant<'tcx>,
) -> Result<ConstValue<'tcx>, ErrorHandled> {
match self.monomorphize(constant.literal).val {
let ct = self.monomorphize(constant.literal);
let ct = match ct {
mir::ConstantKind::Ty(ct) => ct,
mir::ConstantKind::Val(val, _) => return Ok(val),
};
match ct.val {
ty::ConstKind::Unevaluated(def, substs, promoted) => self
.cx
.tcx()
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Original file line number Diff line number Diff line change
@@ -372,7 +372,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
(var_ty, var_kind)
}
mir::VarDebugInfoContents::Const(c) => {
let ty = self.monomorphize(c.literal.ty);
let ty = self.monomorphize(c.ty());
(ty, VariableKind::LocalVariable)
}
};
48 changes: 38 additions & 10 deletions compiler/rustc_middle/src/mir/interpret/value.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::convert::TryFrom;
use std::convert::{TryFrom, TryInto};
use std::fmt;

use rustc_apfloat::{
@@ -8,12 +8,12 @@ use rustc_apfloat::{
use rustc_macros::HashStable;
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};

use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
use crate::ty::{Lift, ParamEnv, ScalarInt, Ty, TyCtxt};

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

/// Represents the result of const evaluation via the `eval_to_allocation` query.
#[derive(Clone, HashStable, TyEncodable, TyDecodable, Debug)]
#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
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`).
@@ -47,6 +47,27 @@ pub enum ConstValue<'tcx> {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(ConstValue<'_>, 32);

impl From<Scalar> for ConstValue<'tcx> {
fn from(s: Scalar) -> Self {
Self::Scalar(s)
}
}

impl<'a, 'tcx> Lift<'tcx> for ConstValue<'a> {
type Lifted = ConstValue<'tcx>;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<ConstValue<'tcx>> {
Some(match self {
ConstValue::Scalar(s) => ConstValue::Scalar(s),
ConstValue::Slice { data, start, end } => {
ConstValue::Slice { data: tcx.lift(data)?, start, end }
}
ConstValue::ByRef { alloc, offset } => {
ConstValue::ByRef { alloc: tcx.lift(alloc)?, offset }
}
})
}
}

impl<'tcx> ConstValue<'tcx> {
#[inline]
pub fn try_to_scalar(&self) -> Option<Scalar> {
@@ -56,20 +77,20 @@ impl<'tcx> ConstValue<'tcx> {
}
}

pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
Some(self.try_to_scalar()?.assert_int())
}

pub fn try_to_bits(&self, size: Size) -> Option<u128> {
self.try_to_scalar()?.to_bits(size).ok()
self.try_to_scalar_int()?.to_bits(size).ok()
}

pub fn try_to_bool(&self) -> Option<bool> {
match self.try_to_bits(Size::from_bytes(1))? {
0 => Some(false),
1 => Some(true),
_ => None,
}
self.try_to_scalar_int()?.try_into().ok()
}

pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
Some(self.try_to_bits(tcx.data_layout.pointer_size)? as u64)
self.try_to_scalar_int()?.try_to_machine_usize(tcx).ok()
}

pub fn try_to_bits_for_ty(
@@ -503,6 +524,13 @@ impl<Tag> From<Pointer<Tag>> for Scalar<Tag> {
}
}

impl<Tag> From<ScalarInt> for Scalar<Tag> {
#[inline(always)]
fn from(ptr: ScalarInt) -> Self {
Scalar::Int(ptr)
}
}

#[derive(Clone, Copy, Eq, PartialEq, TyEncodable, TyDecodable, HashStable, Hash)]
pub enum ScalarMaybeUninit<Tag = ()> {
Scalar(Scalar<Tag>),
134 changes: 126 additions & 8 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -11,12 +11,12 @@ use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::subst::{Subst, SubstsRef};
use crate::ty::{self, List, Ty, TyCtxt};
use crate::ty::{AdtDef, InstanceDef, Region, UserTypeAnnotationIndex};
use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, GeneratorKind};
use rustc_target::abi::VariantIdx;
use rustc_target::abi::{Size, VariantIdx};

use polonius_engine::Atom;
pub use rustc_ast::Mutability;
@@ -30,6 +30,7 @@ use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass;
use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::{self, Debug, Display, Formatter, Write};
use std::ops::{ControlFlow, Index, IndexMut};
use std::slice;
@@ -2032,7 +2033,7 @@ impl<'tcx> Operand<'tcx> {
Operand::Constant(box Constant {
span,
user_ty: None,
literal: ty::Const::zero_sized(tcx, ty),
literal: ConstantKind::Ty(ty::Const::zero_sized(tcx, ty)),
})
}

@@ -2063,7 +2064,7 @@ impl<'tcx> Operand<'tcx> {
Operand::Constant(box Constant {
span,
user_ty: None,
literal: ty::Const::from_scalar(tcx, val, ty),
literal: ConstantKind::Val(val.into(), ty),
})
}

@@ -2405,12 +2406,21 @@ pub struct Constant<'tcx> {
/// Needed for NLL to impose user-given type constraints.
pub user_ty: Option<UserTypeAnnotationIndex>,

pub literal: &'tcx ty::Const<'tcx>,
pub literal: ConstantKind<'tcx>,
}

#[derive(Clone, Copy, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
pub enum ConstantKind<'tcx> {
/// This constant came from the type system
Ty(&'tcx ty::Const<'tcx>),
/// This constant cannot go back into the type system, as it represents
/// something the type system cannot handle (e.g. pointers).
Val(interpret::ConstValue<'tcx>, Ty<'tcx>),
}

impl Constant<'tcx> {
pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
match self.literal.val.try_to_scalar() {
match self.literal.const_for_ty()?.val.try_to_scalar() {
Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) {
GlobalAlloc::Static(def_id) => {
assert!(!tcx.is_thread_local_static(def_id));
@@ -2421,6 +2431,94 @@ impl Constant<'tcx> {
_ => None,
}
}
pub fn ty(&self) -> Ty<'tcx> {
self.literal.ty()
}
}

impl From<&'tcx ty::Const<'tcx>> for ConstantKind<'tcx> {
fn from(ct: &'tcx ty::Const<'tcx>) -> Self {
Self::Ty(ct)
}
}

impl ConstantKind<'tcx> {
/// Returns `None` if the constant is not trivially safe for use in the type system.
pub fn const_for_ty(&self) -> Option<&'tcx ty::Const<'tcx>> {
match self {
ConstantKind::Ty(c) => Some(c),
ConstantKind::Val(..) => None,
}
}

pub fn ty(&self) -> Ty<'tcx> {
match self {
ConstantKind::Ty(c) => c.ty,
ConstantKind::Val(_, ty) => ty,
}
}

#[inline]
pub fn try_to_value(self) -> Option<interpret::ConstValue<'tcx>> {
match self {
ConstantKind::Ty(c) => c.val.try_to_value(),
ConstantKind::Val(val, _) => Some(val),
}
}

#[inline]
pub fn try_to_scalar(self) -> Option<Scalar> {
self.try_to_value()?.try_to_scalar()
}

#[inline]
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
Some(self.try_to_value()?.try_to_scalar()?.assert_int())
}

#[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_scalar_int()?.to_bits(size).ok()
}

#[inline]
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_scalar_int()?.try_into().ok()
}

#[inline]
pub fn try_eval_bits(
&self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<u128> {
match self {
Self::Ty(ct) => ct.try_eval_bits(tcx, param_env, ty),
Self::Val(val, t) => {
assert_eq!(*t, ty);
let size =
tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
val.try_to_bits(size)
}
}
}

#[inline]
pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
match self {
Self::Ty(ct) => ct.try_eval_bool(tcx, param_env),
Self::Val(val, _) => val.try_to_bool(),
}
}

#[inline]
pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<u64> {
match self {
Self::Ty(ct) => ct.try_eval_usize(tcx, param_env),
Self::Val(val, _) => val.try_to_machine_usize(tcx),
}
}
}

/// A collection of projections into user types.
@@ -2606,11 +2704,14 @@ impl<'tcx> Debug for Constant<'tcx> {

impl<'tcx> Display for Constant<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match self.literal.ty.kind() {
match self.ty().kind() {
ty::FnDef(..) => {}
_ => write!(fmt, "const ")?,
}
pretty_print_const(self.literal, fmt, true)
match self.literal {
ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt, true),
}
}
}

@@ -2629,6 +2730,23 @@ fn pretty_print_const(
})
}

fn pretty_print_const_value(
val: interpret::ConstValue<'tcx>,
ty: Ty<'tcx>,
fmt: &mut Formatter<'_>,
print_types: bool,
) -> fmt::Result {
use crate::ty::print::PrettyPrinter;
ty::tls::with(|tcx| {
let val = tcx.lift(val).unwrap();
let ty = tcx.lift(ty).unwrap();
let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS);
cx.print_alloc_ids = true;
cx.pretty_print_const_value(val, ty, print_types)?;
Ok(())
})
}

impl<'tcx> graph::DirectedGraph for Body<'tcx> {
type Node = BasicBlock;
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/tcx.rs
Original file line number Diff line number Diff line change
@@ -227,7 +227,7 @@ impl<'tcx> Operand<'tcx> {
{
match self {
&Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
&Operand::Constant(ref c) => c.literal.ty,
&Operand::Constant(ref c) => c.literal.ty(),
}
}
}
19 changes: 18 additions & 1 deletion compiler/rustc_middle/src/mir/type_foldable.rs
Original file line number Diff line number Diff line change
@@ -342,6 +342,23 @@ impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> {
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.literal.visit_with(visitor)
self.literal.visit_with(visitor)?;
self.user_ty.visit_with(visitor)
}
}

impl<'tcx> TypeFoldable<'tcx> for ConstantKind<'tcx> {
fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
match self {
ConstantKind::Ty(c) => ConstantKind::Ty(c.fold_with(folder)),
ConstantKind::Val(v, t) => ConstantKind::Val(v, t.fold_with(folder)),
}
}

fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
match *self {
ConstantKind::Ty(c) => c.visit_with(visitor),
ConstantKind::Val(_, t) => t.visit_with(visitor),
}
}
}
5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/mir/visit.rs
Original file line number Diff line number Diff line change
@@ -871,7 +871,10 @@ macro_rules! make_mir_visitor {

self.visit_span(span);
drop(user_ty); // no visit method for this
self.visit_const(literal, location);
match literal {
ConstantKind::Ty(ct) => self.visit_const(ct, location),
ConstantKind::Val(_, t) => self.visit_ty(t, TyContext::Location(location)),
}
}

fn super_span(&mut self, _span: & $($mutability)? Span) {
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
@@ -785,6 +785,14 @@ rustc_queries! {
cache_on_disk_if { true }
}

/// Convert an evaluated constant to a type level constant or
/// return `None` if that is not possible.
query const_to_valtree(
key: ty::ParamEnvAnd<'tcx, ConstAlloc<'tcx>>
) -> Option<ty::ValTree<'tcx>> {
desc { "destructure constant" }
}

/// Destructure a constant ADT or array into its variant index and its
/// field values.
query destructure_const(
10 changes: 10 additions & 0 deletions compiler/rustc_middle/src/ty/codec.rs
Original file line number Diff line number Diff line change
@@ -333,6 +333,16 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::Const<'tcx> {
}
}

impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] {
fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
Ok(decoder.tcx().arena.alloc_from_iter(
(0..decoder.read_usize()?)
.map(|_| Decodable::decode(decoder))
.collect::<Result<Vec<_>, _>>()?,
))
}
}

impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for Allocation {
fn decode(decoder: &mut D) -> Result<&'tcx Self, D::Error> {
Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?))
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
@@ -10,9 +10,11 @@ use rustc_macros::HashStable;

mod int;
mod kind;
mod valtree;

pub use int::*;
pub use kind::*;
pub use valtree::*;

/// Typed constant value.
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
19 changes: 19 additions & 0 deletions compiler/rustc_middle/src/ty/consts/int.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@ use rustc_target::abi::{Size, TargetDataLayout};
use std::convert::{TryFrom, TryInto};
use std::fmt;

use crate::ty::TyCtxt;

#[derive(Copy, Clone)]
/// A type for representing any integer. Only used for printing.
pub struct ConstInt {
@@ -239,6 +241,11 @@ impl ScalarInt {
Err(self.size())
}
}

#[inline]
pub fn try_to_machine_usize(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
}
}

macro_rules! from {
@@ -277,6 +284,18 @@ macro_rules! try_from {
from!(u8, u16, u32, u64, u128, bool);
try_from!(u8, u16, u32, u64, u128);

impl TryFrom<ScalarInt> for bool {
type Error = Size;
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.to_bits(Size::from_bytes(1)).and_then(|u| match u {
0 => Ok(false),
1 => Ok(true),
_ => Err(Size::from_bytes(1)),
})
}
}

impl From<char> for ScalarInt {
#[inline]
fn from(c: char) -> Self {
13 changes: 11 additions & 2 deletions compiler/rustc_middle/src/ty/consts/kind.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::convert::TryInto;

use crate::mir::interpret::ConstValue;
use crate::mir::interpret::Scalar;
use crate::mir::Promoted;
@@ -9,6 +11,8 @@ use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
use rustc_target::abi::Size;

use super::ScalarInt;

/// Represents a constant in Rust.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
#[derive(HashStable)]
@@ -51,14 +55,19 @@ impl<'tcx> ConstKind<'tcx> {
self.try_to_value()?.try_to_scalar()
}

#[inline]
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
Some(self.try_to_value()?.try_to_scalar()?.assert_int())
}

#[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_value()?.try_to_bits(size)
self.try_to_scalar_int()?.to_bits(size).ok()
}

#[inline]
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_value()?.try_to_bool()
self.try_to_scalar_int()?.try_into().ok()
}

#[inline]
34 changes: 34 additions & 0 deletions compiler/rustc_middle/src/ty/consts/valtree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use super::ScalarInt;
use rustc_macros::HashStable;

#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
#[derive(HashStable)]
/// This datastructure is used to represent the value of constants used in the type system.
///
/// We explicitly choose a different datastructure from the way values are processed within
/// CTFE, as in the type system equal values (according to their `PartialEq`) must also have
/// equal representation (`==` on the rustc data structure, e.g. `ValTree`) and vice versa.
/// Since CTFE uses `AllocId` to represent pointers, it often happens that two different
/// `AllocId`s point to equal values. So we may end up with different representations for
/// two constants whose value is `&42`. Furthermore any kind of struct that has padding will
/// have arbitrary values within that padding, even if the values of the struct are the same.
///
/// `ValTree` does not have this problem with representation, as it only contains integers or
/// lists of (nested) `ValTree`.
pub enum ValTree<'tcx> {
/// ZSTs, integers, `bool`, `char` are represented as scalars.
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
/// of these types have the same representation.
Leaf(ScalarInt),
/// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
/// listing their fields' values in order.
/// Enums are represented by storing their discriminant as a field, followed by all
/// the fields of the variant.
Branch(&'tcx [ValTree<'tcx>]),
}

impl ValTree<'tcx> {
pub fn zst() -> Self {
Self::Branch(&[])
}
}
35 changes: 29 additions & 6 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
@@ -94,6 +94,8 @@ pub struct CtxtInterners<'tcx> {
projs: InternedSet<'tcx, List<ProjectionKind>>,
place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>,
const_: InternedSet<'tcx, Const<'tcx>>,
/// Const allocations.
allocation: InternedSet<'tcx, Allocation>,
}

impl<'tcx> CtxtInterners<'tcx> {
@@ -111,6 +113,7 @@ impl<'tcx> CtxtInterners<'tcx> {
projs: Default::default(),
place_elems: Default::default(),
const_: Default::default(),
allocation: Default::default(),
}
}

@@ -1013,9 +1016,6 @@ pub struct GlobalCtxt<'tcx> {
/// `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes
const_stability_interner: ShardedHashMap<&'tcx attr::ConstStability, ()>,

/// Stores the value of constants (and deduplicates the actual memory)
allocation_interner: ShardedHashMap<&'tcx Allocation, ()>,

/// Stores memory for globals (statics/consts).
pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,

@@ -1058,7 +1058,10 @@ impl<'tcx> TyCtxt<'tcx> {
}

pub fn intern_const_alloc(self, alloc: Allocation) -> &'tcx Allocation {
self.allocation_interner.intern(alloc, |alloc| self.arena.alloc(alloc))
self.interners
.allocation
.intern(alloc, |alloc| Interned(self.interners.arena.alloc(alloc)))
.0
}

/// Allocates a read-only byte or string literal for `mir::interpret`.
@@ -1174,7 +1177,6 @@ impl<'tcx> TyCtxt<'tcx> {
layout_interner: Default::default(),
stability_interner: Default::default(),
const_stability_interner: Default::default(),
allocation_interner: Default::default(),
alloc_map: Lock::new(interpret::AllocMap::new()),
output_filenames: Arc::new(output_filenames.clone()),
}
@@ -1610,6 +1612,7 @@ macro_rules! nop_list_lift {
nop_lift! {type_; Ty<'a> => Ty<'tcx>}
nop_lift! {region; Region<'a> => Region<'tcx>}
nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>}
nop_lift! {allocation; &'a Allocation => &'tcx Allocation}
nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>}

nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>}
@@ -1900,7 +1903,7 @@ impl<'tcx> TyCtxt<'tcx> {
"Const Stability interner: #{}",
self.0.const_stability_interner.len()
)?;
writeln!(fmt, "Allocation interner: #{}", self.0.allocation_interner.len())?;
writeln!(fmt, "Allocation interner: #{}", self.0.interners.allocation.len())?;
writeln!(fmt, "Layout interner: #{}", self.0.layout_interner.len())?;

Ok(())
@@ -2001,6 +2004,26 @@ impl<'tcx> Borrow<Const<'tcx>> for Interned<'tcx, Const<'tcx>> {
}
}

impl<'tcx> Borrow<Allocation> for Interned<'tcx, Allocation> {
fn borrow<'a>(&'a self) -> &'a Allocation {
&self.0
}
}

impl<'tcx> PartialEq for Interned<'tcx, Allocation> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}

impl<'tcx> Eq for Interned<'tcx, Allocation> {}

impl<'tcx> Hash for Interned<'tcx, Allocation> {
fn hash<H: Hasher>(&self, s: &mut H) {
self.0.hash(s)
}
}

macro_rules! direct_interners {
($($name:ident: $method:ident($ty:ty),)+) => {
$(impl<'tcx> PartialEq for Interned<'tcx, $ty> {
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ pub use rustc_type_ir::*;

pub use self::binding::BindingMode;
pub use self::binding::BindingMode::*;
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt};
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, ValTree};
pub use self::context::{
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt,
113 changes: 67 additions & 46 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
@@ -956,32 +956,40 @@ pub trait PrettyPrinter<'tcx>:
}

fn pretty_print_const_scalar(
mut self,
self,
scalar: Scalar,
ty: Ty<'tcx>,
print_ty: bool,
) -> Result<Self::Const, Self::Error> {
match scalar {
Scalar::Ptr(ptr) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty),
Scalar::Int(int) => self.pretty_print_const_scalar_int(int, ty, print_ty),
}
}

fn pretty_print_const_scalar_ptr(
mut self,
ptr: Pointer,
ty: Ty<'tcx>,
print_ty: bool,
) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self);

match (scalar, &ty.kind()) {
match ty.kind() {
// Byte strings (&[u8; N])
(
Scalar::Ptr(ptr),
ty::Ref(
_,
ty::TyS {
kind:
ty::Array(
ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. },
ty::Const {
val: ty::ConstKind::Value(ConstValue::Scalar(int)),
..
},
),
..
},
_,
),
ty::Ref(
_,
ty::TyS {
kind:
ty::Array(
ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. },
ty::Const {
val: ty::ConstKind::Value(ConstValue::Scalar(int)), ..
},
),
..
},
_,
) => match self.tcx().get_global_alloc(ptr.alloc_id) {
Some(GlobalAlloc::Memory(alloc)) => {
let bytes = int.assert_bits(self.tcx().data_layout.pointer_size);
@@ -997,28 +1005,59 @@ pub trait PrettyPrinter<'tcx>:
Some(GlobalAlloc::Function(_)) => p!("<function>"),
None => p!("<dangling pointer>"),
},
ty::FnPtr(_) => {
// FIXME: We should probably have a helper method to share code with the "Byte strings"
// printing above (which also has to handle pointers to all sorts of things).
match self.tcx().get_global_alloc(ptr.alloc_id) {
Some(GlobalAlloc::Function(instance)) => {
self = self.typed_value(
|this| this.print_value_path(instance.def_id(), instance.substs),
|this| this.print_type(ty),
" as ",
)?;
}
_ => self = self.pretty_print_const_pointer(ptr, ty, print_ty)?,
}
}
// Any pointer values not covered by a branch above
_ => {
self = self.pretty_print_const_pointer(ptr, ty, print_ty)?;
}
}
Ok(self)
}

fn pretty_print_const_scalar_int(
mut self,
int: ScalarInt,
ty: Ty<'tcx>,
print_ty: bool,
) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self);

match ty.kind() {
// Bool
(Scalar::Int(int), ty::Bool) if int == ScalarInt::FALSE => p!("false"),
(Scalar::Int(int), ty::Bool) if int == ScalarInt::TRUE => p!("true"),
ty::Bool if int == ScalarInt::FALSE => p!("false"),
ty::Bool if int == ScalarInt::TRUE => p!("true"),
// Float
(Scalar::Int(int), ty::Float(ty::FloatTy::F32)) => {
ty::Float(ty::FloatTy::F32) => {
p!(write("{}f32", Single::try_from(int).unwrap()))
}
(Scalar::Int(int), ty::Float(ty::FloatTy::F64)) => {
ty::Float(ty::FloatTy::F64) => {
p!(write("{}f64", Double::try_from(int).unwrap()))
}
// Int
(Scalar::Int(int), ty::Uint(_) | ty::Int(_)) => {
ty::Uint(_) | ty::Int(_) => {
let int =
ConstInt::new(int, matches!(ty.kind(), ty::Int(_)), ty.is_ptr_sized_integral());
if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) }
}
// Char
(Scalar::Int(int), ty::Char) if char::try_from(int).is_ok() => {
ty::Char if char::try_from(int).is_ok() => {
p!(write("{:?}", char::try_from(int).unwrap()))
}
// Raw pointers
(Scalar::Int(int), ty::RawPtr(_) | ty::FnPtr(_)) => {
ty::RawPtr(_) | ty::FnPtr(_) => {
let data = int.assert_bits(self.tcx().data_layout.pointer_size);
self = self.typed_value(
|mut this| {
@@ -1029,26 +1068,12 @@ pub trait PrettyPrinter<'tcx>:
" as ",
)?;
}
(Scalar::Ptr(ptr), ty::FnPtr(_)) => {
// FIXME: We should probably have a helper method to share code with the "Byte strings"
// printing above (which also has to handle pointers to all sorts of things).
match self.tcx().get_global_alloc(ptr.alloc_id) {
Some(GlobalAlloc::Function(instance)) => {
self = self.typed_value(
|this| this.print_value_path(instance.def_id(), instance.substs),
|this| this.print_type(ty),
" as ",
)?;
}
_ => self = self.pretty_print_const_pointer(ptr, ty, print_ty)?,
}
}
// For function type zsts just printing the path is enough
(Scalar::Int(int), ty::FnDef(d, s)) if int == ScalarInt::ZST => {
ty::FnDef(d, s) if int == ScalarInt::ZST => {
p!(print_value_path(*d, s))
}
// Nontrivial types with scalar bit representation
(Scalar::Int(int), _) => {
_ => {
let print = |mut this: Self| {
if int.size() == Size::ZERO {
write!(this, "transmute(())")?;
@@ -1063,10 +1088,6 @@ pub trait PrettyPrinter<'tcx>:
print(self)?
};
}
// Any pointer values not covered by a branch above
(Scalar::Ptr(p), _) => {
self = self.pretty_print_const_pointer(p, ty, print_ty)?;
}
}
Ok(self)
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/query/mod.rs
Original file line number Diff line number Diff line change
@@ -14,8 +14,8 @@ 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::{ConstAlloc, LitToConstError, LitToConstInput};
use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult};
use crate::mir::interpret::{LitToConstError, LitToConstInput};
use crate::mir::mono::CodegenUnit;
use crate::traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
159 changes: 74 additions & 85 deletions compiler/rustc_middle/src/ty/relate.rs
Original file line number Diff line number Diff line change
@@ -4,13 +4,12 @@
//! types or regions but can be other things. Examples of type relations are
//! subtyping, type equality, etc.
use crate::mir::interpret::{get_slice_bytes, ConstValue};
use crate::mir::interpret::{get_slice_bytes, ConstValue, GlobalAlloc, Scalar};
use crate::ty::error::{ExpectedFound, TypeError};
use crate::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_hir as ast;
use rustc_hir::def_id::DefId;
use rustc_span::DUMMY_SP;
use rustc_target::spec::abi;
use std::iter;

@@ -498,104 +497,41 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
let tcx = relation.tcx();

let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env()).val;
// FIXME(oli-obk): once const generics can have generic types, this assertion
// will likely get triggered. Move to `normalize_erasing_regions` at that point.
assert_eq!(
tcx.erase_regions(a.ty),
tcx.erase_regions(b.ty),
"cannot relate constants of different types"
);

// FIXME(eddyb) doesn't look like everything below checks that `a.ty == b.ty`.
// We could probably always assert it early, as const generic parameters
// are not allowed to depend on other generic parameters, i.e. are concrete.
// (although there could be normalization differences)
let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env());
let a = eagerly_eval(a);
let b = eagerly_eval(b);

// Currently, the values that can be unified are primitive types,
// and those that derive both `PartialEq` and `Eq`, corresponding
// to structural-match types.
let new_const_val = match (eagerly_eval(a), eagerly_eval(b)) {
let is_match = match (a.val, b.val) {
(ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
// The caller should handle these cases!
bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b)
}

(ty::ConstKind::Error(d), _) | (_, ty::ConstKind::Error(d)) => Ok(ty::ConstKind::Error(d)),
(ty::ConstKind::Error(_), _) => return Ok(a),
(_, ty::ConstKind::Error(_)) => return Ok(b),

(ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => {
return Ok(a);
}
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) if p1 == p2 => {
return Ok(a);
}
(ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) => a_p.index == b_p.index,
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => {
let new_val = match (a_val, b_val) {
(ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => {
if a_val == b_val {
Ok(ConstValue::Scalar(a_val))
} else if let ty::FnPtr(_) = a.ty.kind() {
let a_instance = tcx.global_alloc(a_val.assert_ptr().alloc_id).unwrap_fn();
let b_instance = tcx.global_alloc(b_val.assert_ptr().alloc_id).unwrap_fn();
if a_instance == b_instance {
Ok(ConstValue::Scalar(a_val))
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
}

(ConstValue::Slice { .. }, ConstValue::Slice { .. }) => {
let a_bytes = get_slice_bytes(&tcx, a_val);
let b_bytes = get_slice_bytes(&tcx, b_val);
if a_bytes == b_bytes {
Ok(a_val)
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
}

(ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => {
match a.ty.kind() {
ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => {
let a_destructured = tcx.destructure_const(relation.param_env().and(a));
let b_destructured = tcx.destructure_const(relation.param_env().and(b));

// Both the variant and each field have to be equal.
if a_destructured.variant == b_destructured.variant {
for (a_field, b_field) in
a_destructured.fields.iter().zip(b_destructured.fields.iter())
{
relation.consts(a_field, b_field)?;
}

Ok(a_val)
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
}
// FIXME(const_generics): There are probably some `TyKind`s
// which should be handled here.
_ => {
tcx.sess.delay_span_bug(
DUMMY_SP,
&format!("unexpected consts: a: {:?}, b: {:?}", a, b),
);
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
}
}

_ => Err(TypeError::ConstMismatch(expected_found(relation, a, b))),
};

new_val.map(ty::ConstKind::Value)
check_const_value_eq(relation, a_val, b_val, a, b)?
}

(
ty::ConstKind::Unevaluated(a_def, a_substs, None),
ty::ConstKind::Unevaluated(b_def, b_substs, None),
) if tcx.features().const_evaluatable_checked && !relation.visit_ct_substs() => {
if tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) {
Ok(a.val)
} else {
Err(TypeError::ConstMismatch(expected_found(relation, a, b)))
}
tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs)))
}

// While this is slightly incorrect, it shouldn't matter for `min_const_generics`
@@ -607,11 +543,64 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
) if a_def == b_def && a_promoted == b_promoted => {
let substs =
relation.relate_with_variance(ty::Variance::Invariant, a_substs, b_substs)?;
Ok(ty::ConstKind::Unevaluated(a_def, substs, a_promoted))
return Ok(tcx.mk_const(ty::Const {
val: ty::ConstKind::Unevaluated(a_def, substs, a_promoted),
ty: a.ty,
}));
}
_ => Err(TypeError::ConstMismatch(expected_found(relation, a, b))),
_ => false,
};
new_const_val.map(|val| tcx.mk_const(ty::Const { val, ty: a.ty }))
if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(relation, a, b))) }
}

fn check_const_value_eq<R: TypeRelation<'tcx>>(
relation: &mut R,
a_val: ConstValue<'tcx>,
b_val: ConstValue<'tcx>,
// FIXME(oli-obk): these arguments should go away with valtrees
a: &'tcx ty::Const<'tcx>,
b: &'tcx ty::Const<'tcx>,
// FIXME(oli-obk): this should just be `bool` with valtrees
) -> RelateResult<'tcx, bool> {
let tcx = relation.tcx();
Ok(match (a_val, b_val) {
(ConstValue::Scalar(Scalar::Int(a_val)), ConstValue::Scalar(Scalar::Int(b_val))) => {
a_val == b_val
}
(ConstValue::Scalar(Scalar::Ptr(a_val)), ConstValue::Scalar(Scalar::Ptr(b_val))) => {
a_val == b_val
|| match (tcx.global_alloc(a_val.alloc_id), tcx.global_alloc(b_val.alloc_id)) {
(GlobalAlloc::Function(a_instance), GlobalAlloc::Function(b_instance)) => {
a_instance == b_instance
}
_ => false,
}
}

(ConstValue::Slice { .. }, ConstValue::Slice { .. }) => {
get_slice_bytes(&tcx, a_val) == get_slice_bytes(&tcx, b_val)
}

(ConstValue::ByRef { .. }, ConstValue::ByRef { .. }) => {
let a_destructured = tcx.destructure_const(relation.param_env().and(a));
let b_destructured = tcx.destructure_const(relation.param_env().and(b));

// Both the variant and each field have to be equal.
if a_destructured.variant == b_destructured.variant {
for (a_field, b_field) in
a_destructured.fields.iter().zip(b_destructured.fields.iter())
{
relation.consts(a_field, b_field)?;
}

true
} else {
false
}
}

_ => false,
})
}

impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::Binder<ty::ExistentialPredicate<'tcx>>> {
4 changes: 2 additions & 2 deletions compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
Original file line number Diff line number Diff line change
@@ -81,12 +81,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let terminator = self.body[location.block].terminator();
debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
if let TerminatorKind::Call {
func: Operand::Constant(box Constant { literal: ty::Const { ty: const_ty, .. }, .. }),
func: Operand::Constant(box Constant { literal, .. }),
args,
..
} = &terminator.kind
{
if let ty::FnDef(id, _) = *const_ty.kind() {
if let ty::FnDef(id, _) = *literal.ty().kind() {
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
let closure = match args.first() {
23 changes: 16 additions & 7 deletions compiler/rustc_mir/src/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
@@ -282,7 +282,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {

fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
self.super_constant(constant, location);
let ty = self.sanitize_type(constant, constant.literal.ty);
let ty = self.sanitize_type(constant, constant.literal.ty());

self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| {
let live_region_vid =
@@ -296,7 +296,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {

if let Some(annotation_index) = constant.user_ty {
if let Err(terr) = self.cx.relate_type_and_user_type(
constant.literal.ty,
constant.literal.ty(),
ty::Variance::Invariant,
&UserTypeProjection { base: annotation_index, projs: vec![] },
location.to_locations(),
@@ -308,13 +308,22 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
constant,
"bad constant user type {:?} vs {:?}: {:?}",
annotation,
constant.literal.ty,
constant.literal.ty(),
terr,
);
}
} else {
let tcx = self.tcx();
if let ty::ConstKind::Unevaluated(def, substs, promoted) = constant.literal.val {
let maybe_uneval = match constant.literal {
ConstantKind::Ty(ct) => match ct.val {
ty::ConstKind::Unevaluated(def, substs, promoted) => {
Some((def, substs, promoted))
}
_ => None,
},
_ => None,
};
if let Some((def, substs, promoted)) = maybe_uneval {
if let Some(promoted) = promoted {
let check_err = |verifier: &mut TypeVerifier<'a, 'b, 'tcx>,
promoted: &Body<'tcx>,
@@ -349,7 +358,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
location.to_locations(),
ConstraintCategory::Boring,
self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(
constant.literal.ty,
constant.literal.ty(),
def.did,
UserSubsts { substs, user_self_ty: None },
)),
@@ -367,7 +376,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
let unnormalized_ty = tcx.type_of(static_def_id);
let locations = location.to_locations();
let normalized_ty = self.cx.normalize(unnormalized_ty, locations);
let literal_ty = constant.literal.ty.builtin_deref(true).unwrap().ty;
let literal_ty = constant.literal.ty().builtin_deref(true).unwrap().ty;

if let Err(terr) = self.cx.eq_types(
normalized_ty,
@@ -379,7 +388,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
}
}

if let ty::FnDef(def_id, substs) = *constant.literal.ty.kind() {
if let ty::FnDef(def_id, substs) = *constant.literal.ty().kind() {
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
self.cx.normalize_and_prove_instantiated_predicates(
instantiated_predicates,
101 changes: 99 additions & 2 deletions compiler/rustc_mir/src/const_eval/mod.rs
Original file line number Diff line number Diff line change
@@ -3,12 +3,15 @@
use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::mir;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{
mir::{self, interpret::ConstAlloc},
ty::ScalarInt,
};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar,
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
};

mod error;
@@ -35,6 +38,100 @@ pub(crate) fn const_caller_location(
ConstValue::Scalar(loc_place.ptr)
}

/// Convert an evaluated constant to a type level constant
pub(crate) fn const_to_valtree<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
raw: ConstAlloc<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
let ecx = mk_eval_cx(
tcx, DUMMY_SP, param_env,
// It is absolutely crucial for soundness that
// we do not read from static items or other mutable memory.
false,
);
let place = ecx.raw_const_to_mplace(raw).unwrap();
const_to_valtree_inner(&ecx, &place)
}

fn const_to_valtree_inner<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
let branches = |n, variant| {
let place = match variant {
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
None => *place,
};
let variant =
variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
let fields = (0..n).map(|i| {
let field = ecx.mplace_field(&place, i).unwrap();
const_to_valtree_inner(ecx, &field)
});
// For enums, we preped their variant index before the variant's fields so we can figure out
// the variant again when just seeing a valtree.
let branches = variant.into_iter().chain(fields);
Some(ty::ValTree::Branch(
ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?),
))
};
match place.layout.ty.kind() {
ty::FnDef(..) => Some(ty::ValTree::zst()),
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
let val = ecx.read_immediate(&place.into()).unwrap();
let val = val.to_scalar().unwrap();
Some(ty::ValTree::Leaf(val.assert_int()))
}

// Raw pointers are not allowed in type level constants, as we cannot properly test them for
// equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
// agree with runtime equality tests.
ty::FnPtr(_) | ty::RawPtr(_) => None,
ty::Ref(..) => unimplemented!("need to use deref_const"),

// Trait objects are not allowed in type level constants, as we have no concept for
// resolving their backing type, even if we can do that at const eval time. We may
// hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
// but it is unclear if this is useful.
ty::Dynamic(..) => None,

ty::Slice(_) | ty::Str => {
unimplemented!("need to find the backing data of the slice/str and recurse on that")
}
ty::Tuple(substs) => branches(substs.len(), None),
ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),

ty::Adt(def, _) => {
if def.variants.is_empty() {
bug!("uninhabited types should have errored and never gotten converted to valtree")
}

let variant = ecx.read_discriminant(&place.into()).unwrap().1;

branches(def.variants[variant].fields.len(), Some(variant))
}

ty::Never
| ty::Error(_)
| ty::Foreign(..)
| ty::Infer(ty::FreshIntTy(_))
| ty::Infer(ty::FreshFloatTy(_))
| ty::Projection(..)
| ty::Param(_)
| ty::Bound(..)
| ty::Placeholder(..)
// FIXME(oli-obk): we could look behind opaque types
| ty::Opaque(..)
| ty::Infer(_)
// FIXME(oli-obk): we can probably encode closures just like structs
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..) => None,
}
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
@@ -689,7 +689,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let span = const_.span;
let const_ =
self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal);
self.const_to_op(const_, None).map_err(|err| {
self.mir_const_to_op(&const_, None).map_err(|err| {
// If there was an error, set the span of the current frame to this constant.
// Avoiding doing this when evaluation succeeds.
self.frame_mut().loc = Err(span);
46 changes: 32 additions & 14 deletions compiler/rustc_mir/src/interpret/operand.rs
Original file line number Diff line number Diff line change
@@ -532,7 +532,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
// checked yet.
// * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
self.const_to_op(val, layout)?

self.mir_const_to_op(&val, layout)?
}
};
trace!("{:?}: {:?}", mir_op, *op);
@@ -556,28 +557,45 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
val: &ty::Const<'tcx>,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
let tag_scalar = |scalar| -> InterpResult<'tcx, _> {
Ok(match scalar {
Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?),
Scalar::Int(int) => Scalar::Int(int),
})
};
// Early-return cases.
let val_val = match val.val {
match val.val {
ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)),
ty::ConstKind::Unevaluated(def, substs, promoted) => {
let instance = self.resolve(def, substs)?;
return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into());
Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into())
}
ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => {
span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val)
}
ty::ConstKind::Value(val_val) => val_val,
};
ty::ConstKind::Value(val_val) => self.const_val_to_op(val_val, val.ty, layout),
}
}

crate fn mir_const_to_op(
&self,
val: &mir::ConstantKind<'tcx>,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
match val {
mir::ConstantKind::Ty(ct) => self.const_to_op(ct, layout),
mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, ty, None),
}
}

crate fn const_val_to_op(
&self,
val_val: ConstValue<'tcx>,
ty: Ty<'tcx>,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
// Other cases need layout.
let layout =
from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(val.ty))?;
let tag_scalar = |scalar| -> InterpResult<'tcx, _> {
Ok(match scalar {
Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?),
Scalar::Int(int) => Scalar::Int(int),
})
};
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
let op = match val_val {
ConstValue::ByRef { alloc, offset } => {
let id = self.tcx.create_memory_alloc(alloc);
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/interpret/place.rs
Original file line number Diff line number Diff line change
@@ -531,7 +531,7 @@ where
base.offset(from_offset, meta, layout, self)
}

pub(super) fn mplace_downcast(
pub(crate) fn mplace_downcast(
&self,
base: &MPlaceTy<'tcx, M::PointerTag>,
variant: VariantIdx,
4 changes: 4 additions & 0 deletions compiler/rustc_mir/src/lib.rs
Original file line number Diff line number Diff line change
@@ -63,6 +63,10 @@ pub fn provide(providers: &mut Providers) {
let (param_env, value) = param_env_and_value.into_parts();
const_eval::destructure_const(tcx, param_env, value)
};
providers.const_to_valtree = |tcx, param_env_and_value| {
let (param_env, raw) = param_env_and_value.into_parts();
const_eval::const_to_valtree(tcx, param_env, raw)
};
providers.deref_const = |tcx, param_env_and_value| {
let (param_env, value) = param_env_and_value.into_parts();
const_eval::deref_const(tcx, param_env, value)
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/monomorphize/collector.rs
Original file line number Diff line number Diff line change
@@ -684,7 +684,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
for op in operands {
match *op {
mir::InlineAsmOperand::SymFn { ref value } => {
let fn_ty = self.monomorphize(value.literal.ty);
let fn_ty = self.monomorphize(value.literal.ty());
visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output);
}
mir::InlineAsmOperand::SymStatic { def_id } => {
8 changes: 4 additions & 4 deletions compiler/rustc_mir/src/shim.rs
Original file line number Diff line number Diff line change
@@ -421,7 +421,7 @@ impl CloneShimBuilder<'tcx> {
let func = Operand::Constant(box Constant {
span: self.span,
user_ty: None,
literal: ty::Const::zero_sized(tcx, func_ty),
literal: ty::Const::zero_sized(tcx, func_ty).into(),
});

let ref_loc = self.make_place(
@@ -478,7 +478,7 @@ impl CloneShimBuilder<'tcx> {
box Constant {
span: self.span,
user_ty: None,
literal: ty::Const::from_usize(self.tcx, value),
literal: ty::Const::from_usize(self.tcx, value).into(),
}
}

@@ -509,7 +509,7 @@ impl CloneShimBuilder<'tcx> {
Rvalue::Use(Operand::Constant(box Constant {
span: self.span,
user_ty: None,
literal: len,
literal: len.into(),
})),
))),
];
@@ -768,7 +768,7 @@ fn build_call_shim<'tcx>(
Operand::Constant(box Constant {
span,
user_ty: None,
literal: ty::Const::zero_sized(tcx, ty),
literal: ty::Const::zero_sized(tcx, ty).into(),
}),
rcvr.into_iter().collect::<Vec<_>>(),
)
36 changes: 19 additions & 17 deletions compiler/rustc_mir/src/transform/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
@@ -246,25 +246,27 @@ where
};

// Check the qualifs of the value of `const` items.
if let ty::ConstKind::Unevaluated(def, _, promoted) = constant.literal.val {
assert!(promoted.is_none());
// Don't peek inside trait associated constants.
if cx.tcx.trait_of_item(def.did).is_none() {
let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did))
} else {
cx.tcx.at(constant.span).mir_const_qualif(def.did)
};

if !Q::in_qualifs(&qualifs) {
return false;
}
if let Some(ct) = constant.literal.const_for_ty() {
if let ty::ConstKind::Unevaluated(def, _, promoted) = ct.val {
assert!(promoted.is_none());
// Don't peek inside trait associated constants.
if cx.tcx.trait_of_item(def.did).is_none() {
let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
cx.tcx.at(constant.span).mir_const_qualif_const_arg((did, param_did))
} else {
cx.tcx.at(constant.span).mir_const_qualif(def.did)
};

if !Q::in_qualifs(&qualifs) {
return false;
}

// Just in case the type is more specific than
// the definition, e.g., impl associated const
// with type parameters, take it into account.
// Just in case the type is more specific than
// the definition, e.g., impl associated const
// with type parameters, take it into account.
}
}
}
// Otherwise use the qualifs of the type.
Q::in_any_value_of_ty(cx, constant.literal.ty)
Q::in_any_value_of_ty(cx, constant.literal.ty())
}
52 changes: 31 additions & 21 deletions compiler/rustc_mir/src/transform/const_prop.rs
Original file line number Diff line number Diff line change
@@ -13,9 +13,9 @@ use rustc_middle::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
use rustc_middle::mir::{
AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local, LocalDecl, LocalKind,
Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl,
LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData,
Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -482,18 +482,21 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None;
}

match self.ecx.const_to_op(c.literal, None) {
match self.ecx.mir_const_to_op(&c.literal, None) {
Ok(op) => Some(op),
Err(error) => {
let tcx = self.ecx.tcx.at(c.span);
let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
if let Some(lint_root) = self.lint_root(source_info) {
let lint_only = match c.literal.val {
// Promoteds must lint and not error as the user didn't ask for them
ConstKind::Unevaluated(_, _, Some(_)) => true,
// Out of backwards compatibility we cannot report hard errors in unused
// generic functions using associated constants of the generic parameters.
_ => c.literal.needs_subst(),
let lint_only = match c.literal {
ConstantKind::Ty(ct) => match ct.val {
// Promoteds must lint and not error as the user didn't ask for them
ConstKind::Unevaluated(_, _, Some(_)) => true,
// Out of backwards compatibility we cannot report hard errors in unused
// generic functions using associated constants of the generic parameters.
_ => c.literal.needs_subst(),
},
ConstantKind::Val(_, ty) => ty.needs_subst(),
};
if lint_only {
// Out of backwards compatibility we cannot report hard errors in unused
@@ -803,7 +806,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
Operand::Constant(Box::new(Constant {
span,
user_ty: None,
literal: ty::Const::from_scalar(self.tcx, scalar, ty),
literal: ty::Const::from_scalar(self.tcx, scalar, ty).into(),
}))
}

@@ -814,9 +817,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
source_info: SourceInfo,
) {
if let Rvalue::Use(Operand::Constant(c)) = rval {
if !matches!(c.literal.val, ConstKind::Unevaluated(..)) {
trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
return;
match c.literal {
ConstantKind::Ty(c) if matches!(c.val, ConstKind::Unevaluated(..)) => {}
_ => {
trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
return;
}
}
}

@@ -883,13 +889,17 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
*rval = Rvalue::Use(Operand::Constant(Box::new(Constant {
span: source_info.span,
user_ty: None,
literal: self.ecx.tcx.mk_const(ty::Const {
ty,
val: ty::ConstKind::Value(ConstValue::ByRef {
alloc,
offset: Size::ZERO,
}),
}),
literal: self
.ecx
.tcx
.mk_const(ty::Const {
ty,
val: ty::ConstKind::Value(ConstValue::ByRef {
alloc,
offset: Size::ZERO,
}),
})
.into(),
})));
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/elaborate_drops.rs
Original file line number Diff line number Diff line change
@@ -471,7 +471,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
Rvalue::Use(Operand::Constant(Box::new(Constant {
span,
user_ty: None,
literal: ty::Const::from_bool(self.tcx, val),
literal: ty::Const::from_bool(self.tcx, val).into(),
})))
}

2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/generator.rs
Original file line number Diff line number Diff line change
@@ -989,7 +989,7 @@ fn insert_panic_block<'tcx>(
cond: Operand::Constant(box Constant {
span: body.span,
user_ty: None,
literal: ty::Const::from_bool(tcx, false),
literal: ty::Const::from_bool(tcx, false).into(),
}),
expected: true,
msg: message,
9 changes: 6 additions & 3 deletions compiler/rustc_mir/src/transform/inline.rs
Original file line number Diff line number Diff line change
@@ -416,7 +416,7 @@ impl Inliner<'tcx> {

TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => {
if let ty::FnDef(def_id, substs) =
*callsite.callee.subst_mir(self.tcx, &f.literal.ty).kind()
*callsite.callee.subst_mir(self.tcx, &f.literal.ty()).kind()
{
let substs = self.tcx.normalize_erasing_regions(self.param_env, substs);
if let Ok(Some(instance)) =
@@ -637,8 +637,11 @@ impl Inliner<'tcx> {
// `required_consts`, here we may not only have `ConstKind::Unevaluated`
// because we are calling `subst_and_normalize_erasing_regions`.
caller_body.required_consts.extend(
callee_body.required_consts.iter().copied().filter(|&constant| {
matches!(constant.literal.val, ConstKind::Unevaluated(_, _, _))
callee_body.required_consts.iter().copied().filter(|&ct| {
match ct.literal.const_for_ty() {
Some(ct) => matches!(ct.val, ConstKind::Unevaluated(_, _, _)),
None => true,
}
}),
);
}
7 changes: 4 additions & 3 deletions compiler/rustc_mir/src/transform/instcombine.rs
Original file line number Diff line number Diff line change
@@ -79,7 +79,7 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {

fn try_eval_bool(&self, a: &Operand<'_>) -> Option<bool> {
let a = a.constant()?;
if a.literal.ty.is_bool() { a.literal.val.try_to_bool() } else { None }
if a.literal.ty().is_bool() { a.literal.try_to_bool() } else { None }
}

/// Transform "&(*a)" ==> "a".
@@ -110,12 +110,13 @@ impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {
fn combine_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
if let Rvalue::Len(ref place) = *rvalue {
let place_ty = place.ty(self.local_decls, self.tcx).ty;
if let ty::Array(_, len) = place_ty.kind() {
if let ty::Array(_, len) = *place_ty.kind() {
if !self.should_combine(source_info, rvalue) {
return;
}

let constant = Constant { span: source_info.span, literal: len, user_ty: None };
let constant =
Constant { span: source_info.span, literal: len.into(), user_ty: None };
*rvalue = Rvalue::Use(Operand::Constant(box constant));
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/lower_intrinsics.rs
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
Rvalue::Use(Operand::Constant(box Constant {
span: terminator.source_info.span,
user_ty: None,
literal: ty::Const::zero_sized(tcx, tcx.types.unit),
literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
})),
)),
});
4 changes: 2 additions & 2 deletions compiler/rustc_mir/src/transform/match_branches.rs
Original file line number Diff line number Diff line change
@@ -93,8 +93,8 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
StatementKind::Assign(box (lhs_f, Rvalue::Use(Operand::Constant(f_c)))),
StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))),
) if lhs_f == lhs_s
&& f_c.literal.ty.is_bool()
&& s_c.literal.ty.is_bool()
&& f_c.literal.ty().is_bool()
&& s_c.literal.ty().is_bool()
&& f_c.literal.try_eval_bool(tcx, param_env).is_some()
&& s_c.literal.try_eval_bool(tcx, param_env).is_some() => {}

36 changes: 19 additions & 17 deletions compiler/rustc_mir/src/transform/promote_consts.rs
Original file line number Diff line number Diff line change
@@ -921,7 +921,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
let unit = Rvalue::Use(Operand::Constant(box Constant {
span: statement.source_info.span,
user_ty: None,
literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit),
literal: ty::Const::zero_sized(self.tcx, self.tcx.types.unit).into(),
}));
mem::replace(rhs, unit)
},
@@ -998,20 +998,22 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
Operand::Constant(Box::new(Constant {
span,
user_ty: None,
literal: tcx.mk_const(ty::Const {
ty,
val: ty::ConstKind::Unevaluated(
def,
InternalSubsts::for_item(tcx, def.did, |param, _| {
if let ty::GenericParamDefKind::Lifetime = param.kind {
tcx.lifetimes.re_erased.into()
} else {
tcx.mk_param_from_def(param)
}
}),
Some(promoted_id),
),
}),
literal: tcx
.mk_const(ty::Const {
ty,
val: ty::ConstKind::Unevaluated(
def,
InternalSubsts::for_item(tcx, def.did, |param, _| {
if let ty::GenericParamDefKind::Lifetime = param.kind {
tcx.lifetimes.re_erased.into()
} else {
tcx.mk_param_from_def(param)
}
}),
Some(promoted_id),
),
})
.into(),
}))
};
let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
@@ -1250,8 +1252,8 @@ crate fn is_const_fn_in_array_repeat_expression<'tcx>(
if let Some(Terminator { kind: TerminatorKind::Call { func, destination, .. }, .. }) =
&block.terminator
{
if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
if let ty::FnDef(def_id, _) = *ty.kind() {
if let Operand::Constant(box Constant { literal, .. }) = func {
if let ty::FnDef(def_id, _) = *literal.ty().kind() {
if let Some((destination_place, _)) = destination {
if destination_place == place {
if is_const_fn(ccx.tcx, def_id) {
8 changes: 4 additions & 4 deletions compiler/rustc_mir/src/transform/required_consts.rs
Original file line number Diff line number Diff line change
@@ -14,10 +14,10 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {

impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) {
let const_kind = constant.literal.val;

if let ConstKind::Unevaluated(_, _, _) = const_kind {
self.required_consts.push(*constant);
if let Some(ct) = constant.literal.const_for_ty() {
if let ConstKind::Unevaluated(_, _, _) = ct.val {
self.required_consts.push(*constant);
}
}
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/transform/rustc_peek.rs
Original file line number Diff line number Diff line change
@@ -205,7 +205,7 @@ impl PeekCall {
if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
&terminator.kind
{
if let ty::FnDef(def_id, substs) = *func.literal.ty.kind() {
if let ty::FnDef(def_id, substs) = *func.literal.ty().kind() {
let sig = tcx.fn_sig(def_id);
let name = tcx.item_name(def_id);
if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
Original file line number Diff line number Diff line change
@@ -205,12 +205,12 @@ fn find_branch_value_info<'tcx>(
match (left, right) {
(Constant(branch_value), Copy(to_switch_on) | Move(to_switch_on))
| (Copy(to_switch_on) | Move(to_switch_on), Constant(branch_value)) => {
let branch_value_ty = branch_value.literal.ty;
let branch_value_ty = branch_value.literal.ty();
// we only want to apply this optimization if we are matching on integrals (and chars), as it is not possible to switch on floats
if !branch_value_ty.is_integral() && !branch_value_ty.is_char() {
return None;
};
let branch_value_scalar = branch_value.literal.val.try_to_scalar()?;
let branch_value_scalar = branch_value.literal.try_to_scalar()?;
Some((branch_value_scalar, branch_value_ty, *to_switch_on))
}
_ => None,
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/util/elaborate_drops.rs
Original file line number Diff line number Diff line change
@@ -1035,7 +1035,7 @@ where
Operand::Constant(box Constant {
span: self.source_info.span,
user_ty: None,
literal: ty::Const::from_usize(self.tcx(), val.into()),
literal: ty::Const::from_usize(self.tcx(), val.into()).into(),
})
}

4 changes: 2 additions & 2 deletions compiler/rustc_mir/src/util/find_self_call.rs
Original file line number Diff line number Diff line change
@@ -17,8 +17,8 @@ pub fn find_self_call<'tcx>(
&body[block].terminator
{
debug!("find_self_call: func={:?}", func);
if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
if let ty::FnDef(def_id, substs) = *ty.kind() {
if let Operand::Constant(box Constant { literal, .. }) = func {
if let ty::FnDef(def_id, substs) = *literal.ty().kind() {
if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
tcx.opt_associated_item(def_id)
{
9 changes: 7 additions & 2 deletions compiler/rustc_mir/src/util/pretty.rs
Original file line number Diff line number Diff line change
@@ -439,7 +439,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
self.super_constant(constant, location);
let Constant { span, user_ty, literal } = constant;
match literal.ty.kind() {
match literal.ty().kind() {
ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {}
// Unit type
ty::Tuple(tys) if tys.is_empty() => {}
@@ -449,7 +449,12 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
if let Some(user_ty) = user_ty {
self.push(&format!("+ user_ty: {:?}", user_ty));
}
self.push(&format!("+ literal: {:?}", literal));
match literal {
ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)),
ConstantKind::Val(val, ty) => {
self.push(&format!("+ literal: {:?}, {}", val, ty))
}
}
}
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/build/cfg.rs
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@ impl<'tcx> CFG<'tcx> {
Rvalue::Use(Operand::Constant(box Constant {
span: source_info.span,
user_ty: None,
literal: ty::Const::zero_sized(tcx, tcx.types.unit),
literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
})),
);
}
10 changes: 6 additions & 4 deletions compiler/rustc_mir_build/src/build/expr/as_constant.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
crate fn as_constant(&mut self, expr: &Expr<'_, 'tcx>) -> Constant<'tcx> {
let this = self;
let Expr { ty, temp_lifetime: _, span, ref kind } = *expr;
match kind {
match *kind {
ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
ExprKind::Literal { literal, user_ty, const_id: _ } => {
let user_ty = user_ty.map(|user_ty| {
@@ -22,11 +22,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
})
});
assert_eq!(literal.ty, ty);
Constant { span, user_ty, literal }
Constant { span, user_ty, literal: literal.into() }
}
ExprKind::StaticRef { literal, .. } => {
Constant { span, user_ty: None, literal: literal.into() }
}
ExprKind::StaticRef { literal, .. } => Constant { span, user_ty: None, literal },
ExprKind::ConstBlock { value } => {
Constant { span: span, user_ty: None, literal: value }
Constant { span: span, user_ty: None, literal: value.into() }
}
_ => span_bug!(span, "expression is not a valid constant {:?}", kind),
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
@@ -219,7 +219,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(Rvalue::Use(Operand::Constant(box Constant {
span: expr_span,
user_ty: None,
literal: ty::Const::zero_sized(this.tcx, this.tcx.types.unit),
literal: ty::Const::zero_sized(this.tcx, this.tcx.types.unit).into(),
})))
}
ExprKind::Yield { .. }
4 changes: 2 additions & 2 deletions compiler/rustc_mir_build/src/build/expr/into.rs
Original file line number Diff line number Diff line change
@@ -146,7 +146,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Constant {
span: expr_span,
user_ty: None,
literal: ty::Const::from_bool(this.tcx, true),
literal: ty::Const::from_bool(this.tcx, true).into(),
},
);

@@ -157,7 +157,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Constant {
span: expr_span,
user_ty: None,
literal: ty::Const::from_bool(this.tcx, false),
literal: ty::Const::from_bool(this.tcx, false).into(),
},
);

2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/build/matches/test.rs
Original file line number Diff line number Diff line change
@@ -429,7 +429,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Need to experiment.
user_ty: None,

literal: method,
literal: method.into(),
}),
args: vec![val, expect],
destination: Some((eq_result, eq_block)),
3 changes: 2 additions & 1 deletion compiler/rustc_mir_build/src/build/misc.rs
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span: Span,
literal: &'tcx ty::Const<'tcx>,
) -> Operand<'tcx> {
let literal = literal.into();
let constant = box Constant { span, user_ty: None, literal };
Operand::Constant(constant)
}
@@ -57,7 +58,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Constant {
span: source_info.span,
user_ty: None,
literal: ty::Const::from_usize(self.tcx, value),
literal: ty::Const::from_usize(self.tcx, value).into(),
},
);
temp
4 changes: 2 additions & 2 deletions compiler/rustc_mir_build/src/thir/cx/mod.rs
Original file line number Diff line number Diff line change
@@ -68,11 +68,11 @@ impl<'thir, 'tcx> Cx<'thir, 'tcx> {
// FIXME(#31407) this is only necessary because float parsing is buggy
self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)");
// create a dummy value and continue compiling
Const::from_bits(self.tcx, 0, self.param_env.and(ty))
self.tcx.const_error(ty)
}
Err(LitToConstError::Reported) => {
// create a dummy value and continue compiling
Const::from_bits(self.tcx, 0, self.param_env.and(ty))
self.tcx.const_error(ty)
}
Err(LitToConstError::TypeError) => bug!("const_eval_literal: had type error"),
}
9 changes: 9 additions & 0 deletions compiler/rustc_query_impl/src/keys.rs
Original file line number Diff line number Diff line change
@@ -228,6 +228,15 @@ impl<'tcx> Key for (&'tcx ty::Const<'tcx>, mir::Field) {
}
}

impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
fn query_crate(&self) -> CrateNum {
LOCAL_CRATE
}
fn default_span(&self, _: TyCtxt<'_>) -> Span {
DUMMY_SP
}
}

impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
fn query_crate(&self) -> CrateNum {
self.def_id().krate
Original file line number Diff line number Diff line change
@@ -377,7 +377,10 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
let local = self.place_to_local(span, p)?;
Ok(self.locals[local])
}
mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal), span)),
mir::Operand::Constant(ct) => match ct.literal {
mir::ConstantKind::Ty(ct) => Ok(self.add_node(Node::Leaf(ct), span)),
mir::ConstantKind::Val(..) => self.error(Some(span), "unsupported constant")?,
},
}
}