Move the Unevaluated
constant arm upwards in the type structure
This commit is contained in:
parent
cae164753f
commit
b903cb9d60
62 changed files with 405 additions and 316 deletions
|
@ -301,7 +301,6 @@ impl_stable_hash_for!(struct ty::FieldDef {
|
|||
|
||||
impl_stable_hash_for!(
|
||||
impl<'tcx> for enum mir::interpret::ConstValue<'tcx> [ mir::interpret::ConstValue ] {
|
||||
Unevaluated(def_id, substs),
|
||||
Scalar(val),
|
||||
ScalarPair(a, b),
|
||||
ByRef(id, alloc, offset),
|
||||
|
@ -378,6 +377,11 @@ impl_stable_hash_for!(struct ty::Const<'tcx> {
|
|||
val
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(impl<'tcx> for enum ty::LazyConst<'tcx> [ty::LazyConst] {
|
||||
Unevaluated(did, substs),
|
||||
Evaluated(c)
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(enum mir::interpret::ErrorHandled {
|
||||
Reported,
|
||||
TooGeneric
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::fmt;
|
||||
|
||||
use crate::ty::{Ty, subst::Substs, layout::{HasDataLayout, Size}};
|
||||
use crate::hir::def_id::DefId;
|
||||
use crate::ty::{Ty, layout::{HasDataLayout, Size}};
|
||||
|
||||
use super::{EvalResult, Pointer, PointerArithmetic, Allocation, AllocId, sign_extend, truncate};
|
||||
|
||||
|
@ -18,12 +17,6 @@ pub struct RawConst<'tcx> {
|
|||
/// matches the LocalValue optimizations for easy conversions between Value and ConstValue.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)]
|
||||
pub enum ConstValue<'tcx> {
|
||||
/// Never returned from the `const_eval` query, but the HIR contains these frequently in order
|
||||
/// to allow HIR creation to happen for everything before needing to be able to run constant
|
||||
/// evaluation
|
||||
/// FIXME: The query should then return a type that does not even have this variant.
|
||||
Unevaluated(DefId, &'tcx Substs<'tcx>),
|
||||
|
||||
/// Used only for types with layout::abi::Scalar ABI and ZSTs
|
||||
///
|
||||
/// Not using the enum `Value` to encode that this must not be `Undef`
|
||||
|
@ -43,7 +36,6 @@ impl<'tcx> ConstValue<'tcx> {
|
|||
#[inline]
|
||||
pub fn try_to_scalar(&self) -> Option<Scalar> {
|
||||
match *self {
|
||||
ConstValue::Unevaluated(..) |
|
||||
ConstValue::ByRef(..) |
|
||||
ConstValue::ScalarPair(..) => None,
|
||||
ConstValue::Scalar(val) => Some(val),
|
||||
|
|
|
@ -2154,7 +2154,9 @@ impl<'tcx> Operand<'tcx> {
|
|||
span,
|
||||
ty,
|
||||
user_ty: None,
|
||||
literal: ty::Const::zero_sized(tcx, ty),
|
||||
literal: tcx.intern_lazy_const(
|
||||
ty::LazyConst::Evaluated(ty::Const::zero_sized(tcx, ty)),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2457,7 +2459,7 @@ 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: &'tcx ty::LazyConst<'tcx>,
|
||||
}
|
||||
|
||||
/// A collection of projections into user types.
|
||||
|
@ -2655,7 +2657,15 @@ newtype_index! {
|
|||
impl<'tcx> Debug for Constant<'tcx> {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "const ")?;
|
||||
fmt_const_val(fmt, self.literal)
|
||||
fmt_lazy_const_val(fmt, self.literal)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a `ConstValue` in a way closer to the original source code than the `Debug` output.
|
||||
pub fn fmt_lazy_const_val(f: &mut impl Write, const_val: &ty::LazyConst<'_>) -> fmt::Result {
|
||||
match const_val {
|
||||
ty::LazyConst::Unevaluated(..) => write!(f, "{:?}", const_val),
|
||||
ty::LazyConst::Evaluated(c) => fmt_const_val(f, c),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
|
|||
PlaceTy::Ty {
|
||||
ty: match ty.sty {
|
||||
ty::Array(inner, size) => {
|
||||
let size = size.unwrap_usize(tcx);
|
||||
let size = size.unwrap_evaluated().unwrap_usize(tcx);
|
||||
let len = size - (from as u64) - (to as u64);
|
||||
tcx.mk_array(inner, len)
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ macro_rules! make_mir_visitor {
|
|||
}
|
||||
|
||||
fn visit_const(&mut self,
|
||||
constant: & $($mutability)* &'tcx ty::Const<'tcx>,
|
||||
constant: & $($mutability)* &'tcx ty::LazyConst<'tcx>,
|
||||
_: Location) {
|
||||
self.super_const(constant);
|
||||
}
|
||||
|
@ -892,7 +892,7 @@ macro_rules! make_mir_visitor {
|
|||
fn super_region(&mut self, _region: & $($mutability)* ty::Region<'tcx>) {
|
||||
}
|
||||
|
||||
fn super_const(&mut self, _const: & $($mutability)* &'tcx ty::Const<'tcx>) {
|
||||
fn super_const(&mut self, _const: & $($mutability)* &'tcx ty::LazyConst<'tcx>) {
|
||||
}
|
||||
|
||||
fn super_substs(&mut self, _substs: & $($mutability)* &'tcx Substs<'tcx>) {
|
||||
|
|
|
@ -418,18 +418,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
Some(format!("[{}]", self.tcx.type_of(def.did).to_string())),
|
||||
));
|
||||
let tcx = self.tcx;
|
||||
if let Some(len) = len.val.try_to_scalar().and_then(|scalar| {
|
||||
scalar.to_usize(&tcx).ok()
|
||||
}) {
|
||||
flags.push((
|
||||
"_Self".to_owned(),
|
||||
Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)),
|
||||
));
|
||||
} else {
|
||||
flags.push((
|
||||
"_Self".to_owned(),
|
||||
Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())),
|
||||
));
|
||||
if let ty::LazyConst::Evaluated(len) = len {
|
||||
if let Some(len) = len.val.try_to_scalar().and_then(|scalar| {
|
||||
scalar.to_usize(&tcx).ok()
|
||||
}) {
|
||||
flags.push((
|
||||
"_Self".to_owned(),
|
||||
Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)),
|
||||
));
|
||||
} else {
|
||||
flags.push((
|
||||
"_Self".to_owned(),
|
||||
Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ use super::util;
|
|||
use hir::def_id::DefId;
|
||||
use infer::{InferCtxt, InferOk};
|
||||
use infer::type_variable::TypeVariableOrigin;
|
||||
use mir::interpret::ConstValue;
|
||||
use mir::interpret::{GlobalId};
|
||||
use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
|
||||
use syntax::ast::Ident;
|
||||
|
@ -410,8 +409,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
|
|||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
|
||||
if let ConstValue::Unevaluated(def_id, substs) = constant.val {
|
||||
fn fold_const(&mut self, constant: &'tcx ty::LazyConst<'tcx>) -> &'tcx ty::LazyConst<'tcx> {
|
||||
if let ty::LazyConst::Unevaluated(def_id, substs) = *constant {
|
||||
let tcx = self.selcx.tcx().global_tcx();
|
||||
if let Some(param_env) = self.tcx().lift_to_global(&self.param_env) {
|
||||
if substs.needs_infer() || substs.has_placeholders() {
|
||||
|
@ -423,8 +422,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
|
|||
promoted: None
|
||||
};
|
||||
if let Ok(evaluated) = tcx.const_eval(param_env.and(cid)) {
|
||||
let evaluated = evaluated.subst(self.tcx(), substs);
|
||||
return self.fold_const(evaluated);
|
||||
let substs = tcx.lift_to_global(&substs).unwrap();
|
||||
let evaluated = evaluated.subst(tcx, substs);
|
||||
return tcx.intern_lazy_const(ty::LazyConst::Evaluated(evaluated));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -436,7 +436,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
|
|||
promoted: None
|
||||
};
|
||||
if let Ok(evaluated) = tcx.const_eval(param_env.and(cid)) {
|
||||
return self.fold_const(evaluated)
|
||||
return tcx.intern_lazy_const(ty::LazyConst::Evaluated(evaluated));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use infer::at::At;
|
||||
use infer::canonical::OriginalQueryValues;
|
||||
use infer::{InferCtxt, InferOk};
|
||||
use mir::interpret::{ConstValue, GlobalId};
|
||||
use mir::interpret::GlobalId;
|
||||
use traits::project::Normalized;
|
||||
use traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
|
||||
use ty::fold::{TypeFoldable, TypeFolder};
|
||||
|
@ -188,8 +188,8 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx
|
|||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
|
||||
if let ConstValue::Unevaluated(def_id, substs) = constant.val {
|
||||
fn fold_const(&mut self, constant: &'tcx ty::LazyConst<'tcx>) -> &'tcx ty::LazyConst<'tcx> {
|
||||
if let ty::LazyConst::Unevaluated(def_id, substs) = *constant {
|
||||
let tcx = self.infcx.tcx.global_tcx();
|
||||
if let Some(param_env) = self.tcx().lift_to_global(&self.param_env) {
|
||||
if substs.needs_infer() || substs.has_placeholders() {
|
||||
|
@ -201,8 +201,9 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx
|
|||
promoted: None,
|
||||
};
|
||||
if let Ok(evaluated) = tcx.const_eval(param_env.and(cid)) {
|
||||
let evaluated = evaluated.subst(self.tcx(), substs);
|
||||
return self.fold_const(evaluated);
|
||||
let substs = tcx.lift_to_global(&substs).unwrap();
|
||||
let evaluated = evaluated.subst(tcx, substs);
|
||||
return tcx.intern_lazy_const(ty::LazyConst::Evaluated(evaluated));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -214,7 +215,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx
|
|||
promoted: None,
|
||||
};
|
||||
if let Ok(evaluated) = tcx.const_eval(param_env.and(cid)) {
|
||||
return self.fold_const(evaluated)
|
||||
return tcx.intern_lazy_const(ty::LazyConst::Evaluated(evaluated));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -255,6 +255,15 @@ pub fn decode_const<'a, 'tcx, D>(decoder: &mut D)
|
|||
Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decode_lazy_const<'a, 'tcx, D>(decoder: &mut D)
|
||||
-> Result<&'tcx ty::LazyConst<'tcx>, D::Error>
|
||||
where D: TyDecoder<'a, 'tcx>,
|
||||
'tcx: 'a,
|
||||
{
|
||||
Ok(decoder.tcx().intern_lazy_const(Decodable::decode(decoder)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decode_allocation<'a, 'tcx, D>(decoder: &mut D)
|
||||
-> Result<&'tcx Allocation, D::Error>
|
||||
|
@ -396,6 +405,13 @@ macro_rules! implement_ty_decoder {
|
|||
}
|
||||
}
|
||||
|
||||
impl<$($typaram),*> SpecializedDecoder<&'tcx $crate::ty::LazyConst<'tcx>>
|
||||
for $DecoderName<$($typaram),*> {
|
||||
fn specialized_decode(&mut self) -> Result<&'tcx ty::LazyConst<'tcx>, Self::Error> {
|
||||
decode_lazy_const(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($typaram),*> SpecializedDecoder<&'tcx $crate::mir::interpret::Allocation>
|
||||
for $DecoderName<$($typaram),*> {
|
||||
fn specialized_decode(
|
||||
|
|
|
@ -29,7 +29,7 @@ use traits;
|
|||
use traits::{Clause, Clauses, GoalKind, Goal, Goals};
|
||||
use ty::{self, Ty, TypeAndMut};
|
||||
use ty::{TyS, TyKind, List};
|
||||
use ty::{AdtKind, AdtDef, ClosureSubsts, GeneratorSubsts, Region, Const};
|
||||
use ty::{AdtKind, AdtDef, ClosureSubsts, GeneratorSubsts, Region, Const, LazyConst};
|
||||
use ty::{PolyFnSig, InferTy, ParamTy, ProjectionTy, ExistentialPredicate, Predicate};
|
||||
use ty::RegionKind;
|
||||
use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
|
||||
|
@ -1112,6 +1112,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn intern_lazy_const(self, c: ty::LazyConst<'tcx>) -> &'tcx ty::LazyConst<'tcx> {
|
||||
self.global_interners.arena.alloc(c)
|
||||
}
|
||||
|
||||
pub fn intern_layout(self, layout: LayoutDetails) -> &'gcx LayoutDetails {
|
||||
self.layout_interner.borrow_mut().intern(layout, |layout| {
|
||||
self.global_arenas.layout.alloc(layout)
|
||||
|
@ -1814,6 +1818,21 @@ impl<'a, 'tcx> Lift<'tcx> for &'a List<Clause<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Lift<'tcx> for &'a LazyConst<'a> {
|
||||
type Lifted = &'tcx LazyConst<'tcx>;
|
||||
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<&'tcx LazyConst<'tcx>> {
|
||||
if tcx.interners.arena.in_arena(*self as *const _) {
|
||||
return Some(unsafe { mem::transmute(*self) });
|
||||
}
|
||||
// Also try in the global tcx if we're not that.
|
||||
if !tcx.is_global() {
|
||||
self.lift_to_tcx(tcx.global_tcx())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Lift<'tcx> for &'a Const<'a> {
|
||||
type Lifted = &'tcx Const<'tcx>;
|
||||
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<&'tcx Const<'tcx>> {
|
||||
|
@ -2683,7 +2702,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
#[inline]
|
||||
pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> {
|
||||
self.mk_ty(Array(ty, ty::Const::from_usize(self, n)))
|
||||
self.mk_ty(Array(ty, self.intern_lazy_const(
|
||||
ty::LazyConst::Evaluated(ty::Const::from_usize(self.global_tcx(), n))
|
||||
)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -167,11 +167,12 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
|
|||
|
||||
ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.item_path_str(def.did)).into(),
|
||||
ty::Foreign(def_id) => format!("extern type `{}`", tcx.item_path_str(def_id)).into(),
|
||||
ty::Array(_, n) => {
|
||||
match n.assert_usize(tcx) {
|
||||
ty::Array(_, n) => match n {
|
||||
ty::LazyConst::Evaluated(n) => match n.assert_usize(tcx) {
|
||||
Some(n) => format!("array of {} elements", n).into(),
|
||||
None => "array".into(),
|
||||
}
|
||||
},
|
||||
ty::LazyConst::Unevaluated(..) => "array".into(),
|
||||
}
|
||||
ty::Slice(_) => "slice".into(),
|
||||
ty::RawPtr(_) => "*-ptr".into(),
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use mir::interpret::ConstValue;
|
||||
use ty::subst::Substs;
|
||||
use ty::{self, Ty, TypeFlags, TypeFoldable};
|
||||
|
||||
|
@ -173,7 +172,10 @@ impl FlagComputation {
|
|||
|
||||
&ty::Array(tt, len) => {
|
||||
self.add_ty(tt);
|
||||
self.add_const(len);
|
||||
if let ty::LazyConst::Unevaluated(_, substs) = len {
|
||||
self.add_flags(TypeFlags::HAS_PROJECTION);
|
||||
self.add_substs(substs);
|
||||
}
|
||||
}
|
||||
|
||||
&ty::Slice(tt) => {
|
||||
|
@ -230,14 +232,6 @@ impl FlagComputation {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_const(&mut self, constant: &ty::Const<'_>) {
|
||||
self.add_ty(constant.ty);
|
||||
if let ConstValue::Unevaluated(_, substs) = constant.val {
|
||||
self.add_flags(TypeFlags::HAS_PROJECTION);
|
||||
self.add_substs(substs);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_existential_projection(&mut self, projection: &ty::ExistentialProjection<'_>) {
|
||||
self.add_substs(projection.substs);
|
||||
self.add_ty(projection.ty);
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
//! These methods return true to indicate that the visitor has found what it is looking for
|
||||
//! and does not need to visit anything else.
|
||||
|
||||
use mir::interpret::ConstValue;
|
||||
use hir::def_id::DefId;
|
||||
use ty::{self, Binder, Ty, TyCtxt, TypeFlags};
|
||||
|
||||
|
@ -164,7 +163,7 @@ pub trait TypeFolder<'gcx: 'tcx, 'tcx> : Sized {
|
|||
r.super_fold_with(self)
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
|
||||
fn fold_const(&mut self, c: &'tcx ty::LazyConst<'tcx>) -> &'tcx ty::LazyConst<'tcx> {
|
||||
c.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +181,7 @@ pub trait TypeVisitor<'tcx> : Sized {
|
|||
r.super_visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
|
||||
fn visit_const(&mut self, c: &'tcx ty::LazyConst<'tcx>) -> bool {
|
||||
c.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
@ -864,8 +863,8 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
|
|||
flags.intersects(self.flags)
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
|
||||
if let ConstValue::Unevaluated(..) = c.val {
|
||||
fn visit_const(&mut self, c: &'tcx ty::LazyConst<'tcx>) -> bool {
|
||||
if let ty::LazyConst::Unevaluated(..) = c {
|
||||
let projection_flags = TypeFlags::HAS_NORMALIZABLE_PROJECTION |
|
||||
TypeFlags::HAS_PROJECTION;
|
||||
if projection_flags.intersects(self.flags) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ty::context::TyCtxt;
|
||||
use ty::{AdtDef, VariantDef, FieldDef, Ty, TyS};
|
||||
use ty::{DefId, Substs};
|
||||
use ty::{self, DefId, Substs};
|
||||
use ty::{AdtKind, Visibility};
|
||||
use ty::TyKind::*;
|
||||
|
||||
|
@ -213,11 +213,14 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
|||
}
|
||||
|
||||
Array(ty, len) => {
|
||||
match len.assert_usize(tcx) {
|
||||
// If the array is definitely non-empty, it's uninhabited if
|
||||
// the type of its elements is uninhabited.
|
||||
Some(n) if n != 0 => ty.uninhabited_from(tcx),
|
||||
_ => DefIdForest::empty()
|
||||
match len {
|
||||
ty::LazyConst::Unevaluated(..) => DefIdForest::empty(),
|
||||
ty::LazyConst::Evaluated(len) => match len.assert_usize(tcx) {
|
||||
// If the array is definitely non-empty, it's uninhabited if
|
||||
// the type of its elements is uninhabited.
|
||||
Some(n) if n != 0 => ty.uninhabited_from(tcx),
|
||||
_ => DefIdForest::empty()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -543,7 +543,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||
}
|
||||
|
||||
let element = self.layout_of(element)?;
|
||||
let count = count.unwrap_usize(tcx);
|
||||
let count = count.unwrap_evaluated().unwrap_usize(tcx);
|
||||
let size = element.size.checked_mul(count, dl)
|
||||
.ok_or(LayoutError::SizeOverflow(ty))?;
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
|
|||
pub use self::sty::{ClosureSubsts, GeneratorSubsts, UpvarSubsts, TypeAndMut};
|
||||
pub use self::sty::{TraitRef, TyKind, PolyTraitRef};
|
||||
pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef};
|
||||
pub use self::sty::{ExistentialProjection, PolyExistentialProjection, Const};
|
||||
pub use self::sty::{ExistentialProjection, PolyExistentialProjection, Const, LazyConst};
|
||||
pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region};
|
||||
pub use self::sty::RegionKind;
|
||||
pub use self::sty::{TyVid, IntVid, FloatVid, RegionVid};
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
//! subtyping, type equality, etc.
|
||||
|
||||
use hir::def_id::DefId;
|
||||
use mir::interpret::ConstValue;
|
||||
use ty::subst::{Kind, UnpackedKind, Substs};
|
||||
use ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use ty::error::{ExpectedFound, TypeError};
|
||||
|
@ -480,14 +479,9 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
|
|||
(&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) =>
|
||||
{
|
||||
let t = relation.relate(&a_t, &b_t)?;
|
||||
assert_eq!(sz_a.ty, tcx.types.usize);
|
||||
assert_eq!(sz_b.ty, tcx.types.usize);
|
||||
let to_u64 = |x: &'tcx ty::Const<'tcx>| -> Result<u64, ErrorReported> {
|
||||
if let Some(s) = x.assert_usize(tcx) {
|
||||
return Ok(s);
|
||||
}
|
||||
match x.val {
|
||||
ConstValue::Unevaluated(def_id, substs) => {
|
||||
let to_u64 = |x: ty::LazyConst<'tcx>| -> Result<u64, ErrorReported> {
|
||||
match x {
|
||||
ty::LazyConst::Unevaluated(def_id, substs) => {
|
||||
// FIXME(eddyb) get the right param_env.
|
||||
let param_env = ty::ParamEnv::empty();
|
||||
if let Some(substs) = tcx.lift_to_global(&substs) {
|
||||
|
@ -513,14 +507,14 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
|
|||
"array length could not be evaluated");
|
||||
Err(ErrorReported)
|
||||
}
|
||||
_ => {
|
||||
ty::LazyConst::Evaluated(c) => c.assert_usize(tcx).ok_or_else(|| {
|
||||
tcx.sess.delay_span_bug(DUMMY_SP,
|
||||
&format!("arrays should not have {:?} as length", x));
|
||||
Err(ErrorReported)
|
||||
}
|
||||
"array length could not be evaluated");
|
||||
ErrorReported
|
||||
})
|
||||
}
|
||||
};
|
||||
match (to_u64(sz_a), to_u64(sz_b)) {
|
||||
match (to_u64(*sz_a), to_u64(*sz_b)) {
|
||||
(Ok(sz_a_u64), Ok(sz_b_u64)) => {
|
||||
if sz_a_u64 == sz_b_u64 {
|
||||
Ok(tcx.mk_ty(ty::Array(t, sz_a)))
|
||||
|
|
|
@ -53,6 +53,7 @@ CloneTypeFoldableAndLiftImpls! {
|
|||
::ty::UniverseIndex,
|
||||
::ty::Variance,
|
||||
::syntax_pos::Span,
|
||||
ConstValue<'tcx>,
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1020,26 +1021,31 @@ EnumTypeFoldableImpl! {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> {
|
||||
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::LazyConst<'tcx> {
|
||||
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
|
||||
match *self {
|
||||
ConstValue::Scalar(v) => ConstValue::Scalar(v),
|
||||
ConstValue::ScalarPair(a, b) => ConstValue::ScalarPair(a, b),
|
||||
ConstValue::ByRef(id, alloc, offset) => ConstValue::ByRef(id, alloc, offset),
|
||||
ConstValue::Unevaluated(def_id, substs) => {
|
||||
ConstValue::Unevaluated(def_id, substs.fold_with(folder))
|
||||
let new = match self {
|
||||
ty::LazyConst::Evaluated(v) => ty::LazyConst::Evaluated(v.fold_with(folder)),
|
||||
ty::LazyConst::Unevaluated(def_id, substs) => {
|
||||
ty::LazyConst::Unevaluated(*def_id, substs.fold_with(folder))
|
||||
}
|
||||
}
|
||||
};
|
||||
folder.tcx().intern_lazy_const(new)
|
||||
}
|
||||
|
||||
fn fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
|
||||
folder.fold_const(*self)
|
||||
}
|
||||
|
||||
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
|
||||
match *self {
|
||||
ConstValue::Scalar(_) |
|
||||
ConstValue::ScalarPair(_, _) |
|
||||
ConstValue::ByRef(_, _, _) => false,
|
||||
ConstValue::Unevaluated(_, substs) => substs.visit_with(visitor),
|
||||
ty::LazyConst::Evaluated(c) => c.visit_with(visitor),
|
||||
ty::LazyConst::Unevaluated(_, substs) => substs.visit_with(visitor),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
|
||||
visitor.visit_const(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> {
|
||||
|
@ -1052,15 +1058,7 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
fn fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
|
||||
folder.fold_const(*self)
|
||||
}
|
||||
|
||||
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
|
||||
self.ty.visit_with(visitor) || self.val.visit_with(visitor)
|
||||
}
|
||||
|
||||
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
|
||||
visitor.visit_const(self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ pub enum TyKind<'tcx> {
|
|||
Str,
|
||||
|
||||
/// An array with the given length. Written as `[T; n]`.
|
||||
Array(Ty<'tcx>, &'tcx ty::Const<'tcx>),
|
||||
Array(Ty<'tcx>, &'tcx ty::LazyConst<'tcx>),
|
||||
|
||||
/// The pointee of an array slice. Written as `[T]`.
|
||||
Slice(Ty<'tcx>),
|
||||
|
@ -2013,6 +2013,36 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)]
|
||||
/// Used in the HIR by using `Unevaluated` everywhere and later normalizing to `Evaluated` if the
|
||||
/// code is monomorphic enough for that.
|
||||
pub enum LazyConst<'tcx> {
|
||||
Unevaluated(DefId, &'tcx Substs<'tcx>),
|
||||
Evaluated(&'tcx Const<'tcx>),
|
||||
}
|
||||
|
||||
static_assert!(MEM_SIZE_OF_LAZY_CONST: ::std::mem::size_of::<LazyConst<'_>>() == 24);
|
||||
|
||||
impl<'tcx> LazyConst<'tcx> {
|
||||
pub fn unwrap_evaluated(self) -> &'tcx Const<'tcx> {
|
||||
match self {
|
||||
LazyConst::Evaluated(c) => c,
|
||||
LazyConst::Unevaluated(..) => bug!("unexpected unevaluated constant"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_evaluated<R>(self, f: impl FnOnce(&'tcx Const<'tcx>) -> Option<R>) -> Option<R> {
|
||||
match self {
|
||||
LazyConst::Evaluated(c) => f(c),
|
||||
LazyConst::Unevaluated(..) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_usize(self, tcx: TyCtxt<'_, '_, 'tcx>) -> Option<u64> {
|
||||
self.map_evaluated(|c| c.assert_usize(tcx))
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed constant value.
|
||||
#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Const<'tcx> {
|
||||
|
@ -2022,18 +2052,6 @@ pub struct Const<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> Const<'tcx> {
|
||||
pub fn unevaluated(
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
def_id: DefId,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> &'tcx Self {
|
||||
tcx.mk_const(Const {
|
||||
val: ConstValue::Unevaluated(def_id, substs),
|
||||
ty,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_const_value(
|
||||
tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
|
@ -2149,3 +2167,4 @@ impl<'tcx> Const<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> serialize::UseSpecializedDecodable for &'tcx Const<'tcx> {}
|
||||
impl<'tcx> serialize::UseSpecializedDecodable for &'tcx LazyConst<'tcx> {}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! An iterator over the type substructure.
|
||||
//! WARNING: this does not keep track of the region depth.
|
||||
|
||||
use mir::interpret::ConstValue;
|
||||
use ty::{self, Ty};
|
||||
use smallvec::{self, SmallVec};
|
||||
|
||||
|
@ -75,7 +74,9 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) {
|
|||
ty::Placeholder(..) | ty::Bound(..) | ty::Foreign(..) => {
|
||||
}
|
||||
ty::Array(ty, len) => {
|
||||
push_const(stack, len);
|
||||
if let ty::LazyConst::Unevaluated(_, substs) = len {
|
||||
stack.extend(substs.types().rev());
|
||||
}
|
||||
stack.push(ty);
|
||||
}
|
||||
ty::Slice(ty) => {
|
||||
|
@ -128,10 +129,3 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_const<'tcx>(stack: &mut TypeWalkerStack<'tcx>, constant: &'tcx ty::Const<'tcx>) {
|
||||
if let ConstValue::Unevaluated(_, substs) = constant.val {
|
||||
stack.extend(substs.types().rev());
|
||||
}
|
||||
stack.push(constant.ty);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use hir::def_id::DefId;
|
||||
use mir::interpret::ConstValue;
|
||||
use infer::InferCtxt;
|
||||
use ty::subst::Substs;
|
||||
use traits;
|
||||
|
@ -202,11 +201,10 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Pushes the obligations required for a constant value to be WF
|
||||
/// Pushes the obligations required for an array length to be WF
|
||||
/// into `self.out`.
|
||||
fn compute_const(&mut self, constant: &'tcx ty::Const<'tcx>) {
|
||||
self.require_sized(constant.ty, traits::ConstSized);
|
||||
if let ConstValue::Unevaluated(def_id, substs) = constant.val {
|
||||
fn compute_array_len(&mut self, constant: ty::LazyConst<'tcx>) {
|
||||
if let ty::LazyConst::Unevaluated(def_id, substs) = constant {
|
||||
let obligations = self.nominal_obligations(def_id, substs);
|
||||
self.out.extend(obligations);
|
||||
|
||||
|
@ -260,8 +258,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
|
|||
|
||||
ty::Array(subty, len) => {
|
||||
self.require_sized(subty, traits::SliceOrArrayElem);
|
||||
assert_eq!(len.ty, self.infcx.tcx.types.usize);
|
||||
self.compute_const(len);
|
||||
self.compute_array_len(*len);
|
||||
}
|
||||
|
||||
ty::Tuple(ref tys) => {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use hir::def_id::DefId;
|
||||
use hir::map::definitions::DefPathData;
|
||||
use mir::interpret::ConstValue;
|
||||
use middle::region;
|
||||
use ty::subst::{self, Subst};
|
||||
use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
|
||||
|
@ -1272,12 +1271,12 @@ define_print! {
|
|||
}),
|
||||
Array(ty, sz) => {
|
||||
print!(f, cx, write("["), print(ty), write("; "))?;
|
||||
match sz.val {
|
||||
ConstValue::Unevaluated(_def_id, _substs) => {
|
||||
match sz {
|
||||
ty::LazyConst::Unevaluated(_def_id, _substs) => {
|
||||
write!(f, "_")?;
|
||||
}
|
||||
_ => ty::tls::with(|tcx| {
|
||||
write!(f, "{}", sz.unwrap_usize(tcx))
|
||||
ty::LazyConst::Evaluated(c) => ty::tls::with(|tcx| {
|
||||
write!(f, "{}", c.unwrap_usize(tcx))
|
||||
})?,
|
||||
}
|
||||
write!(f, "]")
|
||||
|
|
|
@ -302,7 +302,7 @@ fn fixed_vec_metadata(
|
|||
|
||||
let upper_bound = match array_or_slice_type.sty {
|
||||
ty::Array(_, len) => {
|
||||
len.unwrap_usize(cx.tcx) as c_longlong
|
||||
len.unwrap_evaluated().unwrap_usize(cx.tcx) as c_longlong
|
||||
}
|
||||
_ => -1
|
||||
};
|
||||
|
|
|
@ -88,7 +88,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
|
|||
ty::Array(inner_type, len) => {
|
||||
output.push('[');
|
||||
push_debuginfo_type_name(cx, inner_type, true, output);
|
||||
output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx)));
|
||||
output.push_str(&format!("; {}", len.unwrap_evaluated().unwrap_usize(cx.tcx)));
|
||||
output.push(']');
|
||||
},
|
||||
ty::Slice(inner_type) => {
|
||||
|
|
|
@ -171,7 +171,7 @@ pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>(
|
|||
let (source, target) = cx.tcx().struct_lockstep_tails(source, target);
|
||||
match (&source.sty, &target.sty) {
|
||||
(&ty::Array(_, len), &ty::Slice(_)) => {
|
||||
cx.const_usize(len.unwrap_usize(cx.tcx()))
|
||||
cx.const_usize(len.unwrap_evaluated().unwrap_usize(cx.tcx()))
|
||||
}
|
||||
(&ty::Dynamic(..), &ty::Dynamic(..)) => {
|
||||
// For now, upcasts are limited to changes in marker
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustc::mir::interpret::ErrorHandled;
|
|||
use rustc_mir::const_eval::const_field;
|
||||
use rustc::mir;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc::mir::interpret::{GlobalId, ConstValue};
|
||||
use rustc::mir::interpret::GlobalId;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout;
|
||||
use syntax::source_map::Span;
|
||||
|
@ -14,10 +14,10 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
fn fully_evaluate(
|
||||
&mut self,
|
||||
bx: &Bx,
|
||||
constant: &'tcx ty::Const<'tcx>,
|
||||
constant: &'tcx ty::LazyConst<'tcx>,
|
||||
) -> Result<&'tcx ty::Const<'tcx>, ErrorHandled> {
|
||||
match constant.val {
|
||||
ConstValue::Unevaluated(def_id, ref substs) => {
|
||||
match *constant {
|
||||
ty::LazyConst::Unevaluated(def_id, ref substs) => {
|
||||
let tcx = bx.tcx();
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap();
|
||||
|
@ -27,7 +27,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
};
|
||||
tcx.const_eval(param_env.and(cid))
|
||||
},
|
||||
_ => Ok(constant),
|
||||
ty::LazyConst::Evaluated(constant) => Ok(constant),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
.and_then(|c| {
|
||||
let field_ty = c.ty.builtin_index().unwrap();
|
||||
let fields = match c.ty.sty {
|
||||
ty::Array(_, n) => n.unwrap_usize(bx.tcx()),
|
||||
ty::Array(_, n) => n.unwrap_evaluated().unwrap_usize(bx.tcx()),
|
||||
ref other => bug!("invalid simd shuffle type: {}", other),
|
||||
};
|
||||
let values: Result<Vec<_>, ErrorHandled> = (0..fields).map(|field| {
|
||||
|
|
|
@ -76,7 +76,6 @@ impl<'a, 'tcx: 'a, V: CodegenObject> OperandRef<'tcx, V> {
|
|||
}
|
||||
|
||||
let val = match val.val {
|
||||
ConstValue::Unevaluated(..) => bug!(),
|
||||
ConstValue::Scalar(x) => {
|
||||
let scalar = match layout.abi {
|
||||
layout::Abi::Scalar(ref x) => x,
|
||||
|
|
|
@ -537,7 +537,7 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
if let mir::Place::Local(index) = *place {
|
||||
if let LocalRef::Operand(Some(op)) = self.locals[index] {
|
||||
if let ty::Array(_, n) = op.layout.ty.sty {
|
||||
let n = n.unwrap_usize(bx.cx().tcx());
|
||||
let n = n.unwrap_evaluated().unwrap_usize(bx.cx().tcx());
|
||||
return bx.cx().const_usize(n);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1380,7 +1380,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||
);
|
||||
if let TerminatorKind::Call {
|
||||
func: Operand::Constant(box Constant {
|
||||
literal: ty::Const { ty: &ty::TyS { sty: ty::TyKind::FnDef(id, _), .. }, .. },
|
||||
literal: ty::LazyConst::Evaluated(ty::Const {
|
||||
ty: &ty::TyS { sty: ty::TyKind::FnDef(id, _), .. },
|
||||
..
|
||||
}),
|
||||
..
|
||||
}),
|
||||
args,
|
||||
|
|
|
@ -468,13 +468,13 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
|
|||
Terminator {
|
||||
kind: TerminatorKind::Call {
|
||||
func: Operand::Constant(box Constant {
|
||||
literal: Const {
|
||||
literal: ty::LazyConst::Evaluated(Const {
|
||||
ty: &TyS {
|
||||
sty: TyKind::FnDef(id, substs),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
}),
|
||||
..
|
||||
}),
|
||||
..
|
||||
|
|
|
@ -88,7 +88,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
|
|||
debug!("visit_region: region={:?}", region);
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _location: Location) {
|
||||
fn visit_const(&mut self, constant: &mut &'tcx ty::LazyConst<'tcx>, _location: Location) {
|
||||
*constant = self.renumber_regions(&*constant);
|
||||
}
|
||||
|
||||
|
|
|
@ -382,6 +382,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
constant, location
|
||||
);
|
||||
|
||||
let literal = match constant.literal {
|
||||
ty::LazyConst::Evaluated(lit) => lit,
|
||||
ty::LazyConst::Unevaluated(..) => return,
|
||||
};
|
||||
|
||||
// FIXME(#46702) -- We need some way to get the predicates
|
||||
// associated with the "pre-evaluated" form of the
|
||||
// constant. For example, consider that the constant
|
||||
|
@ -390,7 +395,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
// constraints on `'a` and `'b`. These constraints
|
||||
// would be lost if we just look at the normalized
|
||||
// value.
|
||||
if let ty::FnDef(def_id, substs) = constant.literal.ty.sty {
|
||||
if let ty::FnDef(def_id, substs) = literal.ty.sty {
|
||||
let tcx = self.tcx();
|
||||
let type_checker = &mut self.cx;
|
||||
|
||||
|
@ -411,10 +416,10 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
);
|
||||
}
|
||||
|
||||
debug!("sanitize_constant: expected_ty={:?}", constant.literal.ty);
|
||||
debug!("sanitize_constant: expected_ty={:?}", literal.ty);
|
||||
|
||||
if let Err(terr) = self.cx.eq_types(
|
||||
constant.literal.ty,
|
||||
literal.ty,
|
||||
constant.ty,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Boring,
|
||||
|
@ -424,7 +429,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
constant,
|
||||
"constant {:?} should have type {:?} but has {:?} ({:?})",
|
||||
constant,
|
||||
constant.literal.ty,
|
||||
literal.ty,
|
||||
constant.ty,
|
||||
terr,
|
||||
);
|
||||
|
@ -563,7 +568,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||
ProjectionElem::Subslice { from, to } => PlaceTy::Ty {
|
||||
ty: match base_ty.sty {
|
||||
ty::Array(inner, size) => {
|
||||
let size = size.unwrap_usize(tcx);
|
||||
let size = size.unwrap_evaluated().unwrap_usize(tcx);
|
||||
let min_size = (from as u64) + (to as u64);
|
||||
if let Some(rest_size) = size.checked_sub(min_size) {
|
||||
tcx.mk_array(inner, rest_size)
|
||||
|
|
|
@ -388,7 +388,7 @@ fn place_element_conflict<'a, 'gcx: 'tcx, 'tcx>(
|
|||
(Place::Promoted(p1), Place::Promoted(p2)) => {
|
||||
if p1.0 == p2.0 {
|
||||
if let ty::Array(_, size) = p1.1.sty {
|
||||
if size.unwrap_usize(tcx) == 0 {
|
||||
if size.unwrap_evaluated().unwrap_usize(tcx) == 0 {
|
||||
// Ignore conflicts with promoted [T; 0].
|
||||
debug!("place_element_conflict: IGNORE-LEN-0-PROMOTED");
|
||||
return Overlap::Disjoint;
|
||||
|
|
|
@ -270,11 +270,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
span: expr_span,
|
||||
ty: this.hir.tcx().types.u32,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_bits(
|
||||
this.hir.tcx(),
|
||||
0,
|
||||
ty::ParamEnv::empty().and(this.hir.tcx().types.u32),
|
||||
),
|
||||
literal: this.hir.tcx().intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
ty::Const::from_bits(
|
||||
this.hir.tcx(),
|
||||
0,
|
||||
ty::ParamEnv::empty().and(this.hir.tcx().types.u32),
|
||||
),
|
||||
)),
|
||||
}));
|
||||
box AggregateKind::Generator(closure_id, substs, movability)
|
||||
}
|
||||
|
|
|
@ -302,6 +302,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
let eq_def_id = self.hir.tcx().lang_items().eq_trait().unwrap();
|
||||
let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty.into()]);
|
||||
let method = self.hir.tcx().intern_lazy_const(ty::LazyConst::Evaluated(method));
|
||||
|
||||
// take the argument by reference
|
||||
let region_scope = self.topmost_scope();
|
||||
|
|
|
@ -33,7 +33,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||
span,
|
||||
ty,
|
||||
user_ty: None,
|
||||
literal,
|
||||
literal: self.hir.tcx().intern_lazy_const(ty::LazyConst::Evaluated(literal)),
|
||||
};
|
||||
Operand::Constant(constant)
|
||||
}
|
||||
|
|
|
@ -202,7 +202,7 @@ impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _: Location) {
|
||||
fn visit_const(&mut self, constant: &mut &'tcx ty::LazyConst<'tcx>, _: Location) {
|
||||
if let Some(lifted) = self.tcx.lift(constant) {
|
||||
*constant = lifted;
|
||||
} else {
|
||||
|
|
|
@ -136,12 +136,14 @@ pub fn op_to_const<'tcx>(
|
|||
};
|
||||
Ok(ty::Const::from_const_value(ecx.tcx.tcx, val, op.layout.ty))
|
||||
}
|
||||
|
||||
pub fn const_to_op<'tcx>(
|
||||
ecx: &CompileTimeEvalContext<'_, '_, 'tcx>,
|
||||
cnst: &ty::Const<'tcx>,
|
||||
cnst: ty::LazyConst<'tcx>,
|
||||
ty: ty::Ty<'tcx>,
|
||||
) -> EvalResult<'tcx, OpTy<'tcx>> {
|
||||
let op = ecx.const_value_to_op(cnst.val)?;
|
||||
Ok(OpTy { op, layout: ecx.layout_of(cnst.ty)? })
|
||||
let op = ecx.const_value_to_op(cnst)?;
|
||||
Ok(OpTy { op, layout: ecx.layout_of(ty)? })
|
||||
}
|
||||
|
||||
fn eval_body_and_ecx<'a, 'mir, 'tcx>(
|
||||
|
@ -514,7 +516,7 @@ pub fn const_field<'a, 'tcx>(
|
|||
let ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
|
||||
let result = (|| {
|
||||
// get the operand again
|
||||
let op = const_to_op(&ecx, value)?;
|
||||
let op = const_to_op(&ecx, ty::LazyConst::Evaluated(value), value.ty)?;
|
||||
// downcast
|
||||
let down = match variant {
|
||||
None => op,
|
||||
|
@ -541,7 +543,7 @@ pub fn const_variant_index<'a, 'tcx>(
|
|||
) -> EvalResult<'tcx, VariantIdx> {
|
||||
trace!("const_variant_index: {:?}, {:?}", instance, val);
|
||||
let ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
|
||||
let op = const_to_op(&ecx, val)?;
|
||||
let op = const_to_op(&ecx, ty::LazyConst::Evaluated(val), val.ty)?;
|
||||
Ok(ecx.read_discriminant(op)?.1)
|
||||
}
|
||||
|
||||
|
|
|
@ -356,7 +356,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
|||
}
|
||||
|
||||
hir::ExprKind::Lit(ref lit) => ExprKind::Literal {
|
||||
literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false),
|
||||
literal: cx.tcx.intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
cx.const_eval_literal(&lit.node, expr_ty, lit.span, false)
|
||||
)),
|
||||
user_ty: None,
|
||||
},
|
||||
|
||||
|
@ -454,7 +456,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
|||
} else {
|
||||
if let hir::ExprKind::Lit(ref lit) = arg.node {
|
||||
ExprKind::Literal {
|
||||
literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true),
|
||||
literal: cx.tcx.intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
cx.const_eval_literal(&lit.node, expr_ty, lit.span, true)
|
||||
)),
|
||||
user_ty: None,
|
||||
}
|
||||
} else {
|
||||
|
@ -711,24 +715,22 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
|||
temp_lifetime,
|
||||
ty: var_ty,
|
||||
span: expr.span,
|
||||
kind: ExprKind::Literal { literal, user_ty: None },
|
||||
kind: ExprKind::Literal {
|
||||
literal: cx.tcx.intern_lazy_const(literal),
|
||||
user_ty: None
|
||||
},
|
||||
}.to_ref();
|
||||
let offset = mk_const(ty::Const::from_bits(
|
||||
let offset = mk_const(ty::LazyConst::Evaluated(ty::Const::from_bits(
|
||||
cx.tcx,
|
||||
offset as u128,
|
||||
cx.param_env.and(var_ty),
|
||||
));
|
||||
)));
|
||||
match did {
|
||||
Some(did) => {
|
||||
// in case we are offsetting from a computed discriminant
|
||||
// and not the beginning of discriminants (which is always `0`)
|
||||
let substs = Substs::identity_for_item(cx.tcx(), did);
|
||||
let lhs = mk_const(ty::Const::unevaluated(
|
||||
cx.tcx(),
|
||||
did,
|
||||
substs,
|
||||
var_ty,
|
||||
));
|
||||
let lhs = mk_const(ty::LazyConst::Unevaluated(did, substs));
|
||||
let bin = ExprKind::Binary {
|
||||
op: BinOp::Add,
|
||||
lhs,
|
||||
|
@ -868,7 +870,9 @@ fn method_callee<'a, 'gcx, 'tcx>(
|
|||
ty,
|
||||
span,
|
||||
kind: ExprKind::Literal {
|
||||
literal: ty::Const::zero_sized(cx.tcx(), ty),
|
||||
literal: cx.tcx().intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
ty::Const::zero_sized(cx.tcx(), ty)
|
||||
)),
|
||||
user_ty,
|
||||
},
|
||||
}
|
||||
|
@ -928,10 +932,10 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
|||
let user_ty = user_substs_applied_to_def(cx, expr.hir_id, &def);
|
||||
debug!("convert_path_expr: user_ty={:?}", user_ty);
|
||||
ExprKind::Literal {
|
||||
literal: ty::Const::zero_sized(
|
||||
literal: cx.tcx.intern_lazy_const(ty::LazyConst::Evaluated(ty::Const::zero_sized(
|
||||
cx.tcx,
|
||||
cx.tables().node_id_to_type(expr.hir_id),
|
||||
),
|
||||
))),
|
||||
user_ty,
|
||||
}
|
||||
},
|
||||
|
@ -941,12 +945,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
|
|||
let user_ty = user_substs_applied_to_def(cx, expr.hir_id, &def);
|
||||
debug!("convert_path_expr: (const) user_ty={:?}", user_ty);
|
||||
ExprKind::Literal {
|
||||
literal: ty::Const::unevaluated(
|
||||
cx.tcx,
|
||||
def_id,
|
||||
substs,
|
||||
cx.tables().node_id_to_type(expr.hir_id),
|
||||
),
|
||||
literal: cx.tcx.intern_lazy_const(ty::LazyConst::Unevaluated(def_id, substs)),
|
||||
user_ty,
|
||||
}
|
||||
},
|
||||
|
|
|
@ -108,8 +108,8 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
|
|||
self.tcx.types.usize
|
||||
}
|
||||
|
||||
pub fn usize_literal(&mut self, value: u64) -> &'tcx ty::Const<'tcx> {
|
||||
ty::Const::from_usize(self.tcx, value)
|
||||
pub fn usize_literal(&mut self, value: u64) -> &'tcx ty::LazyConst<'tcx> {
|
||||
self.tcx.intern_lazy_const(ty::LazyConst::Evaluated(ty::Const::from_usize(self.tcx, value)))
|
||||
}
|
||||
|
||||
pub fn bool_ty(&mut self) -> Ty<'tcx> {
|
||||
|
@ -120,12 +120,12 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
|
|||
self.tcx.mk_unit()
|
||||
}
|
||||
|
||||
pub fn true_literal(&mut self) -> &'tcx ty::Const<'tcx> {
|
||||
ty::Const::from_bool(self.tcx, true)
|
||||
pub fn true_literal(&mut self) -> &'tcx ty::LazyConst<'tcx> {
|
||||
self.tcx.intern_lazy_const(ty::LazyConst::Evaluated(ty::Const::from_bool(self.tcx, true)))
|
||||
}
|
||||
|
||||
pub fn false_literal(&mut self) -> &'tcx ty::Const<'tcx> {
|
||||
ty::Const::from_bool(self.tcx, false)
|
||||
pub fn false_literal(&mut self) -> &'tcx ty::LazyConst<'tcx> {
|
||||
self.tcx.intern_lazy_const(ty::LazyConst::Evaluated(ty::Const::from_bool(self.tcx, false)))
|
||||
}
|
||||
|
||||
pub fn const_eval_literal(
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc::hir::def_id::DefId;
|
|||
use rustc::infer::canonical::Canonical;
|
||||
use rustc::middle::region;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::{AdtDef, UpvarSubsts, Region, Ty, Const, UserTypeAnnotation};
|
||||
use rustc::ty::{AdtDef, UpvarSubsts, Region, Ty, Const, LazyConst, UserTypeAnnotation};
|
||||
use rustc::ty::layout::VariantIdx;
|
||||
use rustc::hir;
|
||||
use syntax::ast;
|
||||
|
@ -288,7 +288,7 @@ pub enum ExprKind<'tcx> {
|
|||
movability: Option<hir::GeneratorMovability>,
|
||||
},
|
||||
Literal {
|
||||
literal: &'tcx Const<'tcx>,
|
||||
literal: &'tcx LazyConst<'tcx>,
|
||||
user_ty: Option<Canonical<'tcx, UserTypeAnnotation<'tcx>>>,
|
||||
},
|
||||
InlineAsm {
|
||||
|
|
|
@ -635,7 +635,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
|
|||
}).collect()
|
||||
}
|
||||
ty::Array(ref sub_ty, len) if len.assert_usize(cx.tcx).is_some() => {
|
||||
let len = len.unwrap_usize(cx.tcx);
|
||||
let len = len.unwrap_evaluated().unwrap_usize(cx.tcx);
|
||||
if len != 0 && cx.is_uninhabited(sub_ty) {
|
||||
vec![]
|
||||
} else {
|
||||
|
@ -1310,7 +1310,7 @@ fn pat_constructors<'tcx>(cx: &mut MatchCheckCtxt<'_, 'tcx>,
|
|||
)]),
|
||||
PatternKind::Array { .. } => match pcx.ty.sty {
|
||||
ty::Array(_, length) => Some(vec![
|
||||
Slice(length.unwrap_usize(cx.tcx))
|
||||
Slice(length.unwrap_evaluated().unwrap_usize(cx.tcx))
|
||||
]),
|
||||
_ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty)
|
||||
},
|
||||
|
@ -1751,23 +1751,23 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
|
|||
// necessarily point to memory, they are usually just integers. The only time
|
||||
// they should be pointing to memory is when they are subslices of nonzero
|
||||
// slices
|
||||
let (opt_ptr, n, ty) = match (value.val, &value.ty.sty) {
|
||||
(ConstValue::ByRef(id, alloc, offset), ty::TyKind::Array(t, n)) => (
|
||||
Some((
|
||||
Pointer::new(id, offset),
|
||||
alloc,
|
||||
)),
|
||||
n.unwrap_usize(cx.tcx),
|
||||
t,
|
||||
),
|
||||
(ConstValue::ScalarPair(ptr, n), ty::TyKind::Slice(t)) => (
|
||||
ptr.to_ptr().ok().map(|ptr| (
|
||||
ptr,
|
||||
cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
|
||||
)),
|
||||
n.to_bits(cx.tcx.data_layout.pointer_size).unwrap() as u64,
|
||||
t,
|
||||
),
|
||||
let (opt_ptr, n, ty) = match value.ty.builtin_deref(false).unwrap().ty.sty {
|
||||
ty::TyKind::Array(t, n) =>
|
||||
(value.to_ptr(), n.unwrap_evaluated().unwrap_usize(cx.tcx), t),
|
||||
ty::TyKind::Slice(t) => {
|
||||
match value.val {
|
||||
ConstValue::ScalarPair(ptr, n) => (
|
||||
ptr.to_ptr().ok(),
|
||||
n.to_bits(cx.tcx.data_layout.pointer_size).unwrap() as u64,
|
||||
t,
|
||||
),
|
||||
_ => span_bug!(
|
||||
pat.span,
|
||||
"slice pattern constant must be scalar pair but is {:?}",
|
||||
value,
|
||||
),
|
||||
}
|
||||
},
|
||||
_ => span_bug!(
|
||||
pat.span,
|
||||
"unexpected const-val {:?} with ctor {:?}",
|
||||
|
|
|
@ -650,7 +650,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
|||
|
||||
ty::Array(_, len) => {
|
||||
// fixed-length array
|
||||
let len = len.unwrap_usize(self.tcx);
|
||||
let len = len.unwrap_evaluated().unwrap_usize(self.tcx);
|
||||
assert!(len >= prefix.len() as u64 + suffix.len() as u64);
|
||||
PatternKind::Array { prefix: prefix, slice: slice, suffix: suffix }
|
||||
}
|
||||
|
@ -934,7 +934,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
|
|||
}
|
||||
ty::Array(_, n) => {
|
||||
PatternKind::Array {
|
||||
prefix: (0..n.unwrap_usize(self.tcx))
|
||||
prefix: (0..n.unwrap_evaluated().unwrap_usize(self.tcx))
|
||||
.map(|i| adt_subpattern(i as usize, None))
|
||||
.collect(),
|
||||
slice: None,
|
||||
|
|
|
@ -307,7 +307,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
// u64 cast is from usize to u64, which is always good
|
||||
let val = Immediate::new_slice(
|
||||
ptr,
|
||||
length.unwrap_usize(self.tcx.tcx),
|
||||
length.unwrap_evaluated().unwrap_usize(self.tcx.tcx),
|
||||
self,
|
||||
);
|
||||
self.write_immediate(val, dest)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use std::convert::TryInto;
|
||||
|
||||
use rustc::mir;
|
||||
use rustc::{mir, ty};
|
||||
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt, VariantIdx};
|
||||
|
||||
use rustc::mir::interpret::{
|
||||
|
@ -517,7 +517,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
let ty = self.monomorphize(mir_op.ty(self.mir(), *self.tcx), self.substs());
|
||||
self.layout_of(ty)
|
||||
})?;
|
||||
let op = self.const_value_to_op(constant.literal.val)?;
|
||||
let op = self.const_value_to_op(*constant.literal)?;
|
||||
OpTy { op, layout }
|
||||
}
|
||||
};
|
||||
|
@ -540,17 +540,20 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
// `eval_operand`, ideally).
|
||||
pub(crate) fn const_value_to_op(
|
||||
&self,
|
||||
val: ConstValue<'tcx>,
|
||||
val: ty::LazyConst<'tcx>,
|
||||
) -> EvalResult<'tcx, Operand<M::PointerTag>> {
|
||||
trace!("const_value_to_op: {:?}", val);
|
||||
match val {
|
||||
ConstValue::Unevaluated(def_id, substs) => {
|
||||
let val = match val {
|
||||
ty::LazyConst::Unevaluated(def_id, substs) => {
|
||||
let instance = self.resolve(def_id, substs)?;
|
||||
Ok(*OpTy::from(self.const_eval_raw(GlobalId {
|
||||
return Ok(*OpTy::from(self.const_eval_raw(GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
})?))
|
||||
}
|
||||
})?));
|
||||
},
|
||||
ty::LazyConst::Evaluated(c) => c,
|
||||
};
|
||||
match val.val {
|
||||
ConstValue::ByRef(id, alloc, offset) => {
|
||||
// We rely on mutability being set correctly in that allocation to prevent writes
|
||||
// where none should happen -- and for `static mut`, we copy on demand anyway.
|
||||
|
|
|
@ -381,7 +381,7 @@ fn collect_items_rec<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
let param_env = ty::ParamEnv::reveal_all();
|
||||
|
||||
if let Ok(val) = tcx.const_eval(param_env.and(cid)) {
|
||||
collect_const(tcx, val, instance.substs, &mut neighbors);
|
||||
collect_const(tcx, val, &mut neighbors);
|
||||
}
|
||||
}
|
||||
MonoItem::Fn(instance) => {
|
||||
|
@ -583,10 +583,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
|
|||
self.super_rvalue(rvalue, location);
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) {
|
||||
fn visit_const(&mut self, constant: &&'tcx ty::LazyConst<'tcx>, location: Location) {
|
||||
debug!("visiting const {:?} @ {:?}", *constant, location);
|
||||
|
||||
collect_const(self.tcx, constant, self.param_substs, self.output);
|
||||
collect_lazy_const(self.tcx, constant, self.param_substs, self.output);
|
||||
|
||||
self.super_const(constant);
|
||||
}
|
||||
|
@ -987,7 +987,7 @@ impl<'b, 'a, 'v> ItemLikeVisitor<'v> for RootCollector<'b, 'a, 'v> {
|
|||
let param_env = ty::ParamEnv::reveal_all();
|
||||
|
||||
if let Ok(val) = self.tcx.const_eval(param_env.and(cid)) {
|
||||
collect_const(self.tcx, val, instance.substs, &mut self.output);
|
||||
collect_const(self.tcx, val, &mut self.output);
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Fn(..) => {
|
||||
|
@ -1198,7 +1198,7 @@ fn collect_neighbours<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
promoted: Some(i),
|
||||
};
|
||||
match tcx.const_eval(param_env.and(cid)) {
|
||||
Ok(val) => collect_const(tcx, val, instance.substs, output),
|
||||
Ok(val) => collect_const(tcx, val, output),
|
||||
Err(ErrorHandled::Reported) => {},
|
||||
Err(ErrorHandled::TooGeneric) => span_bug!(
|
||||
mir.promoted[i].span, "collection encountered polymorphic constant",
|
||||
|
@ -1216,43 +1216,48 @@ fn def_id_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
output
|
||||
}
|
||||
|
||||
fn collect_lazy_const<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
constant: &ty::LazyConst<'tcx>,
|
||||
param_substs: &'tcx Substs<'tcx>,
|
||||
output: &mut Vec<MonoItem<'tcx>>,
|
||||
) {
|
||||
let (def_id, substs) = match *constant {
|
||||
ty::LazyConst::Evaluated(c) => return collect_const(tcx, c, output),
|
||||
ty::LazyConst::Unevaluated(did, substs) => (did, substs),
|
||||
};
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
let substs = tcx.subst_and_normalize_erasing_regions(
|
||||
param_substs,
|
||||
param_env,
|
||||
&substs,
|
||||
);
|
||||
let instance = ty::Instance::resolve(tcx,
|
||||
param_env,
|
||||
def_id,
|
||||
substs).unwrap();
|
||||
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
match tcx.const_eval(param_env.and(cid)) {
|
||||
Ok(val) => collect_const(tcx, val, output),
|
||||
Err(ErrorHandled::Reported) => {},
|
||||
Err(ErrorHandled::TooGeneric) => span_bug!(
|
||||
tcx.def_span(def_id), "collection encountered polymorphic constant",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_const<'a, 'tcx>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
constant: &ty::Const<'tcx>,
|
||||
param_substs: &'tcx Substs<'tcx>,
|
||||
output: &mut Vec<MonoItem<'tcx>>,
|
||||
) {
|
||||
debug!("visiting const {:?}", *constant);
|
||||
|
||||
let val = match constant.val {
|
||||
ConstValue::Unevaluated(def_id, substs) => {
|
||||
let param_env = ty::ParamEnv::reveal_all();
|
||||
let substs = tcx.subst_and_normalize_erasing_regions(
|
||||
param_substs,
|
||||
param_env,
|
||||
&substs,
|
||||
);
|
||||
let instance = ty::Instance::resolve(tcx,
|
||||
param_env,
|
||||
def_id,
|
||||
substs).unwrap();
|
||||
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
match tcx.const_eval(param_env.and(cid)) {
|
||||
Ok(val) => val.val,
|
||||
Err(ErrorHandled::Reported) => return,
|
||||
Err(ErrorHandled::TooGeneric) => span_bug!(
|
||||
tcx.def_span(def_id), "collection encountered polymorphic constant",
|
||||
),
|
||||
}
|
||||
},
|
||||
_ => constant.val,
|
||||
};
|
||||
match val {
|
||||
ConstValue::Unevaluated(..) => bug!("const eval yielded unevaluated const"),
|
||||
match constant.val {
|
||||
ConstValue::ScalarPair(Scalar::Ptr(a), Scalar::Ptr(b)) => {
|
||||
collect_miri(tcx, a.alloc_id, output);
|
||||
collect_miri(tcx, b.alloc_id, output);
|
||||
|
|
|
@ -295,7 +295,7 @@ impl<'a, 'tcx> DefPathBasedNames<'a, 'tcx> {
|
|||
ty::Array(inner_type, len) => {
|
||||
output.push('[');
|
||||
self.push_type_name(inner_type, output);
|
||||
write!(output, "; {}", len.unwrap_usize(self.tcx)).unwrap();
|
||||
write!(output, "; {}", len.unwrap_evaluated().unwrap_usize(self.tcx)).unwrap();
|
||||
output.push(']');
|
||||
},
|
||||
ty::Slice(inner_type) => {
|
||||
|
|
|
@ -318,7 +318,7 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
match self_ty.sty {
|
||||
_ if is_copy => builder.copy_shim(),
|
||||
ty::Array(ty, len) => {
|
||||
let len = len.unwrap_usize(tcx);
|
||||
let len = len.unwrap_evaluated().unwrap_usize(tcx);
|
||||
builder.array_shim(dest, src, ty, len)
|
||||
}
|
||||
ty::Closure(def_id, substs) => {
|
||||
|
@ -459,7 +459,9 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
|
|||
span: self.span,
|
||||
ty: func_ty,
|
||||
user_ty: None,
|
||||
literal: ty::Const::zero_sized(self.tcx, func_ty),
|
||||
literal: tcx.intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
ty::Const::zero_sized(self.tcx, func_ty),
|
||||
)),
|
||||
});
|
||||
|
||||
let ref_loc = self.make_place(
|
||||
|
@ -519,7 +521,9 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
|
|||
span: self.span,
|
||||
ty: self.tcx.types.usize,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_usize(self.tcx, value),
|
||||
literal: self.tcx.intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
ty::Const::from_usize(self.tcx, value),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -755,7 +759,9 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
span,
|
||||
ty,
|
||||
user_ty: None,
|
||||
literal: ty::Const::zero_sized(tcx, ty),
|
||||
literal: tcx.intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
ty::Const::zero_sized(tcx, ty)
|
||||
)),
|
||||
}),
|
||||
vec![rcvr])
|
||||
}
|
||||
|
|
|
@ -255,7 +255,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
|
|||
source_info: SourceInfo,
|
||||
) -> Option<Const<'tcx>> {
|
||||
self.ecx.tcx.span = source_info.span;
|
||||
match const_to_op(&self.ecx, c.literal) {
|
||||
match const_to_op(&self.ecx, *c.literal, c.ty) {
|
||||
Ok(op) => {
|
||||
Some((op, c.span))
|
||||
},
|
||||
|
|
|
@ -533,7 +533,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
|
|||
span,
|
||||
ty: self.tcx.types.bool,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_bool(self.tcx, val),
|
||||
literal: self.tcx.intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
ty::Const::from_bool(self.tcx, val),
|
||||
)),
|
||||
})))
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
|
|||
*region = self.tcx.types.re_erased;
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _: Location) {
|
||||
fn visit_const(&mut self, constant: &mut &'tcx ty::LazyConst<'tcx>, _: Location) {
|
||||
*constant = self.tcx.erase_regions(constant);
|
||||
}
|
||||
|
||||
|
|
|
@ -171,11 +171,11 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
|
|||
span: source_info.span,
|
||||
ty: self.tcx.types.u32,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_bits(
|
||||
literal: self.tcx.intern_lazy_const(ty::LazyConst::Evaluated(ty::Const::from_bits(
|
||||
self.tcx,
|
||||
state_disc.into(),
|
||||
ty::ParamEnv::empty().and(self.tcx.types.u32)
|
||||
),
|
||||
))),
|
||||
});
|
||||
Statement {
|
||||
source_info,
|
||||
|
@ -717,7 +717,9 @@ fn insert_panic_block<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||
span: mir.span,
|
||||
ty: tcx.types.bool,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_bool(tcx, false),
|
||||
literal: tcx.intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
ty::Const::from_bool(tcx, false),
|
||||
)),
|
||||
}),
|
||||
expected: true,
|
||||
msg: message,
|
||||
|
|
|
@ -11,7 +11,6 @@ use rustc_data_structures::sync::Lrc;
|
|||
use rustc_target::spec::abi::Abi;
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::mir::interpret::ConstValue;
|
||||
use rustc::traits::{self, TraitEngine};
|
||||
use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
|
||||
use rustc::ty::cast::CastTy;
|
||||
|
@ -625,12 +624,12 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
Operand::Constant(ref constant) => {
|
||||
if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
|
||||
if let ty::LazyConst::Unevaluated(def_id, _) = constant.literal {
|
||||
// Don't peek inside trait associated constants.
|
||||
if self.tcx.trait_of_item(def_id).is_some() {
|
||||
self.add_type(constant.literal.ty);
|
||||
if self.tcx.trait_of_item(*def_id).is_some() {
|
||||
self.add_type(constant.ty);
|
||||
} else {
|
||||
let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id);
|
||||
let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(*def_id);
|
||||
|
||||
let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
|
||||
self.add(qualif);
|
||||
|
@ -638,7 +637,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
|||
// Just in case the type is more specific than
|
||||
// the definition, e.g., impl associated const
|
||||
// with type parameters, take it into account.
|
||||
self.qualif.restrict(constant.literal.ty, self.tcx, self.param_env);
|
||||
self.qualif.restrict(constant.ty, self.tcx, self.param_env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -713,7 +712,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
|||
} else if let ty::Array(_, len) = ty.sty {
|
||||
// FIXME(eddyb) the `self.mode == Mode::Fn` condition
|
||||
// seems unnecessary, given that this is merely a ZST.
|
||||
if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
|
||||
let len = len.unwrap_evaluated().unwrap_usize(self.tcx);
|
||||
if len == 0 && self.mode == Mode::Fn {
|
||||
forbidden_mut = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! A pass that simplifies branches when their condition is known.
|
||||
|
||||
use rustc::ty::{TyCtxt, ParamEnv};
|
||||
use rustc::ty::{self, TyCtxt, ParamEnv};
|
||||
use rustc::mir::*;
|
||||
use transform::{MirPass, MirSource};
|
||||
|
||||
|
@ -30,23 +30,28 @@ impl MirPass for SimplifyBranches {
|
|||
discr: Operand::Constant(ref c), switch_ty, ref values, ref targets, ..
|
||||
} => {
|
||||
let switch_ty = ParamEnv::empty().and(switch_ty);
|
||||
if let Some(constint) = c.literal.assert_bits(tcx, switch_ty) {
|
||||
let (otherwise, targets) = targets.split_last().unwrap();
|
||||
let mut ret = TerminatorKind::Goto { target: *otherwise };
|
||||
for (&v, t) in values.iter().zip(targets.iter()) {
|
||||
if v == constint {
|
||||
ret = TerminatorKind::Goto { target: *t };
|
||||
break;
|
||||
if let ty::LazyConst::Evaluated(c) = c.literal {
|
||||
let c = c.assert_bits(tcx, switch_ty);
|
||||
if let Some(constant) = c {
|
||||
let (otherwise, targets) = targets.split_last().unwrap();
|
||||
let mut ret = TerminatorKind::Goto { target: *otherwise };
|
||||
for (&v, t) in values.iter().zip(targets.iter()) {
|
||||
if v == constant {
|
||||
ret = TerminatorKind::Goto { target: *t };
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
ret
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
},
|
||||
TerminatorKind::Assert {
|
||||
target, cond: Operand::Constant(ref c), expected, ..
|
||||
} if (c.literal.assert_bool(tcx) == Some(true)) == expected => {
|
||||
} if (c.literal.unwrap_evaluated().assert_bool(tcx) == Some(true)) == expected => {
|
||||
TerminatorKind::Goto { target }
|
||||
},
|
||||
TerminatorKind::FalseEdges { real_target, .. } => {
|
||||
|
|
|
@ -71,7 +71,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
|
|||
} else {
|
||||
let place_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
|
||||
if let ty::Array(item_ty, const_size) = place_ty.sty {
|
||||
if let Some(size) = const_size.assert_usize(self.tcx) {
|
||||
if let Some(size) = const_size.unwrap_evaluated().assert_usize(self.tcx) {
|
||||
assert!(size <= u32::max_value() as u64,
|
||||
"uniform array move out doesn't supported
|
||||
for array bigger then u32");
|
||||
|
@ -193,7 +193,7 @@ impl MirPass for RestoreSubsliceArrayMoveOut {
|
|||
let opt_size = opt_src_place.and_then(|src_place| {
|
||||
let src_ty = src_place.ty(mir, tcx).to_ty(tcx);
|
||||
if let ty::Array(_, ref size_o) = src_ty.sty {
|
||||
size_o.assert_usize(tcx)
|
||||
size_o.unwrap_evaluated().assert_usize(tcx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -809,7 +809,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
|
|||
self.complete_drop(Some(DropFlagMode::Deep), succ, unwind)
|
||||
}
|
||||
ty::Array(ety, size) => {
|
||||
let size = size.assert_usize(self.tcx());
|
||||
let size = size.unwrap_evaluated().assert_usize(self.tcx());
|
||||
self.open_drop_for_array(ety, size)
|
||||
},
|
||||
ty::Slice(ety) => self.open_drop_for_array(ety, None),
|
||||
|
@ -963,7 +963,9 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
|
|||
span: self.source_info.span,
|
||||
ty: self.tcx().types.usize,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_usize(self.tcx(), val.into()),
|
||||
literal: self.tcx().intern_lazy_const(ty::LazyConst::Evaluated(
|
||||
ty::Const::from_usize(self.tcx(), val.into())
|
||||
)),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -399,12 +399,21 @@ impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ExtraComments<'cx, 'gcx, 'tcx> {
|
|||
self.push(&format!("+ literal: {:?}", literal));
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
|
||||
fn visit_const(&mut self, constant: &&'tcx ty::LazyConst<'tcx>, _: Location) {
|
||||
self.super_const(constant);
|
||||
let ty::Const { ty, val, .. } = constant;
|
||||
self.push("ty::Const");
|
||||
self.push(&format!("+ ty: {:?}", ty));
|
||||
self.push(&format!("+ val: {:?}", val));
|
||||
match constant {
|
||||
ty::LazyConst::Evaluated(constant) => {
|
||||
let ty::Const { ty, val, .. } = constant;
|
||||
self.push("ty::Const");
|
||||
self.push(&format!("+ ty: {:?}", ty));
|
||||
self.push(&format!("+ val: {:?}", val));
|
||||
},
|
||||
ty::LazyConst::Unevaluated(did, substs) => {
|
||||
self.push("ty::LazyConst::Unevaluated");
|
||||
self.push(&format!("+ did: {:?}", did));
|
||||
self.push(&format!("+ substs: {:?}", substs));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
|
|
|
@ -239,7 +239,7 @@ fn wf_clause_for_slice<'tcx>(tcx: ty::TyCtxt<'_, '_, 'tcx>) -> Clauses<'tcx> {
|
|||
|
||||
fn wf_clause_for_array<'tcx>(
|
||||
tcx: ty::TyCtxt<'_, '_, 'tcx>,
|
||||
length: &'tcx ty::Const<'tcx>
|
||||
length: &'tcx ty::LazyConst<'tcx>
|
||||
) -> Clauses<'tcx> {
|
||||
let ty = generic_types::bound(tcx, 0);
|
||||
let array_ty = tcx.mk_ty(ty::Array(ty, length));
|
||||
|
|
|
@ -1772,7 +1772,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
|
|||
hir::TyKind::Array(ref ty, ref length) => {
|
||||
let length_def_id = tcx.hir().local_def_id(length.id);
|
||||
let substs = Substs::identity_for_item(tcx, length_def_id);
|
||||
let length = ty::Const::unevaluated(tcx, length_def_id, substs, tcx.types.usize);
|
||||
let length = ty::LazyConst::Unevaluated(length_def_id, substs);
|
||||
let length = tcx.intern_lazy_const(length);
|
||||
let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(&ty), length));
|
||||
self.normalize_ty(ast_ty.span, array_ty)
|
||||
}
|
||||
|
|
|
@ -377,7 +377,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
let expected_ty = self.structurally_resolved_type(pat.span, expected);
|
||||
let (inner_ty, slice_ty) = match expected_ty.sty {
|
||||
ty::Array(inner_ty, size) => {
|
||||
let size = size.unwrap_usize(tcx);
|
||||
let size = size.unwrap_evaluated().unwrap_usize(tcx);
|
||||
let min_len = before.len() as u64 + after.len() as u64;
|
||||
if slice.is_none() {
|
||||
if min_len != size {
|
||||
|
|
|
@ -3453,7 +3453,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
ty::Array(_, len) => {
|
||||
if let (Some(len), Ok(user_index)) = (
|
||||
len.assert_usize(self.tcx),
|
||||
len.unwrap_evaluated().assert_usize(self.tcx),
|
||||
field.as_str().parse::<u64>()
|
||||
) {
|
||||
let base = self.tcx.sess.source_map()
|
||||
|
@ -4442,7 +4442,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
if element_ty.references_error() {
|
||||
tcx.types.err
|
||||
} else if let Ok(count) = count {
|
||||
tcx.mk_ty(ty::Array(t, count))
|
||||
tcx.mk_ty(ty::Array(t, tcx.intern_lazy_const(ty::LazyConst::Evaluated(count))))
|
||||
} else {
|
||||
tcx.types.err
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use rustc_data_structures::sync::Lrc;
|
|||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
use rustc::infer::region_constraints::{RegionConstraintData, Constraint};
|
||||
use rustc::mir::interpret::ConstValue;
|
||||
use rustc::middle::resolve_lifetime as rl;
|
||||
use rustc::middle::lang_items;
|
||||
use rustc::middle::stability;
|
||||
|
@ -2420,10 +2419,10 @@ impl Clean<Type> for hir::Ty {
|
|||
instance: ty::Instance::new(def_id, substs),
|
||||
promoted: None
|
||||
};
|
||||
let length = cx.tcx.const_eval(param_env.and(cid)).unwrap_or_else(|_| {
|
||||
ty::Const::unevaluated(cx.tcx, def_id, substs, cx.tcx.types.usize)
|
||||
});
|
||||
let length = print_const(cx, length);
|
||||
let length = match cx.tcx.const_eval(param_env.and(cid)) {
|
||||
Ok(length) => print_const(cx, ty::LazyConst::Evaluated(length)),
|
||||
Err(_) => "_".to_string(),
|
||||
};
|
||||
Array(box ty.clean(cx), length)
|
||||
},
|
||||
TyKind::Tup(ref tys) => Tuple(tys.clean(cx)),
|
||||
|
@ -2583,15 +2582,15 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
|
|||
ty::Str => Primitive(PrimitiveType::Str),
|
||||
ty::Slice(ty) => Slice(box ty.clean(cx)),
|
||||
ty::Array(ty, n) => {
|
||||
let mut n = cx.tcx.lift(&n).expect("array lift failed");
|
||||
if let ConstValue::Unevaluated(def_id, substs) = n.val {
|
||||
let mut n = *cx.tcx.lift(&n).expect("array lift failed");
|
||||
if let ty::LazyConst::Unevaluated(def_id, substs) = n {
|
||||
let param_env = cx.tcx.param_env(def_id);
|
||||
let cid = GlobalId {
|
||||
instance: ty::Instance::new(def_id, substs),
|
||||
promoted: None
|
||||
};
|
||||
if let Ok(new_n) = cx.tcx.const_eval(param_env.and(cid)) {
|
||||
n = new_n;
|
||||
n = ty::LazyConst::Evaluated(new_n);
|
||||
}
|
||||
};
|
||||
let n = print_const(cx, n);
|
||||
|
@ -3691,16 +3690,16 @@ fn name_from_pat(p: &hir::Pat) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_const(cx: &DocContext, n: &ty::Const) -> String {
|
||||
match n.val {
|
||||
ConstValue::Unevaluated(def_id, _) => {
|
||||
fn print_const(cx: &DocContext, n: ty::LazyConst) -> String {
|
||||
match n {
|
||||
ty::LazyConst::Unevaluated(def_id, _) => {
|
||||
if let Some(node_id) = cx.tcx.hir().as_local_node_id(def_id) {
|
||||
print_const_expr(cx, cx.tcx.hir().body_owned_by(node_id))
|
||||
} else {
|
||||
inline::print_inlined_const(cx, def_id)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
ty::LazyConst::Evaluated(n) => {
|
||||
let mut s = String::new();
|
||||
::rustc::mir::fmt_const_val(&mut s, n).expect("fmt_const_val failed");
|
||||
// array lengths are obviously usize
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue